diff --git a/.dockerignore b/.dockerignore index 7da6456c..37ff8f51 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,8 +3,8 @@ Dockerfile *.md logo.png versions.txt -build.sh docker-compose.yml .env docker/__pycache__ -.env.example \ No newline at end of file +.env.example +build diff --git a/.env.example b/.env.example index 462fb17f..5e84184c 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,7 @@ # Full path to the LEGO Universe client -CLIENT_PATH=/Users/someuser/LEGO Universe -# Can improve build time -BUILD_THREADS=1 +CLIENT_PATH=./client # Updates NET_VERSION in CMakeVariables.txt -BUILD_VERSION=171022 +NET_VERSION=171022 # make sure this is a long random string # grab a "SHA 256-bit Key" from here: https://keygen.io/ ACCOUNT_MANAGER_SECRET= @@ -12,6 +10,5 @@ EXTERNAL_IP=localhost # Database values # Be careful with special characters here. It is more safe to use normal characters and/or numbers. MARIADB_USER=darkflame -MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME -MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME +MARIADB_PASSWORD= MARIADB_DATABASE=darkflame diff --git a/.github/workflows/build-and-push-docker.yml b/.github/workflows/build-and-push-docker.yml new file mode 100644 index 00000000..e4bffabe --- /dev/null +++ b/.github/workflows/build-and-push-docker.yml @@ -0,0 +1,56 @@ +name: CI + +on: + push: + branches: + - "main" + tags: + - "v*.*.*" + pull_request: + branches: + - "main" + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + build-and-push-image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Log in to the Container registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + # generate Docker tags based on the following events/attributes + tags: | + type=ref,event=pr + type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }} + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=semver,pattern={{major}} + + - name: Build and push Docker image + uses: docker/build-push-action@v5 + with: + context: . + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index 8a81def7..fc44885d 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -13,7 +13,7 @@ jobs: continue-on-error: true strategy: matrix: - os: [ windows-2022, ubuntu-20.04, macos-11 ] + os: [ windows-2022, ubuntu-22.04, macos-13 ] steps: - uses: actions/checkout@v3 @@ -25,9 +25,11 @@ jobs: with: vs-version: '[17,18)' msbuild-architecture: x64 - - name: Install libssl (Mac Only) - if: ${{ matrix.os == 'macos-11' }} - run: brew install openssl@3 + - name: Install libssl and switch to XCode 15.2 (Mac Only) + if: ${{ matrix.os == 'macos-13' }} + run: | + brew install openssl@3 + sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer - name: cmake uses: lukka/run-cmake@v10 with: diff --git a/.gitignore b/.gitignore index e093ba4b..12a3b284 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ docker/__pycache__ docker-compose.override.yml !*Test.bin +!cmake/* diff --git a/.gitmodules b/.gitmodules index 33193447..4ba6a43b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,6 +14,6 @@ path = thirdparty/mariadb-connector-cpp url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git ignore = dirty -[submodule "thirdparty/AccountManager"] - path = thirdparty/AccountManager - url = https://github.com/DarkflameUniverse/AccountManager +[submodule "thirdparty/magic_enum"] + path = thirdparty/magic_enum + url = https://github.com/Neargye/magic_enum.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f8981ee..e085bfe7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.18) project(Darkflame) include(CTest) -set (CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) +set(CXX_STANDARD_REQUIRED ON) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # Read variables from file FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) @@ -14,30 +16,26 @@ string(REPLACE "\n" ";" variables ${variables}) foreach(variable ${variables}) # If the string contains a #, skip it if(NOT "${variable}" MATCHES "#") - # Split the variable into name and value string(REPLACE "=" ";" variable ${variable}) # Check that the length of the variable is 2 (name and value) list(LENGTH variable length) - if(${length} EQUAL 2) + if(${length} EQUAL 2) list(GET variable 0 variable_name) list(GET variable 1 variable_value) # Set the variable set(${variable_name} ${variable_value}) - # Add compiler definition - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}") - message(STATUS "Variable: ${variable_name} = ${variable_value}") endif() endif() endforeach() # Set the version -set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") +set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"") # Echo the version message(STATUS "Version: ${PROJECT_VERSION}") @@ -53,19 +51,22 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE) # Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent. # Disabled no-register # Disabled unknown pragmas because Linux doesn't understand Windows pragmas. -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}") if(UNIX) - if(APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC") - else() - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC") + add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) + + if(NOT APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs") endif() - if (__dynamic AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + + if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic") endif() - if (__ggdb) + + if(${GGDB}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") endif() + 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 @@ -98,47 +99,64 @@ 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" "blacklist.dcf") message(STATUS "Checking resource file integrity") -foreach (resource_file ${RESOURCE_FILES}) + +include(Utils) +UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port") +UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port") +UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port") + +foreach(resource_file ${RESOURCE_FILES}) set(file_size 0) - if (EXISTS ${PROJECT_BINARY_DIR}/${resource_file}) + + if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file}) file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size) endif() - if (${file_size} EQUAL 0) + + if(${file_size} EQUAL 0) configure_file( ${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file} COPYONLY ) message(STATUS "Moved " ${resource_file} " to project binary directory") - elseif (resource_file MATCHES ".ini") + elseif(resource_file MATCHES ".ini") message(STATUS "Checking " ${resource_file} " for missing config options") file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents) string(REPLACE "\\\n" "" current_file_contents ${current_file_contents}) string(REPLACE "\n" ";" current_file_contents ${current_file_contents}) set(parsed_current_file_contents "") + # Remove comment lines so they do not interfere with the variable parsing - foreach (line ${current_file_contents}) + foreach(line ${current_file_contents}) string(FIND ${line} "#" is_comment) - if (NOT ${is_comment} EQUAL 0) + + if(NOT ${is_comment} EQUAL 0) string(APPEND parsed_current_file_contents ${line}) endif() endforeach() + file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents) string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents}) string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents}) set(line_to_add "") - foreach (line ${depot_file_contents}) + + foreach(line ${depot_file_contents}) string(FIND ${line} "#" is_comment) - if (NOT ${is_comment} EQUAL 0) + + if(NOT ${is_comment} EQUAL 0) string(REPLACE "=" ";" line_split ${line}) list(GET line_split 0 variable_name) - if (NOT ${parsed_current_file_contents} MATCHES ${variable_name}) + + if(NOT ${parsed_current_file_contents} MATCHES ${variable_name}) message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file}) set(line_to_add ${line_to_add} ${line}) - foreach (line_to_append ${line_to_add}) + + foreach(line_to_append ${line_to_add}) file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append}) endforeach() + file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n") endif() + set(line_to_add "") else() set(line_to_add ${line_to_add} ${line}) @@ -146,21 +164,23 @@ foreach (resource_file ${RESOURCE_FILES}) endforeach() endif() endforeach() + message(STATUS "Resource file integrity check complete") -# Copy navmesh data on first build and extract it -if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes/) - configure_file( - ${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip - COPYONLY - ) - - file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip) - file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip) +# if navmeshes directory does not exist, create it +if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes) + file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes) endif() +# Copy navmesh data on first build and extract it +configure_file(${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip COPYONLY) + +file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PROJECT_BINARY_DIR}/navmeshes) +file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip) + # Copy vanity files on first build set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml") + foreach(file ${VANITY_FILES}) configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY) endforeach() @@ -168,26 +188,18 @@ endforeach() # Move our migrations for MasterServer to run file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql) + foreach(file ${SQL_FILES}) get_filename_component(file ${file} NAME) - if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/dlu/${file}) - configure_file( - ${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file} - COPYONLY - ) - endif() + configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}) endforeach() file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql) + foreach(file ${SQL_FILES}) get_filename_component(file ${file} NAME) - if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}) - configure_file( - ${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file} - COPYONLY - ) - endif() + configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}) endforeach() # Create our list of include directories @@ -195,7 +207,9 @@ set(INCLUDED_DIRECTORIES "dCommon" "dCommon/dClient" "dCommon/dEnums" + "dChatFilter" + "dGame" "dGame/dBehaviors" "dGame/dComponents" @@ -206,128 +220,70 @@ set(INCLUDED_DIRECTORIES "dGame/dPropertyBehaviors" "dGame/dPropertyBehaviors/ControlBehaviorMessages" "dGame/dUtilities" + "dPhysics" + "dNavigation" "dNavigation/dTerrain" - "dZoneManager" - "dDatabase" - "dDatabase/Tables" - "dNet" - "dScripts" - "dScripts/02_server" - "dScripts/ai" - "dScripts/client" - "dScripts/EquipmentScripts" - "dScripts/EquipmentTriggers" - "dScripts/zone" - "dScripts/02_server/DLU" - "dScripts/02_server/Enemy" - "dScripts/02_server/Equipment" - "dScripts/02_server/Map" - "dScripts/02_server/Minigame" - "dScripts/02_server/Objects" - "dScripts/02_server/Pets" - "dScripts/02_server/Enemy/AG" - "dScripts/02_server/Enemy/AM" - "dScripts/02_server/Enemy/FV" - "dScripts/02_server/Enemy/General" - "dScripts/02_server/Enemy/Survival" - "dScripts/02_server/Enemy/VE" - "dScripts/02_server/Enemy/Waves" - "dScripts/02_server/Map/AG" - "dScripts/02_server/Map/AG_Spider_Queen" - "dScripts/02_server/Map/AM" - "dScripts/02_server/Map/FV" - "dScripts/02_server/Map/General" - "dScripts/02_server/Map/GF" - "dScripts/02_server/Map/njhub" - "dScripts/02_server/Map/NS" - "dScripts/02_server/Map/NT" - "dScripts/02_server/Map/PR" - "dScripts/02_server/Map/Property" - "dScripts/02_server/Map/SS" - "dScripts/02_server/Map/VE" - "dScripts/02_server/Map/FV/Racing" - "dScripts/02_server/Map/General/Ninjago" - "dScripts/02_server/Map/njhub/boss_instance" - "dScripts/02_server/Map/NS/Waves" - "dScripts/02_server/Map/Property/AG_Med" - "dScripts/02_server/Map/Property/AG_Small" - "dScripts/02_server/Map/Property/NS_Med" - "dScripts/02_server/Minigame/General" - "dScripts/ai/ACT" - "dScripts/ai/AG" - "dScripts/ai/FV" - "dScripts/ai/GENERAL" - "dScripts/ai/GF" - "dScripts/ai/MINIGAME" - "dScripts/ai/NP" - "dScripts/ai/NS" - "dScripts/ai/PETS" - "dScripts/ai/PROPERTY" - "dScripts/ai/RACING" - "dScripts/ai/SPEC" - "dScripts/ai/WILD" - "dScripts/ai/ACT/FootRace" - "dScripts/ai/MINIGAME/SG_GF" - "dScripts/ai/MINIGAME/SG_GF/SERVER" - "dScripts/ai/NS/NS_PP_01" - "dScripts/ai/NS/WH" - "dScripts/ai/PROPERTY/AG" - "dScripts/ai/RACING/OBJECTS" - "dScripts/client/ai" - "dScripts/client/ai/PR" - "dScripts/zone/AG" - "dScripts/zone/LUPs" - "dScripts/zone/PROPERTY" - "dScripts/zone/PROPERTY/FV" - "dScripts/zone/PROPERTY/GF" - "dScripts/zone/PROPERTY/NS" + "dZoneManager" + + "dDatabase" + "dDatabase/CDClientDatabase" + "dDatabase/CDClientDatabase/CDClientTables" + "dDatabase/GameDatabase" + "dDatabase/GameDatabase/ITables" + "dDatabase/GameDatabase/MySQL" + "dDatabase/GameDatabase/MySQL/Tables" + + "dNet" + + "thirdparty/magic_enum/include/magic_enum" "thirdparty/raknet/Source" "thirdparty/tinyxml2" "thirdparty/recastnavigation" "thirdparty/SQLite" "thirdparty/cpplinq" + "thirdparty/cpp-httplib" + "thirdparty/MD5" "tests" "tests/dCommonTests" "tests/dGameTests" "tests/dGameTests/dComponentsTests" - ) +) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux) -if (APPLE) +if(APPLE) include_directories("/usr/local/include/") endif() -if (WIN32) - set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include") -elseif (UNIX) - set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt") - set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt") -endif() - -# Add binary directory as an include directory -include_directories(${PROJECT_BINARY_DIR}) - # Actually include the directories from our list -foreach (dir ${INCLUDED_DIRECTORIES}) +foreach(dir ${INCLUDED_DIRECTORIES}) include_directories(${PROJECT_SOURCE_DIR}/${dir}) endforeach() +if(NOT WIN32) + include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt") +endif() + +include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include") + # Add linking directories: link_directories(${PROJECT_BINARY_DIR}) # Load all of our third party directories add_subdirectory(thirdparty) - +if (UNIX) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") +endif() # Glob together all headers that need to be precompiled file( GLOB HEADERS_DDATABASE LIST_DIRECTORIES false - ${PROJECT_SOURCE_DIR}/dDatabase/*.h - ${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h + ${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h ${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h ) @@ -362,15 +318,16 @@ add_subdirectory(dGame) add_subdirectory(dZoneManager) add_subdirectory(dNavigation) add_subdirectory(dPhysics) +add_subdirectory(dServer) # Create a list of common libraries shared between all binaries -set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp") +set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "mariadbConnCpp" "magic_enum" "MD5") # Add platform specific common libraries -if (UNIX) +if(UNIX) set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread") - if (NOT APPLE AND __include_backtrace__) + if(NOT APPLE AND ${INCLUDE_BACKTRACE}) set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace") endif() endif() @@ -381,12 +338,6 @@ add_subdirectory(dAuthServer) add_subdirectory(dChatServer) add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries -# Add our precompiled headers -target_precompile_headers( - dGame PRIVATE - ${HEADERS_DGAME} -) - target_precompile_headers( dZoneManager PRIVATE ${HEADERS_DZONEMANAGER} @@ -408,6 +359,6 @@ target_precompile_headers( "$<$:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" ) -if (${__enable_testing__} MATCHES "1") +if(${ENABLE_TESTING}) add_subdirectory(tests) endif() diff --git a/CMakePresets.json b/CMakePresets.json index f8170755..2feabc53 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -14,13 +14,13 @@ "generator": "Unix Makefiles" }, { - "name": "ci-ubuntu-20.04", + "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-11", + "name": "ci-macos-13", "displayName": "CI configure step for MacOS", "description": "Same as default, Used in GitHub actions workflow", "inherits": "default" @@ -67,15 +67,15 @@ "jobs": 2 }, { - "name": "ci-ubuntu-20.04", - "configurePreset": "ci-ubuntu-20.04", + "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-11", - "configurePreset": "ci-macos-11", + "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 @@ -83,8 +83,8 @@ ], "testPresets": [ { - "name": "ci-ubuntu-20.04", - "configurePreset": "ci-ubuntu-20.04", + "name": "ci-ubuntu-22.04", + "configurePreset": "ci-ubuntu-22.04", "displayName": "CI Tests on Linux", "description": "Runs all tests on a linux configuration", "execution": { @@ -95,8 +95,8 @@ } }, { - "name": "ci-macos-11", - "configurePreset": "ci-macos-11", + "name": "ci-macos-13", + "configurePreset": "ci-macos-13", "displayName": "CI Tests on MacOS", "description": "Runs all tests on a Mac configuration", "execution": { diff --git a/CMakeVariables.txt b/CMakeVariables.txt index 67b78aac..d9430d9d 100644 --- a/CMakeVariables.txt +++ b/CMakeVariables.txt @@ -1,20 +1,32 @@ PROJECT_VERSION_MAJOR=1 -PROJECT_VERSION_MINOR=0 +PROJECT_VERSION_MINOR=1 PROJECT_VERSION_PATCH=1 -# LICENSE -LICENSE=AGPL-3.0 + # Debugging -# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs. -__dynamic=1 -# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info. -# __ggdb=1 -# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs. -# __include_backtrace__=1 -# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries. -# __compile_backtrace__=1 +# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs. +DYNAMIC=1 + +# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info. +# Do note, changing this will re-build the whole server +GGDB=0 + +# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs. +# Do note, changing this will re-build the whole server +INCLUDE_BACKTRACE=0 + +# Set COMPILE_BACKTRACE to 1 to compile the backtrace library instead of using system libraries. +# Do note, changing this will re-build the whole server +COMPILE_BACKTRACE=0 + # Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with. -__maria_db_connector_compile_jobs__=1 +MARIADB_CONNECTOR_COMPILE_JOBS=1 + # When set to 1 and uncommented, compiling and linking testing folders and libraries will be done. -__enable_testing__=1 +ENABLE_TESTING=1 + # The path to OpenSSL. Change this if your OpenSSL install path is different than the default. OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/ + +# Whether or not to cache the entire CDClient Database into memory instead of lazy loading. +# 0 means to lazy load, all other values mean load the entire database. +CDCLIENT_CACHE_ALL=0 diff --git a/Docker.md b/Docker.md deleted file mode 100644 index b06bd0fe..00000000 --- a/Docker.md +++ /dev/null @@ -1,46 +0,0 @@ -# Run the Darkflame Server inside Docker - -## What you need - -- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop or on Linux normal Docker) -- [Docker Compose](https://docs.docker.com/compose/install/) (Included in Docker Desktop) -- LEGO® Universe Client. Check the main [README](./README.md) for details on this. - -## Run server inside Docker - -1. Copy `.env.example` and save it as `.env` inside the root directory of this repository -2. Edit the `.env` file and add your path to the root directory of your LEGO® Universe Client after `CLIENT_PATH=` -3. Update other values in the `.env` file as needed (be sure to update passwords!) -4. Run `docker compose up -d --build` -5. Run `docker compose exec darkflame /app/MasterServer -a` and setup your admin account -6. Follow the directions [here](https://github.com/DarkflameUniverse/AccountManager) to setup regular user accounts. The server will be accessible at: `http://:5000` -7. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup. -8. You're ready to connect your client! - -**NOTE #1**: If you're running an older version of Docker, you may need to use the command `docker-compose` instead of `docker compose`. - -**NOTE #2**: To stop the server simply run `docker compose down` and to restart it just run `docker compose up -d` again. No need to run all the steps above every time. - -**NOTE #3**: Docker buildkit needs to be enabled. https://docs.docker.com/develop/develop-images/build_enhancements/#to-enable-buildkit-builds - -**NOTE #4**: Make sure to run the following in the repo root directory after cloning so submodules are also downloaded. -``` -git submodule update --init --recursive -``` -**NOTE #5**: If DarkflameSetup fails due to not having cdclient.fdb, rename CDClient.fdb (in the same folder) to cdclient.fdb - -## Disable brickbuildfix - -If you don't need the http server running on port 80 do this: - -1. Create a file with the name `docker-compose.override.yml` in the root of the repository -2. Paste this content: - -```yml -services: - brickbuildfix: - profiles: - - donotstart -``` - -3. Now run `docker compose up -d` diff --git a/Docker_Windows.md b/Docker_Windows.md deleted file mode 100644 index 984bbe57..00000000 --- a/Docker_Windows.md +++ /dev/null @@ -1,58 +0,0 @@ -# Installation under Windows -## First Run -1. Navigate to the [Docker download page](https://www.docker.com/products/docker-desktop) and download docker. - - ![Docker Download Page](docker/images/Docker_Download_Page.png) - -2. Once the file has finished downloading, run it and proceed through the installation. Make sure, "Install required Windows components for WSL 2" is checked. - - ![Docker Desktop Installer Configuration](docker/images/Docker_Desktop_Installer_Configuration.png) - -3. If necessary, restart your computer. -4. After the restart, Docker Desktop will automatically open. If it does not, simply start it like any other program. -5. If a window "WSL 2 Installation is incomplete." pops up, follow the link and click "WSL2 Linux kernel update package for x64 machines". Run the downloaded file and once that finishes, click "Restart" in the Docker Desktop window. - - ![WSL 2 download](docker/images/WSL_2_download.png) - -6. Wait until Docker Desktop has started. You may skip the tutorial. -7. You may want to disable "Open Docker Dashboard at startup" in _Settings_ -> _General_ - - ![Disable Dashboard Autostart](docker/images/DD_General_Settings.png) - -8. Install [Git for Windows](https://git-scm.com/download/win). During the installation, simply confirming the defaults is sufficient. -9. In the folder you wish to save the Server, right click and select "Git Bash Here". -10. Type `git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer` -11. Once the command has completed (you can see you path again and can enter commands), close the window. -12. Inside the downloaded folder, copy `.env.example` and name the copy `.env` -13. Open `.env` with Notepad by right-clicking it and selecting _Open With_ -> _More apps_ -> _Notepad_. -14. Change the text after `CLIENT_PATH=` to the location of your client. This folder must contain either a folder `client` or `legouniverse.exe`. - > If you need the extra performance, place the client files in `\\wsl$\\...` to avoid working across file systems, see [Docker Best Practices](https://docs.docker.com/desktop/windows/wsl/#best-practices) and [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/filesystems#file-storage-and-performance-across-file-systems). - -15. Optionally, you can change the number after `BUILD_THREADS=` to the number of cores / threads your processor has. If your computer crashes while building, you can try to reduce this value. -16. After `ACCOUNT_MANAGER_SECRET=` paste a "SHA 256-bit Key" from https://keygen.io/ -17. If you are not only hosting a local server, change the value after `EXTERNAL_IP=` to the external IP address of your computer. -18. Change the two values `SECRET_VALUE_CHANGE_ME` to passwords only you know. Save and close the file. -19. In the extracted folder hit Shift+Right Click and select "Open PowerShell window here". - - ![Open PowerShell](docker/images/Open_Powershell.png) - -17. In the new window, paste (with right click) or type `docker compose up -d --build` and confirm with enter. -18. Once you see the blinking cursor and the path again, setup has finished and the server is already running. - - ![setup done](docker/images/setup_finished.png) - -19. Create an admin account by pasting `docker compose exec darkflame /app/MasterServer -a` and following the prompts. - - ![admin account creation](docker/images/Account_Creation.png) - -20. You can now login with these credentials at `http://your_ip:5000` (replace your_ip with your external IP). There you can create your account for playing as well as generate keys for other people to join; use these at `http://your_ip:5000/activate` - -## Normal Use -1. In Docker Desktop you should now see an entry `darkflameserver-main` and when you click on it all containers but `DarkflameSetup` should eventually be green. That means the server is running. - - ![server running](docker/images/Docker_Compose_Finished.png) - -2. For troubleshooting, you can check the logs of the various parts by clicking their entry. -3. You can start and stop the server with the corresponding buttons. Once all containers are grey, the server has shut down, and when all containers but `DarkflameSetup` are green, the server is running. Note that starting and stopping takes some time, please be patient. - - ![start stop buttons](docker/images/DD_Server_Startstop.png) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c1316a60 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,51 @@ +FROM gcc:12 as build + +WORKDIR /app + +RUN set -ex; \ + apt-get update; \ + apt-get install -y cmake + +COPY . /app/ +COPY --chmod=0500 ./build.sh /app/ + +RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt + +RUN ./build.sh + +FROM debian:12 as runtime + +WORKDIR /app + +RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \ + apt update && \ + apt install -y libssl3 libcurl4 && \ + rm -rf /var/lib/apt/lists/* + +# Grab libraries and load them +COPY --from=build /app/build/mariadbcpp/src/mariadb_connector_cpp-build/libmariadbcpp.so /usr/local/lib/ +COPY --from=build /app/build/mariadbcpp/src/mariadb_connector_cpp-build/libmariadb/libmariadb/libmariadb.so.3 /usr/local/lib +RUN ldconfig + +# Server bins +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/navmeshes /app/navmeshes +COPY --from=build /app/build/migrations /app/migrations +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/* + +# needed as the container runs with the root user +# and therefore sudo doesn't exist +ENV USE_SUDO_AUTH=0 +ENV DLU_CONFIG_DIR=/app/configs/ + +COPY --chmod=0500 ./entrypoint.sh /app/ +ENTRYPOINT [ "/app/entrypoint.sh" ] diff --git a/README.md b/README.md index b51fa4b0..f0f2ac36 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,9 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s ### Supply of resource files Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work. +## Step by step walkthrough for a single-player server +If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README. + ## Steps to setup server * [Clone this repository](#clone-the-repository) * [Install dependencies](#install-dependencies) @@ -34,6 +37,7 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive * [Verify your setup](#verify-your-setup) * [Running the server](#running-the-server) * [User Guide](#user-guide) +* [Docker](#docker) ## Clone the repository If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win) @@ -224,6 +228,44 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer ``` and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0. +### Linux Service +If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server. +_Note: You could use screen or tmux instead for virtual terminals_ +To run the server non-interactively, we can use a systemctl service by copying the following file: +```shell +cp ./systemd.example /etc/systemd/system/darkflame.service +``` + +Make sure to edit the file in `/etc/systemd/system/darkflame.service` and change the: +- `User` and `Group` to the user that runs the darkflame server. +- `ExecPath` to the full file path of the server executable. + +To register, enable and start the service use the following commands: + +- Reload the systemd manager configuration to make it aware of the new service file: +```shell +systemctl daemon-reload +``` +- Start the service: +```shell +systemctl start darkflame.service +``` +- Enable OR disable the service to start on boot using: +```shell +systemctl enable darkflame.service +systemctl disable darkflame.service +``` +- Verify that the service is running without errors: +```shell +systemctl status darkflame.service +``` +- You can also restart, stop, or check the logs of the service using journalctl +```shell +systemctl restart darkflame.service +systemctl stop darkflame.service +journalctl -xeu darkflame.service +``` + ### First admin user Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users! @@ -285,6 +327,7 @@ Below are known good SHA256 checksums of the client: * `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed) If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64. +You must also make absolutely sure your LEGO Universe client is not in a Windows OneDrive. DLU is not and will not support a client being stored in a OneDrive, so ensure you have moved the client outside of that location. ### Darkflame Universe Client Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there. @@ -305,6 +348,66 @@ certutil -hashfile SHA1 Known good *SHA1* checksum of the Darkflame Universe client: - `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed) + +# Docker + +The Darkflame Server is automatically built and published as a Docker Container / [OCI](https://opencontainers.org/) Image to the GitHub Container Registry at: +[`ghcr.io/darkflameuniverse/darkflameserver`](https://github.com/DarkflameUniverse/DarkflameServer/pkgs/container/darkflameserver). + +## Compose + +> [!WARNING] +> It seems that Docker Desktop on Windows with the WSL 2 backend has some issues with MariaDB (c.f. [mariadb-docker#331](https://github.com/MariaDB/mariadb-docker/issues/331)) triggered by NexusDashboard +> migrations, so this setup may not work for you. If that is the case, please tell us about your setup in [NexusDashboard#92](https://github.com/DarkflameUniverse/NexusDashboard/issues/92). + +You can use the `docker-compose` tool to [setup a MariaDB database](#database-setup), run the Darkflame Server and manage it with [Nexus Dashboard](https://github.com/DarkflameUniverse/NexusDashboard) all +at once. For that: + +- [Install Docker Desktop](https://docs.docker.com/get-docker/) +- Open the directory that contains your LU Client + - If the `legouniverse.exe` is in a subfolder called `client`, you're good to go. There may also be a folder `versions`. + - Otherwise, create a new `client` folder and move the exe and everything else (e.g. `res` and `locale`) in there. This is necessary to work around a bug in the client that will prevent that you to log back in after getting disconnected. +- Download the [docker-compose.yml](docker-compose.yml) file and place it next to `client`. +- Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env` + - You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows). + - Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords. + - Use a password generator like + - Avoid `:` and `@` characters + - Once the database user is created, changing the password will not update it, so the server will just fail to connect. + - Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family +- Open a terminal in the folder with the `docker-compose.yml` and `client` +- Run `docker compose up -d` + - This might require `sudo` on Linux, and a recent version of [docker compose](https://docs.docker.com/compose/install/) +- Run `docker exec -it dlu-darkflameserver-1 /app/MasterServer -a` and follow the instructions to create the initial admin account +- Open to access Nexus Dashboard with the admin account to create normal users +- Set `AUTHSERVERIP=0:localhost` in `client/boot.cfg` + - Replace `localhost` with the value of `EXTERNAL_IP` if you changed that earlier. + - Also make sure `UGCUSE3DSERVICES=7:` is set to `0` +- Launch `legouniverse.exe` + +## Standalone + +This assumes that you have a database deployed to your host or in another docker container. + +A basic deployment of this contianer would look like: +```sh +# example docker contianer deployment +docker run -it \ + -v /path/to/configs/:/app/configs \ + -v /path/to/logs/:/app/logs \ + -v /path/to/dumps/:/app/dumps \ + -v /path/to/res:/app/res:ro \ + -v /path/to/resServer:/app/resServer \ + -e DUMP_FOLDER=/app/dumps \ + -p 1001:1001/udp \ + -p 2005:2005/udp \ + -p 3000-3300:3000-3300/udp \ +ghcr.io/darkflameuniverse/darkflameserver:latest +``` +You will need to replace the `/path/to/`'s to reflect the paths on your host. + +Any config option in the `.ini`'s can be overridden with environmental variables: Ex: `log_to_console=1` from `shared_config.ini` would be overidden like `-e LOG_TO_CONSOLE=0` + # Development Documentation This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server [Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html) diff --git a/cmake/Utils.cmake b/cmake/Utils.cmake new file mode 100644 index 00000000..3c58241d --- /dev/null +++ b/cmake/Utils.cmake @@ -0,0 +1,51 @@ +# Parses a config file for a specific option and appends the new option if it does not exist +# If the new option does exist, this function will do nothing. +# file_name: The name of the file to parse +# old_option_name: The name of the option to find +# new_option_name: The name of the option to add +function(UpdateConfigOption file_name old_option_name new_option_name) + string(APPEND old_option_name "=") + string(APPEND new_option_name "=") + message(STATUS "Checking " ${file_name} " for " ${old_option_name} " and adding " ${new_option_name} " if it does not exist") + if(NOT EXISTS ${file_name}) + message(STATUS ${file_name} " does not exist. Doing nothing") + return() + endif() + file(READ ${file_name} current_file_contents) + string(REPLACE "\\\n" "" current_file_contents ${current_file_contents}) + string(REPLACE "\n" ";" current_file_contents ${current_file_contents}) + set(parsed_current_file_contents "") + + # Remove comment lines so they do not interfere with the variable parsing + foreach(line ${current_file_contents}) + string(FIND ${line} "#" is_comment) + + if(NOT ${is_comment} EQUAL 0) + string(APPEND parsed_current_file_contents ${line}) + endif() + endforeach() + + set(found_new_option -1) + set(found_old_option -1) + set(current_value -1) + + foreach(line ${current_file_contents}) + string(FIND ${line} ${old_option_name} old_option_in_file) + + if(${old_option_in_file} EQUAL 0) + set(found_old_option 1) + set(current_value ${line}) + endif() + + string(FIND ${line} ${new_option_name} found_new_option_in_file) + + if(${found_new_option_in_file} EQUAL 0) + set(found_new_option 1) + endif() + endforeach(line ${current_file_contents}) + + if(${found_old_option} EQUAL 1 AND NOT ${found_new_option} EQUAL 1) + string(REPLACE ${old_option_name} ${new_option_name} current_value ${current_value}) + file(APPEND ${file_name} "\n" ${current_value}) + endif() +endfunction() diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index 38910823..476e1a68 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -1,13 +1,14 @@ #include #include #include +#include #include #include //DLU Includes: #include "dCommonVars.h" #include "dServer.h" -#include "dLogger.h" +#include "Logger.h" #include "Database.h" #include "dConfig.h" #include "Diagnostics.h" @@ -15,7 +16,7 @@ //RakNet includes: #include "RakNetDefines.h" -#include +#include "MessageIdentifiers.h" //Auth includes: #include "AuthPackets.h" @@ -24,15 +25,17 @@ #include "eAuthMessageType.h" #include "Game.h" +#include "Server.h" + + namespace Game { - dLogger* logger = nullptr; + Logger* logger = nullptr; dServer* server = nullptr; dConfig* config = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; } -dLogger* SetupLogger(); void HandlePacket(Packet* packet); int main(int argc, char** argv) { @@ -42,29 +45,23 @@ int main(int argc, char** argv) { Diagnostics::SetProcessFileName(argv[0]); Diagnostics::Initialize(); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); + + Game::config = new dConfig("authconfig.ini"); + //Create all the objects we need to run our service: - Game::logger = SetupLogger(); + Server::SetupLogger("AuthServer"); if (!Game::logger) return EXIT_FAILURE; - //Read our config: - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - - Game::logger->Log("AuthServer", "Starting Auth server..."); - Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); - Game::logger->Log("AuthServer", "Compiled on: %s", __TIMESTAMP__); - - //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); + LOG("Starting Auth server..."); + LOG("Version: %s", PROJECT_VERSION); + LOG("Compiled on: %s", __TIMESTAMP__); try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { - Game::logger->Log("AuthServer", "Got an error while connecting to the database: %s", ex.what()); + LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("AuthServer"); delete Game::server; delete Game::logger; @@ -74,25 +71,26 @@ int main(int argc, char** argv) { //Find out the master's IP: std::string masterIP; uint32_t masterPort = 1500; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); - } - delete res; - delete stmt; + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; + } + LOG("Master is at %s:%d", masterIP.c_str(), masterPort); Game::randomEngine = std::mt19937(time(0)); //It's safe to pass 'localhost' here, as the IP is only used as the external IP. - uint32_t maxClients = 50; - uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default. - if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + std::string ourIP = "localhost"; + const uint32_t maxClients = GeneralUtils::TryParse(Game::config->GetValue("max_clients")).value_or(999); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown); + //LU client is hardcoded to use this for auth port, so I'm making it the default. + const uint32_t ourPort = GeneralUtils::TryParse(Game::config->GetValue("auth_server_port")).value_or(1001); + const auto externalIPString = Game::config->GetValue("external_ip"); + if (!externalIPString.empty()) ourIP = externalIPString; + + Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal); //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); @@ -103,13 +101,18 @@ int main(int argc, char** argv) { uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceLastSQLPing = 0; - while (!Game::shouldShutdown) { + AuthPackets::LoadClaimCodes(); + + Game::logger->Flush(); // once immediately before main loop + while (!Game::ShouldShutdown()) { //Check if we're still connected to master: if (!Game::server->GetIsConnectedToMaster()) { framesSinceMasterDisconnect++; - if (framesSinceMasterDisconnect >= authFramerate) + if (framesSinceMasterDisconnect >= authFramerate) { + LOG("No connection to master!"); break; //Exit our loop, shut down. + } } else framesSinceMasterDisconnect = 0; //In world we'd update our other systems here. @@ -134,16 +137,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - delete res; - delete stmt; - framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; @@ -152,6 +151,7 @@ int main(int argc, char** argv) { std::this_thread::sleep_until(t); } + LOG("Exited Main Loop! (signal %d)", Game::lastSignal); //Delete our objects here: Database::Destroy("AuthServer"); delete Game::server; @@ -161,18 +161,6 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -dLogger* SetupLogger() { - std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/AuthServer_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new dLogger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { if (packet->length < 4) return; diff --git a/dAuthServer/CMakeLists.txt b/dAuthServer/CMakeLists.txt index 00fa6e7a..7dcbf041 100644 --- a/dAuthServer/CMakeLists.txt +++ b/dAuthServer/CMakeLists.txt @@ -1,2 +1,7 @@ add_executable(AuthServer "AuthServer.cpp") -target_link_libraries(AuthServer ${COMMON_LIBRARIES}) + +target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer) + +target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) + +add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") diff --git a/dChatFilter/dChatFilter.cpp b/dChatFilter/dChatFilter.cpp index 92da9556..6e81db3b 100644 --- a/dChatFilter/dChatFilter.cpp +++ b/dChatFilter/dChatFilter.cpp @@ -8,7 +8,7 @@ #include #include "dCommonVars.h" -#include "dLogger.h" +#include "Logger.h" #include "dConfig.h" #include "Database.h" #include "Game.h" @@ -32,15 +32,11 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) { } //Read player names that are ok as well: - auto stmt = Database::CreatePreppedStmt("select name from charinfo;"); - auto res = stmt->executeQuery(); - while (res->next()) { - std::string line = res->getString(1).c_str(); - std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase - m_ApprovedWords.push_back(CalculateHash(line)); + auto approvedNames = Database::Get()->GetApprovedCharacterNames(); + for (auto& name : approvedNames) { + std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase + m_ApprovedWords.push_back(CalculateHash(name)); } - delete res; - delete stmt; } dChatFilter::~dChatFilter() { @@ -144,7 +140,7 @@ std::vector> dChatFilter::IsSentenceOkay(const std:: listOfBadSegments.emplace_back(position, originalSegment.length()); } - position += segment.length() + 1; + position += originalSegment.length() + 1; } return listOfBadSegments; diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index 9a47803d..cc4cee1f 100644 --- a/dChatServer/CMakeLists.txt +++ b/dChatServer/CMakeLists.txt @@ -1,10 +1,14 @@ set(DCHATSERVER_SOURCES + "ChatIgnoreList.cpp" "ChatPacketHandler.cpp" "PlayerContainer.cpp" ) add_executable(ChatServer "ChatServer.cpp") add_library(dChatServer ${DCHATSERVER_SOURCES}) +target_include_directories(dChatServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) +add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) -target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer) +target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer) + diff --git a/dChatServer/ChatIgnoreList.cpp b/dChatServer/ChatIgnoreList.cpp new file mode 100644 index 00000000..d77eeeed --- /dev/null +++ b/dChatServer/ChatIgnoreList.cpp @@ -0,0 +1,171 @@ +#include "ChatIgnoreList.h" +#include "PlayerContainer.h" +#include "eChatInternalMessageType.h" +#include "BitStreamUtils.h" +#include "Game.h" +#include "Logger.h" +#include "eObjectBits.h" + +#include "Database.h" + +// A note to future readers, The client handles all the actual ignoring logic: +// not allowing teams, rejecting DMs, friends requets etc. +// 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_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receivingPlayer); + + //portion that will get routed: + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type); +} + +void ChatIgnoreList::GetIgnoreList(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + if (!receiver.ignoredPlayers.empty()) { + LOG_DEBUG("Player %llu already has an ignore list, but is requesting it again.", playerId); + } else { + auto ignoreList = Database::Get()->GetIgnoreList(static_cast(playerId)); + if (ignoreList.empty()) { + LOG_DEBUG("Player %llu has no ignores", playerId); + return; + } + + for (auto& ignoredPlayer : ignoreList) { + receiver.ignoredPlayers.emplace_back(ignoredPlayer.name, ignoredPlayer.id); + GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(receiver.ignoredPlayers.back().playerId, eObjectBits::PERSISTENT); + } + } + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::GET_IGNORE); + + bitStream.Write(false); // Probably is Is Free Trial, but we don't care about that + bitStream.Write(0); // literally spacing due to struct alignment + + bitStream.Write(receiver.ignoredPlayers.size()); + for (const auto& ignoredPlayer : receiver.ignoredPlayers) { + bitStream.Write(ignoredPlayer.playerId); + bitStream.Write(LUWString(ignoredPlayer.playerName, 36)); + } + + Game::server->Send(&bitStream, packet->systemAddress, false); +} + +void ChatIgnoreList::AddIgnore(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + constexpr int32_t MAX_IGNORES = 32; + if (receiver.ignoredPlayers.size() > MAX_IGNORES) { + LOG_DEBUG("Player %llu has too many ignores", playerId); + return; + } + + inStream.IgnoreBytes(4); // ignore some garbage zeros idk + + LUWString toIgnoreName; + inStream.Read(toIgnoreName); + std::string toIgnoreStr = toIgnoreName.GetAsString(); + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::ADD_IGNORE); + + // Check if the player exists + LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY; + if (toIgnoreStr == receiver.playerName || toIgnoreStr.find("[GM]") == 0) { + LOG_DEBUG("Player %llu tried to ignore themselves", playerId); + + bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR); + } else if (std::count(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), toIgnoreStr) > 0) { + LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str()); + + bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED); + } else { + // Get the playerId falling back to query if not online + const auto& playerData = Game::playerContainer.GetPlayerData(toIgnoreStr); + if (!playerData) { + // Fall back to query + auto player = Database::Get()->GetCharacterInfo(toIgnoreStr); + if (!player || player->name != toIgnoreStr) { + LOG_DEBUG("Player %s not found", toIgnoreStr.c_str()); + } else { + ignoredPlayerId = player->id; + } + } else { + ignoredPlayerId = playerData.playerID; + } + + if (ignoredPlayerId != LWOOBJID_EMPTY) { + Database::Get()->AddIgnore(static_cast(playerId), static_cast(ignoredPlayerId)); + GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER); + GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT); + + receiver.ignoredPlayers.emplace_back(toIgnoreStr, ignoredPlayerId); + LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str()); + + bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS); + } else { + bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND); + } + } + + LUWString playerNameSend(toIgnoreStr, 33); + bitStream.Write(playerNameSend); + bitStream.Write(ignoredPlayerId); + + Game::server->Send(&bitStream, packet->systemAddress, false); +} + +void ChatIgnoreList::RemoveIgnore(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerId; + inStream.Read(playerId); + + auto& receiver = Game::playerContainer.GetPlayerDataMutable(playerId); + if (!receiver) { + LOG("Tried to get ignore list, but player %llu not found in container", playerId); + return; + } + + inStream.IgnoreBytes(4); // ignore some garbage zeros idk + + LUWString removedIgnoreName; + inStream.Read(removedIgnoreName); + std::string removedIgnoreStr = removedIgnoreName.GetAsString(); + + auto toRemove = std::remove(receiver.ignoredPlayers.begin(), receiver.ignoredPlayers.end(), removedIgnoreStr); + if (toRemove == receiver.ignoredPlayers.end()) { + LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str()); + return; + } + + Database::Get()->RemoveIgnore(static_cast(playerId), static_cast(toRemove->playerId)); + receiver.ignoredPlayers.erase(toRemove, receiver.ignoredPlayers.end()); + + CBITSTREAM; + WriteOutgoingReplyHeader(bitStream, receiver.playerID, ChatIgnoreList::Response::REMOVE_IGNORE); + + bitStream.Write(0); + LUWString playerNameSend(removedIgnoreStr, 33); + bitStream.Write(playerNameSend); + + Game::server->Send(&bitStream, packet->systemAddress, false); +} diff --git a/dChatServer/ChatIgnoreList.h b/dChatServer/ChatIgnoreList.h new file mode 100644 index 00000000..c713c966 --- /dev/null +++ b/dChatServer/ChatIgnoreList.h @@ -0,0 +1,27 @@ +#ifndef __CHATIGNORELIST__H__ +#define __CHATIGNORELIST__H__ + +struct Packet; + +#include + +namespace ChatIgnoreList { + void GetIgnoreList(Packet* packet); + void AddIgnore(Packet* packet); + void RemoveIgnore(Packet* packet); + + enum class Response : uint8_t { + ADD_IGNORE = 32, + REMOVE_IGNORE = 33, + GET_IGNORE = 34, + }; + + enum class AddResponse : uint8_t { + SUCCESS, + ALREADY_IGNORED, + PLAYER_NOT_FOUND, + GENERAL_ERROR, + }; +}; + +#endif //!__CHATIGNORELIST__H__ diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index 878cc71c..5e2e58d7 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -2,11 +2,11 @@ #include "PlayerContainer.h" #include "Database.h" #include -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "Game.h" #include "dServer.h" #include "GeneralUtils.h" -#include "dLogger.h" +#include "Logger.h" #include "eAddFriendResponseCode.h" #include "eAddFriendResponseType.h" #include "RakString.h" @@ -17,8 +17,8 @@ #include "eChatInternalMessageType.h" #include "eClientMessageType.h" #include "eGameMessageType.h" - -extern PlayerContainer playerContainer; +#include "StringifiedEnum.h" +#include "eGameMasterLevel.h" void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { //Get from the packet which player we want to do something with: @@ -26,42 +26,27 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { LWOOBJID playerID = 0; inStream.Read(playerID); - auto player = playerContainer.GetPlayerData(playerID); + auto& player = Game::playerContainer.GetPlayerDataMutable(playerID); if (!player) return; - //Get our friends list from the Db. Using a derived table since the friend of a player can be in either column. - std::unique_ptr stmt(Database::CreatePreppedStmt( - "SELECT fr.requested_player, best_friend, ci.name FROM " - "(SELECT CASE " - "WHEN player_id = ? THEN friend_id " - "WHEN friend_id = ? THEN player_id " - "END AS requested_player, best_friend FROM friends) AS fr " - "JOIN charinfo AS ci ON ci.id = fr.requested_player " - "WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?;")); - stmt->setUInt(1, static_cast(playerID)); - stmt->setUInt(2, static_cast(playerID)); - stmt->setUInt(3, static_cast(playerID)); - - std::vector friends; - - std::unique_ptr res(stmt->executeQuery()); - while (res->next()) { + auto friendsList = Database::Get()->GetFriendsList(playerID); + for (const auto& friendData : friendsList) { FriendData fd; fd.isFTP = false; // not a thing in DLU - fd.friendID = res->getUInt(1); + fd.friendID = friendData.friendID; GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT); GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); - fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs - if (fd.isBestFriend) player->countOfBestFriends += 1; - fd.friendName = res->getString(3); + fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs + if (fd.isBestFriend) player.countOfBestFriends += 1; + fd.friendName = friendData.friendName; //Now check if they're online: - auto fr = playerContainer.GetPlayerData(fd.friendID); + const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID); if (fr) { fd.isOnline = true; - fd.zoneID = fr->zoneID; + fd.zoneID = fr.zoneID; //Since this friend is online, we need to update them on the fact that we've just logged in: SendFriendUpdate(fr, player, 1, fd.isBestFriend); @@ -70,114 +55,106 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { fd.zoneID = LWOZONEID(); } - friends.push_back(fd); + player.friends.push_back(fd); } //Now, we need to send the friendlist to the server they came from: CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); bitStream.Write(playerID); //portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE); bitStream.Write(0); bitStream.Write(1); //Length of packet -- just writing one as it doesn't matter, client skips it. - bitStream.Write((uint16_t)friends.size()); + bitStream.Write(player.friends.size()); - for (auto& data : friends) { + for (const auto& data : player.friends) { data.Serialize(bitStream); } - player->friends = friends; - - SystemAddress sysAddr = player->sysAddr; + SystemAddress sysAddr = player.sysAddr; SEND_PACKET; } void ChatPacketHandler::HandleFriendRequest(Packet* packet) { - auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends"); - // If this config option doesn't exist, default to 5 which is what live used. - auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U; CINSTREAM_SKIP_HEADER; + LWOOBJID requestorPlayerID; - inStream.Read(requestorPlayerID); - uint32_t spacing{}; - inStream.Read(spacing); - std::string playerName = ""; - uint16_t character; - bool noMoreLettersInName = false; - - for (uint32_t j = 0; j < 33; j++) { - inStream.Read(character); - if (character == '\0') noMoreLettersInName = true; - if (!noMoreLettersInName) playerName.push_back(static_cast(character)); - } - + LUWString LUplayerName; char isBestFriendRequest{}; + + inStream.Read(requestorPlayerID); + inStream.IgnoreBytes(4); + inStream.Read(LUplayerName); inStream.Read(isBestFriendRequest); - auto requestor = playerContainer.GetPlayerData(requestorPlayerID); - if (requestor->playerName == playerName) { - SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); + auto playerName = LUplayerName.GetAsString(); + + auto& requestor = Game::playerContainer.GetPlayerDataMutable(requestorPlayerID); + if (!requestor) { + LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str()); + return; + } + + // you cannot friend yourself + if (requestor.playerName == playerName) { + SendFriendResponse(requestor, requestor, eAddFriendResponseType::GENERALERROR); return; }; - std::unique_ptr requestee(playerContainer.GetPlayerData(playerName)); + + auto& requestee = Game::playerContainer.GetPlayerDataMutable(playerName); // Check if player is online first if (isBestFriendRequest && !requestee) { - for (auto friendDataCandidate : requestor->friends) { - if (friendDataCandidate.friendName == playerName) { - requestee.reset(new PlayerData()); - // Setup the needed info since you can add a best friend offline. - requestee->playerID = friendDataCandidate.friendID; - requestee->playerName = friendDataCandidate.friendName; - requestee->zoneID = LWOZONEID(); + for (auto& friendDataCandidate : requestor.friends) { + if (friendDataCandidate.friendName != playerName) continue; + // Setup the needed info since you can add a best friend offline. + requestee.playerID = friendDataCandidate.friendID; + requestee.playerName = friendDataCandidate.friendName; + requestee.zoneID = LWOZONEID(); - FriendData requesteeFriendData{}; - requesteeFriendData.friendID = requestor->playerID; - requesteeFriendData.friendName = requestor->playerName; - requesteeFriendData.isFTP = false; - requesteeFriendData.isOnline = false; - requesteeFriendData.zoneID = requestor->zoneID; - requestee->friends.push_back(requesteeFriendData); - requestee->sysAddr = UNASSIGNED_SYSTEM_ADDRESS; - break; - } + FriendData requesteeFriendData{}; + requesteeFriendData.friendID = requestor.playerID; + requesteeFriendData.friendName = requestor.playerName; + requesteeFriendData.isFTP = false; + requesteeFriendData.isOnline = false; + requesteeFriendData.zoneID = requestor.zoneID; + requestee.friends.push_back(requesteeFriendData); + requestee.sysAddr = UNASSIGNED_SYSTEM_ADDRESS; + break; } } // If at this point we dont have a target, then they arent online and we cant send the request. // Send the response code that corresponds to what the error is. if (!requestee) { - std::unique_ptr nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;")); - nameQuery->setString(1, playerName); - std::unique_ptr result(nameQuery->executeQuery()); + requestee.playerName = playerName; + auto responseType = Database::Get()->GetCharacterInfo(playerName) + ? eAddFriendResponseType::NOTONLINE + : eAddFriendResponseType::INVALIDCHARACTER; - requestee.reset(new PlayerData()); - requestee->playerName = playerName; + SendFriendResponse(requestor, requestee, responseType); + return; + } - SendFriendResponse(requestor, requestee.get(), result->next() ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER); + // Prevent GM friend spam + // If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process + if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN ) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN); return; } if (isBestFriendRequest) { - std::unique_ptr friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - friendUpdate->setUInt(1, static_cast(requestorPlayerID)); - friendUpdate->setUInt(2, static_cast(requestee->playerID)); - friendUpdate->setUInt(3, static_cast(requestee->playerID)); - friendUpdate->setUInt(4, static_cast(requestorPlayerID)); - std::unique_ptr result(friendUpdate->executeQuery()); - LWOOBJID queryPlayerID = LWOOBJID_EMPTY; - LWOOBJID queryFriendID = LWOOBJID_EMPTY; uint8_t oldBestFriendStatus{}; uint8_t bestFriendStatus{}; - - if (result->next()) { + auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee.playerID); + if (bestFriendInfo) { // Get the IDs - queryPlayerID = result->getInt(1); - queryFriendID = result->getInt(2); - oldBestFriendStatus = result->getInt(3); + LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId; + LWOOBJID queryFriendID = bestFriendInfo->friendCharacterId; + oldBestFriendStatus = bestFriendInfo->bestFriendStatus; bestFriendStatus = oldBestFriendStatus; // Set the bits @@ -198,63 +175,66 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) { // Only do updates if there was a change in the bff status. if (oldBestFriendStatus != bestFriendStatus) { - if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) { - if (requestee->countOfBestFriends >= maxNumberOfBestFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false); + auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends(); + if (requestee.countOfBestFriends >= maxBestFriends || requestor.countOfBestFriends >= maxBestFriends) { + if (requestee.countOfBestFriends >= maxBestFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::THEIRFRIENDLISTFULL, false); } - if (requestor->countOfBestFriends >= maxNumberOfBestFriends) { - SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false); + if (requestor.countOfBestFriends >= maxBestFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::YOURFRIENDSLISTFULL, false); } } else { // Then update the database with this new info. - std::unique_ptr updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - updateQuery->setUInt(1, bestFriendStatus); - updateQuery->setUInt(2, static_cast(requestorPlayerID)); - updateQuery->setUInt(3, static_cast(requestee->playerID)); - updateQuery->setUInt(4, static_cast(requestee->playerID)); - updateQuery->setUInt(5, static_cast(requestorPlayerID)); - updateQuery->executeUpdate(); + Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee.playerID, bestFriendStatus); // Sent the best friend update here if the value is 3 if (bestFriendStatus == 3U) { - requestee->countOfBestFriends += 1; - requestor->countOfBestFriends += 1; - if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee.get(), requestor, eAddFriendResponseType::ACCEPTED, false, true); - if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::ACCEPTED, false, true); - for (auto& friendData : requestor->friends) { - if (friendData.friendID == requestee->playerID) { + requestee.countOfBestFriends += 1; + requestor.countOfBestFriends += 1; + if (requestee.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestee, requestor, eAddFriendResponseType::ACCEPTED, false, true); + if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::ACCEPTED, false, true); + for (auto& friendData : requestor.friends) { + if (friendData.friendID == requestee.playerID) { friendData.isBestFriend = true; } } - for (auto& friendData : requestee->friends) { - if (friendData.friendID == requestor->playerID) { + for (auto& friendData : requestee.friends) { + if (friendData.friendID == requestor.playerID) { friendData.isBestFriend = true; } } } } } else { - if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true); + if (requestor.sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee, eAddFriendResponseType::WAITINGAPPROVAL, true, true); } } else { - // Do not send this if we are requesting to be a best friend. - SendFriendRequest(requestee.get(), requestor); + auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends(); + if (requestee.friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::THEIRFRIENDLISTFULL, false); + } else if (requestor.friends.size() >= maxFriends) { + SendFriendResponse(requestor, requestee, eAddFriendResponseType::YOURFRIENDSLISTFULL, false); + } else { + // Do not send this if we are requesting to be a best friend. + SendFriendRequest(requestee, requestor); + } } - - // If the player is actually a player and not a ghost one defined above, release it from being deleted. - if (requestee->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) requestee.release(); } void ChatPacketHandler::HandleFriendResponse(Packet* packet) { CINSTREAM_SKIP_HEADER; - LWOOBJID playerID; - inStream.Read(playerID); - eAddFriendResponseCode clientResponseCode = static_cast(packet->data[0x14]); - std::string friendName = PacketUtils::ReadString(0x15, packet, true); + LWOOBJID playerID; + eAddFriendResponseCode clientResponseCode; + LUWString friendName; + + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(clientResponseCode); + inStream.Read(friendName); //Now to try and find both of these: - auto requestor = playerContainer.GetPlayerData(playerID); - auto requestee = playerContainer.GetPlayerData(friendName); + auto& requestor = Game::playerContainer.GetPlayerDataMutable(playerID); + auto& requestee = Game::playerContainer.GetPlayerDataMutable(friendName.GetAsString()); if (!requestor || !requestee) return; eAddFriendResponseType serverResponseCode{}; @@ -277,8 +257,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { // Now that we have handled the base cases, we need to check the other cases. if (serverResponseCode == eAddFriendResponseType::ACCEPTED) { - for (auto friendData : requestor->friends) { - if (friendData.friendID == requestee->playerID) { + for (const auto& friendData : requestor.friends) { + if (friendData.friendID == requestee.playerID) { serverResponseCode = eAddFriendResponseType::ALREADYFRIEND; if (friendData.isBestFriend) { isAlreadyBestFriends = 1U; @@ -290,29 +270,23 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { // This message is NOT sent for best friends and is handled differently for those requests. if (serverResponseCode == eAddFriendResponseType::ACCEPTED) { // Add the each player to the others friend list. - FriendData requestorData; - requestorData.zoneID = requestor->zoneID; - requestorData.friendID = requestor->playerID; - requestorData.friendName = requestor->playerName; + auto& requestorData = requestee.friends.emplace_back(); + requestorData.zoneID = requestor.zoneID; + requestorData.friendID = requestor.playerID; + requestorData.friendName = requestor.playerName; requestorData.isBestFriend = false; requestorData.isFTP = false; requestorData.isOnline = true; - requestee->friends.push_back(requestorData); - FriendData requesteeData; - requesteeData.zoneID = requestee->zoneID; - requesteeData.friendID = requestee->playerID; - requesteeData.friendName = requestee->playerName; + auto& requesteeData = requestor.friends.emplace_back(); + requesteeData.zoneID = requestee.zoneID; + requesteeData.friendID = requestee.playerID; + requesteeData.friendName = requestee.playerName; requesteeData.isBestFriend = false; requesteeData.isFTP = false; requesteeData.isOnline = true; - requestor->friends.push_back(requesteeData); - std::unique_ptr statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);")); - statement->setUInt(1, static_cast(requestor->playerID)); - statement->setUInt(2, static_cast(requestee->playerID)); - statement->setInt(3, 0); - statement->execute(); + Database::Get()->AddFriend(requestor.playerID, requestee.playerID); } if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends); @@ -322,217 +296,223 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) { void ChatPacketHandler::HandleRemoveFriend(Packet* packet) { CINSTREAM_SKIP_HEADER; LWOOBJID playerID; + LUWString LUFriendName; inStream.Read(playerID); - std::string friendName = PacketUtils::ReadString(0x14, packet, true); + inStream.IgnoreBytes(4); + inStream.Read(LUFriendName); + auto friendName = LUFriendName.GetAsString(); //we'll have to query the db here to find the user, since you can delete them while they're offline. //First, we need to find their ID: - std::unique_ptr stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;")); - stmt->setString(1, friendName.c_str()); - LWOOBJID friendID = 0; - std::unique_ptr res(stmt->executeQuery()); - while (res->next()) { - friendID = res->getUInt(1); + auto friendIdResult = Database::Get()->GetCharacterInfo(friendName); + if (friendIdResult) { + friendID = friendIdResult->id; } // Convert friendID to LWOOBJID GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT); GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER); - std::unique_ptr deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;")); - deletestmt->setUInt(1, static_cast(playerID)); - deletestmt->setUInt(2, static_cast(friendID)); - deletestmt->setUInt(3, static_cast(friendID)); - deletestmt->setUInt(4, static_cast(playerID)); - deletestmt->execute(); + Database::Get()->RemoveFriend(playerID, friendID); //Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended: - auto goonA = playerContainer.GetPlayerData(playerID); + auto& goonA = Game::playerContainer.GetPlayerDataMutable(playerID); if (goonA) { // Remove the friend from our list of friends - for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) { - if ((*friendData).friendID == friendID) { - if ((*friendData).isBestFriend) --goonA->countOfBestFriends; - goonA->friends.erase(friendData); + for (auto friendData = goonA.friends.cbegin(); friendData != goonA.friends.cend(); friendData++) { + if (friendData->friendID == friendID) { + if (friendData->isBestFriend) --goonA.countOfBestFriends; + goonA.friends.erase(friendData); break; } } SendRemoveFriend(goonA, friendName, true); } - auto goonB = playerContainer.GetPlayerData(friendID); + auto& goonB = Game::playerContainer.GetPlayerDataMutable(friendID); if (!goonB) return; + // Do it again for other person - for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) { - if ((*friendData).friendID == playerID) { - if ((*friendData).isBestFriend) --goonB->countOfBestFriends; - goonB->friends.erase(friendData); + for (auto friendData = goonB.friends.cbegin(); friendData != goonB.friends.cend(); friendData++) { + if (friendData->friendID == playerID) { + if (friendData->isBestFriend) --goonB.countOfBestFriends; + goonB.friends.erase(friendData); break; } } - std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID)); + std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID)); SendRemoveFriend(goonB, goonAName, true); } -void ChatPacketHandler::HandleChatMessage(Packet* packet) { - CINSTREAM_SKIP_HEADER; - LWOOBJID playerID = LWOOBJID_EMPTY; - inStream.Read(playerID); - - auto* sender = playerContainer.GetPlayerData(playerID); - - if (sender == nullptr) return; - - if (playerContainer.GetIsMuted(sender)) return; - - const auto senderName = std::string(sender->playerName.c_str()); - - inStream.SetReadOffset(0x14 * 8); - - uint8_t channel = 0; - inStream.Read(channel); - - std::string message = PacketUtils::ReadString(0x66, packet, true, 512); - - Game::logger->Log("ChatPacketHandler", "Got a message from (%s) [%d]: %s", senderName.c_str(), channel, message.c_str()); - - if (channel != 8) return; - - auto* team = playerContainer.GetTeam(playerID); - - if (team == nullptr) return; - - for (const auto memberId : team->memberIDs) { - auto* otherMember = playerContainer.GetPlayerData(memberId); - - if (otherMember == nullptr) return; - - const auto otherName = std::string(otherMember->playerName.c_str()); - - CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(otherMember->playerID); - - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(otherMember->playerID); - bitStream.Write(8); - bitStream.Write(69); - PacketUtils::WritePacketWString(senderName, 33, &bitStream); - bitStream.Write(sender->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - PacketUtils::WritePacketWString(otherName, 33, &bitStream); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //teams? - PacketUtils::WritePacketWString(message, 512, &bitStream); - - SystemAddress sysAddr = otherMember->sysAddr; - SEND_PACKET; - } -} - -void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { - LWOOBJID senderID = PacketUtils::ReadPacketS64(0x08, packet); - std::string receiverName = PacketUtils::ReadString(0x66, packet, true); - std::string message = PacketUtils::ReadString(0xAA, packet, true, 512); - - //Get the bois: - auto goonA = playerContainer.GetPlayerData(senderID); - auto goonB = playerContainer.GetPlayerData(receiverName); - if (!goonA || !goonB) return; - - if (playerContainer.GetIsMuted(goonA)) return; - - std::string goonAName = goonA->playerName.c_str(); - std::string goonBName = goonB->playerName.c_str(); - - //To the sender: - { - CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonA->playerID); - - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - PacketUtils::WritePacketWString(goonAName, 33, &bitStream); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - PacketUtils::WritePacketWString(goonBName, 33, &bitStream); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //success - PacketUtils::WritePacketWString(message, 512, &bitStream); - - SystemAddress sysAddr = goonA->sysAddr; - SEND_PACKET; - } - - //To the receiver: - { - CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonB->playerID); - - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - PacketUtils::WritePacketWString(goonAName, 33, &bitStream); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - PacketUtils::WritePacketWString(goonBName, 33, &bitStream); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(3); //new whisper - PacketUtils::WritePacketWString(message, 512, &bitStream); - - SystemAddress sysAddr = goonB->sysAddr; - SEND_PACKET; - } -} - -void ChatPacketHandler::HandleTeamInvite(Packet* packet) { +void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) { CINSTREAM_SKIP_HEADER; LWOOBJID playerID; inStream.Read(playerID); - std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true); + auto& player = Game::playerContainer.GetPlayerData(playerID); + if (!player) return; + inStream.Read(player.gmLevel); +} - auto* player = playerContainer.GetPlayerData(playerID); +// 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) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerID; + inStream.Read(playerID); - if (player == nullptr) { + const auto& sender = Game::playerContainer.GetPlayerData(playerID); + if (!sender || sender.GetIsMuted()) return; + + eChatChannel channel; + uint32_t size; + + inStream.IgnoreBytes(4); + inStream.Read(channel); + inStream.Read(size); + inStream.IgnoreBytes(77); + + LUWString message(size); + inStream.Read(message); + + LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str()); + + switch (channel) { + case eChatChannel::TEAM: { + auto* team = Game::playerContainer.GetTeam(playerID); + if (team == nullptr) return; + + for (const auto memberId : team->memberIDs) { + const auto& otherMember = Game::playerContainer.GetPlayerData(memberId); + if (!otherMember) return; + SendPrivateChatMessage(sender, otherMember, otherMember, message, eChatChannel::TEAM, eChatMessageResponseCode::SENT); + } + break; + } + default: + LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(channel).data()); + break; + } +} + +// 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::HandlePrivateChatMessage(Packet* packet) { + CINSTREAM_SKIP_HEADER; + LWOOBJID playerID; + inStream.Read(playerID); + + const auto& sender = Game::playerContainer.GetPlayerData(playerID); + if (!sender || sender.GetIsMuted()) return; + + eChatChannel channel; + uint32_t size; + LUWString LUReceiverName; + + inStream.IgnoreBytes(4); + inStream.Read(channel); + if (channel != eChatChannel::PRIVATE_CHAT) LOG("WARNING: Received Private chat with the wrong channel!"); + + inStream.Read(size); + inStream.IgnoreBytes(77); + + inStream.Read(LUReceiverName); + auto receiverName = LUReceiverName.GetAsString(); + inStream.IgnoreBytes(2); + + LUWString message(size); + inStream.Read(message); + + LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str()); + + const auto& receiver = Game::playerContainer.GetPlayerData(receiverName); + if (!receiver) { + PlayerData otherPlayer; + otherPlayer.playerName = receiverName; + auto responseType = Database::Get()->GetCharacterInfo(receiverName) + ? eChatMessageResponseCode::NOTONLINE + : eChatMessageResponseCode::GENERALERROR; + + SendPrivateChatMessage(sender, otherPlayer, sender, message, eChatChannel::GENERAL, responseType); return; } - auto* team = playerContainer.GetTeam(playerID); + // Check to see if they are friends + // only freinds can whispr each other + for (const auto& fr : receiver.friends) { + if (fr.friendID == sender.playerID) { + //To the sender: + SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::SENT); + //To the receiver: + SendPrivateChatMessage(sender, receiver, receiver, message, eChatChannel::PRIVATE_CHAT, eChatMessageResponseCode::RECEIVEDNEWWHISPER); + return; + } + } + SendPrivateChatMessage(sender, receiver, sender, message, eChatChannel::GENERAL, eChatMessageResponseCode::NOTFRIENDS); +} + +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_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(routeTo.playerID); + + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); + bitStream.Write(sender.playerID); + bitStream.Write(channel); + bitStream.Write(0); // not used + bitStream.Write(LUWString(sender.playerName)); + bitStream.Write(sender.playerID); + bitStream.Write(0); // sourceID + bitStream.Write(sender.gmLevel); + bitStream.Write(LUWString(receiver.playerName)); + bitStream.Write(receiver.gmLevel); + bitStream.Write(responseCode); + bitStream.Write(message); + + SystemAddress sysAddr = routeTo.sysAddr; + SEND_PACKET; +} + + +void ChatPacketHandler::HandleTeamInvite(Packet* packet) { + CINSTREAM_SKIP_HEADER; + + LWOOBJID playerID; + LUWString invitedPlayer; + + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(invitedPlayer); + + const auto& player = Game::playerContainer.GetPlayerData(playerID); + + if (!player) return; + + auto* team = Game::playerContainer.GetTeam(playerID); if (team == nullptr) { - team = playerContainer.CreateTeam(playerID); + team = Game::playerContainer.CreateTeam(playerID); } - auto* other = playerContainer.GetPlayerData(invitedPlayer); + const auto& other = Game::playerContainer.GetPlayerData(invitedPlayer.GetAsString()); - if (other == nullptr) { - return; - } + if (!other) return; - if (playerContainer.GetTeam(other->playerID) != nullptr) { + if (Game::playerContainer.GetTeam(other.playerID) != nullptr) { return; } if (team->memberIDs.size() > 3) { // no more teams greater than 4 - Game::logger->Log("ChatPacketHandler", "Someone tried to invite a 5th player to a team"); + LOG("Someone tried to invite a 5th player to a team"); return; } SendTeamInvite(other, player); - Game::logger->Log("ChatPacketHandler", "Got team invite: %llu -> %s", playerID, invitedPlayer.c_str()); + LOG("Got team invite: %llu -> %s", playerID, invitedPlayer.GetAsString().c_str()); } void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { @@ -546,26 +526,26 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { LWOOBJID leaderID = LWOOBJID_EMPTY; inStream.Read(leaderID); - Game::logger->Log("ChatPacketHandler", "Accepted invite: %llu -> %llu (%d)", playerID, leaderID, declined); + LOG("Accepted invite: %llu -> %llu (%d)", playerID, leaderID, declined); if (declined) { return; } - auto* team = playerContainer.GetTeam(leaderID); + auto* team = Game::playerContainer.GetTeam(leaderID); if (team == nullptr) { - Game::logger->Log("ChatPacketHandler", "Failed to find team for leader (%llu)", leaderID); + LOG("Failed to find team for leader (%llu)", leaderID); - team = playerContainer.GetTeam(playerID); + team = Game::playerContainer.GetTeam(playerID); } if (team == nullptr) { - Game::logger->Log("ChatPacketHandler", "Failed to find team for player (%llu)", playerID); + LOG("Failed to find team for player (%llu)", playerID); return; } - playerContainer.AddMember(team, playerID); + Game::playerContainer.AddMember(team, playerID); } void ChatPacketHandler::HandleTeamLeave(Packet* packet) { @@ -575,64 +555,71 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) { uint32_t size = 0; inStream.Read(size); - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); - Game::logger->Log("ChatPacketHandler", "(%llu) leaving team", playerID); + LOG("(%llu) leaving team", playerID); if (team != nullptr) { - playerContainer.RemoveMember(team, playerID, false, false, true); + Game::playerContainer.RemoveMember(team, playerID, false, false, true); } } void ChatPacketHandler::HandleTeamKick(Packet* packet) { CINSTREAM_SKIP_HEADER; + LWOOBJID playerID = LWOOBJID_EMPTY; + LUWString kickedPlayer; + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(kickedPlayer); - std::string kickedPlayer = PacketUtils::ReadString(0x14, packet, true); - Game::logger->Log("ChatPacketHandler", "(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str()); + LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.GetAsString().c_str()); - auto* kicked = playerContainer.GetPlayerData(kickedPlayer); + const auto& kicked = Game::playerContainer.GetPlayerData(kickedPlayer.GetAsString()); LWOOBJID kickedId = LWOOBJID_EMPTY; - if (kicked != nullptr) { - kickedId = kicked->playerID; + if (kicked) { + kickedId = kicked.playerID; } else { - kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer)); + kickedId = Game::playerContainer.GetId(kickedPlayer.string); } if (kickedId == LWOOBJID_EMPTY) return; - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID || team->leaderID == kickedId) return; - playerContainer.RemoveMember(team, kickedId, false, true, false); + Game::playerContainer.RemoveMember(team, kickedId, false, true, false); } } void ChatPacketHandler::HandleTeamPromote(Packet* packet) { CINSTREAM_SKIP_HEADER; + LWOOBJID playerID = LWOOBJID_EMPTY; + LUWString promotedPlayer; + inStream.Read(playerID); + inStream.IgnoreBytes(4); + inStream.Read(promotedPlayer); - std::string promotedPlayer = PacketUtils::ReadString(0x14, packet, true); + LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.GetAsString().c_str()); - Game::logger->Log("ChatPacketHandler", "(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str()); + const auto& promoted = Game::playerContainer.GetPlayerData(promotedPlayer.GetAsString()); - auto* promoted = playerContainer.GetPlayerData(promotedPlayer); + if (!promoted) return; - if (promoted == nullptr) return; - - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID) return; - playerContainer.PromoteMember(team, promoted->playerID); + Game::playerContainer.PromoteMember(team, promoted.playerID); } } @@ -646,16 +633,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) { char option; inStream.Read(option); - auto* team = playerContainer.GetTeam(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); if (team != nullptr) { if (team->leaderID != playerID) return; team->lootFlag = option; - playerContainer.TeamStatusUpdate(team); + Game::playerContainer.TeamStatusUpdate(team); - playerContainer.UpdateTeamsOnWorld(team, false); + Game::playerContainer.UpdateTeamsOnWorld(team, false); } } @@ -664,18 +651,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { LWOOBJID playerID = LWOOBJID_EMPTY; inStream.Read(playerID); - auto* team = playerContainer.GetTeam(playerID); - auto* data = playerContainer.GetPlayerData(playerID); + auto* team = Game::playerContainer.GetTeam(playerID); + const auto& data = Game::playerContainer.GetPlayerData(playerID); - if (team != nullptr && data != nullptr) { - if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) { - playerContainer.RemoveMember(team, playerID, false, false, true, true); + if (team != nullptr && data) { + if (team->local && data.zoneID.GetMapID() != team->zoneId.GetMapID() && data.zoneID.GetCloneID() != team->zoneId.GetCloneID()) { + Game::playerContainer.RemoveMember(team, playerID, false, false, true, true); return; } if (team->memberIDs.size() <= 1 && !team->local) { - playerContainer.DisbandTeam(team); + Game::playerContainer.DisbandTeam(team); return; } @@ -686,51 +673,51 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) { ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY); } - playerContainer.TeamStatusUpdate(team); + Game::playerContainer.TeamStatusUpdate(team); - const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(data.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = playerContainer.GetPlayerData(memberId); + const auto& otherMember = Game::playerContainer.GetPlayerData(memberId); if (memberId == playerID) continue; - const auto memberName = playerContainer.GetName(memberId); + const auto memberName = Game::playerContainer.GetName(memberId); - if (otherMember != nullptr) { - ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID); + if (otherMember) { + ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data.playerID, data.zoneID); } - ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); + ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0)); } - playerContainer.UpdateTeamsOnWorld(team, false); + Game::playerContainer.UpdateTeamsOnWorld(team, false); } } -void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) { +void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE); - PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); - bitStream.Write(sender->playerID); + bitStream.Write(LUWString(sender.playerName.c_str())); + bitStream.Write(sender.playerID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) { +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; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_INVITE_CONFIRM); bitStream.Write(bLeaderIsFreeTrial); @@ -740,24 +727,24 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader bitStream.Write(ucLootFlag); bitStream.Write(ucNumOfOtherPlayers); bitStream.Write(ucResponseCode); - bitStream.Write(static_cast(wsLeaderName.size())); + bitStream.Write(wsLeaderName.size()); for (const auto character : wsLeaderName) { bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) { +void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_GET_STATUS_RESPONSE); bitStream.Write(i64LeaderID); @@ -765,70 +752,70 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI bitStream.Write(0); // BinaryBuffe, no clue what's in here bitStream.Write(ucLootFlag); bitStream.Write(ucNumOfOtherPlayers); - bitStream.Write(static_cast(wsLeaderName.size())); + bitStream.Write(wsLeaderName.size()); for (const auto character : wsLeaderName) { bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) { +void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_SET_LEADER); bitStream.Write(i64PlayerID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) { +void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_ADD_PLAYER); bitStream.Write(bIsFreeTrial); bitStream.Write(bLocal); bitStream.Write(bNoLootOnDeath); bitStream.Write(i64PlayerID); - bitStream.Write(static_cast(wsPlayerName.size())); + bitStream.Write(wsPlayerName.size()); for (const auto character : wsPlayerName) { bitStream.Write(character); } bitStream.Write1(); - if (receiver->zoneID.GetCloneID() == zoneID.GetCloneID()) { + if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) { zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0); } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) { +void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_REMOVE_PLAYER); bitStream.Write(bDisband); @@ -837,37 +824,37 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband bitStream.Write(bLocal); bitStream.Write(i64LeaderID); bitStream.Write(i64PlayerID); - bitStream.Write(static_cast(wsPlayerName.size())); + bitStream.Write(wsPlayerName.size()); for (const auto character : wsPlayerName) { bitStream.Write(character); } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { +void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: CMSGHEADER; - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); bitStream.Write(eGameMessageType::TEAM_SET_OFF_WORLD_FLAG); bitStream.Write(i64PlayerID); - if (receiver->zoneID.GetCloneID() == zoneID.GetCloneID()) { + if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) { zoneID = LWOZONEID(zoneID.GetMapID(), zoneID.GetInstanceID(), 0); } bitStream.Write(zoneID); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend) { +void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend) { /*chat notification is displayed if log in / out and friend is updated in friends list [u8] - update type Update types @@ -882,94 +869,88 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla [bool] - is FTP*/ CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(friendData->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(friendData.playerID); //portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY); bitStream.Write(notifyType); - std::string playerName = playerData->playerName.c_str(); + std::string playerName = playerData.playerName.c_str(); - PacketUtils::WritePacketWString(playerName, 33, &bitStream); + bitStream.Write(LUWString(playerName)); - bitStream.Write(playerData->zoneID.GetMapID()); - bitStream.Write(playerData->zoneID.GetInstanceID()); + bitStream.Write(playerData.zoneID.GetMapID()); + bitStream.Write(playerData.zoneID.GetInstanceID()); - if (playerData->zoneID.GetCloneID() == friendData->zoneID.GetCloneID()) { + if (playerData.zoneID.GetCloneID() == friendData.zoneID.GetCloneID()) { bitStream.Write(0); } else { - bitStream.Write(playerData->zoneID.GetCloneID()); + bitStream.Write(playerData.zoneID.GetCloneID()); } bitStream.Write(isBestFriend); //isBFF bitStream.Write(0); //isFTP - SystemAddress sysAddr = friendData->sysAddr; + SystemAddress sysAddr = friendData.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* sender) { - if (!receiver || !sender) return; - +void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const PlayerData& sender) { //Make sure people aren't requesting people that they're already friends with: - for (auto fr : receiver->friends) { - if (fr.friendID == sender->playerID) { + for (const auto& fr : receiver.friends) { + if (fr.friendID == sender.playerID) { SendFriendResponse(sender, receiver, eAddFriendResponseType::ALREADYFRIEND, fr.isBestFriend); return; //we have this player as a friend, yeet this function so it doesn't send another request. } } CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST); - PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST); + bitStream.Write(LUWString(sender.playerName)); bitStream.Write(0); // This is a BFF flag however this is unused in live and does not have an implementation client side. - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { - if (!receiver || !sender) return; - +void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); // Portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE); bitStream.Write(responseCode); // For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. - bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS); + bitStream.Write(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS); // Then write the player name - PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream); + bitStream.Write(LUWString(sender.playerName)); // Then if this is an acceptance code, write the following extra info. if (responseCode == eAddFriendResponseType::ACCEPTED) { - bitStream.Write(sender->playerID); - bitStream.Write(sender->zoneID); + bitStream.Write(sender.playerID); + bitStream.Write(sender.zoneID); bitStream.Write(isBestFriendRequest); //isBFF bitStream.Write(0); //isFTP } - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } -void ChatPacketHandler::SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful) { - if (!receiver) return; - +void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); + bitStream.Write(receiver.playerID); //portion that will get routed: - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE); bitStream.Write(isSuccessful); //isOnline - PacketUtils::WritePacketWString(personToRemove, 33, &bitStream); + bitStream.Write(LUWString(personToRemove)); - SystemAddress sysAddr = receiver->sysAddr; + SystemAddress sysAddr = receiver.sysAddr; SEND_PACKET; } diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index f2d83502..847fc899 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -4,16 +4,56 @@ #include "BitStream.h" struct PlayerData; + enum class eAddFriendResponseType : uint8_t; +enum class eChatChannel : uint8_t { + SYSTEMNOTIFY = 0, + SYSTEMWARNING, + SYSTEMERROR, + BROADCAST, + LOCAL, + LOCALNOANIM, + EMOTE, + PRIVATE_CHAT, + TEAM, + TEAMLOCAL, + GUILD, + GUILDNOTIFY, + PROPERTY, + ADMIN, + COMBATDAMAGE, + COMBATHEALING, + COMBATLOOT, + COMBATEXP, + COMBATDEATH, + GENERAL, + TRADE, + LFG, + USER +}; + + +enum class eChatMessageResponseCode : uint8_t { + SENT = 0, + NOTONLINE, + GENERALERROR, + RECEIVEDNEWWHISPER, + NOTFRIENDS, + SENDERFREETRIAL, + RECEIVERFREETRIAL, +}; + namespace ChatPacketHandler { void HandleFriendlistRequest(Packet* packet); void HandleFriendRequest(Packet* packet); void HandleFriendResponse(Packet* packet); void HandleRemoveFriend(Packet* packet); + void HandleGMLevelUpdate(Packet* packet); void HandleChatMessage(Packet* packet); void HandlePrivateChatMessage(Packet* packet); + void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode); void HandleTeamInvite(Packet* packet); void HandleTeamInviteResponse(Packet* packet); @@ -23,18 +63,18 @@ namespace ChatPacketHandler { void HandleTeamLootOption(Packet* packet); void HandleTeamStatusRequest(Packet* packet); - void SendTeamInvite(PlayerData* receiver, PlayerData* sender); - void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); - void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); - void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID); - void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); - void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); - void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); + void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender); + void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); + void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); + void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID); + void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); + void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); + void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); //FriendData is the player we're SENDING this stuff to. Player is the friend that changed state. - void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend); + void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend); - void SendFriendRequest(PlayerData* receiver, PlayerData* sender); - void SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); - void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful); + void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender); + void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); + void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful); }; diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index 185bccbf..44957042 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -6,7 +6,7 @@ //DLU Includes: #include "dCommonVars.h" #include "dServer.h" -#include "dLogger.h" +#include "Logger.h" #include "Database.h" #include "dConfig.h" #include "dChatFilter.h" @@ -19,29 +19,29 @@ #include "eChatMessageType.h" #include "eChatInternalMessageType.h" #include "eWorldMessageType.h" +#include "ChatIgnoreList.h" +#include "StringifiedEnum.h" #include "Game.h" +#include "Server.h" //RakNet includes: #include "RakNetDefines.h" -#include +#include "MessageIdentifiers.h" namespace Game { - dLogger* logger = nullptr; + Logger* logger = nullptr; dServer* server = nullptr; dConfig* config = nullptr; dChatFilter* chatFilter = nullptr; AssetManager* assetManager = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; + PlayerContainer playerContainer; } - -dLogger* SetupLogger(); void HandlePacket(Packet* packet); -PlayerContainer playerContainer; - int main(int argc, char** argv) { constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFrameDelta = mediumFrameDelta; @@ -49,18 +49,20 @@ int main(int argc, char** argv) { Diagnostics::SetProcessFileName(argv[0]); Diagnostics::Initialize(); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); + + Game::config = new dConfig("chatconfig.ini"); + //Create all the objects we need to run our service: - Game::logger = SetupLogger(); + Server::SetupLogger("ChatServer"); if (!Game::logger) return EXIT_FAILURE; //Read our config: - Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string()); - Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); - Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1"); - Game::logger->Log("ChatServer", "Starting Chat server..."); - Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); - Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__); + LOG("Starting Chat server..."); + LOG("Version: %s", PROJECT_VERSION); + LOG("Compiled on: %s", __TIMESTAMP__); try { std::string clientPathStr = Game::config->GetValue("client_location"); @@ -72,21 +74,16 @@ int main(int argc, char** argv) { Game::assetManager = new AssetManager(clientPath); } catch (std::runtime_error& ex) { - Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what()); + LOG("Got an error while setting up assets: %s", ex.what()); return EXIT_FAILURE; } //Connect to the MySQL Database - std::string mysql_host = Game::config->GetValue("mysql_host"); - std::string mysql_database = Game::config->GetValue("mysql_database"); - std::string mysql_username = Game::config->GetValue("mysql_username"); - std::string mysql_password = Game::config->GetValue("mysql_password"); - try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { - Game::logger->Log("ChatServer", "Got an error while connecting to the database: %s", ex.what()); + LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("ChatServer"); delete Game::server; delete Game::logger; @@ -96,28 +93,27 @@ int main(int argc, char** argv) { //Find out the master's IP: std::string masterIP; uint32_t masterPort = 1000; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; } - - delete res; - delete stmt; - //It's safe to pass 'localhost' here, as the IP is only used as the external IP. - uint32_t maxClients = 50; - uint32_t ourPort = 1501; - if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + std::string ourIP = "localhost"; + const uint32_t maxClients = GeneralUtils::TryParse(Game::config->GetValue("max_clients")).value_or(999); + const uint32_t ourPort = GeneralUtils::TryParse(Game::config->GetValue("chat_server_port")).value_or(1501); + const auto externalIPString = Game::config->GetValue("external_ip"); + if (!externalIPString.empty()) ourIP = externalIPString; - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown); + Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal); - Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); + const bool dontGenerateDCF = GeneralUtils::TryParse(Game::config->GetValue("dont_generate_dcf")).value_or(false); + Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF); Game::randomEngine = std::mt19937(time(0)); + Game::playerContainer.Initialize(); + //Run it until server gets a kill message from Master: auto t = std::chrono::high_resolution_clock::now(); Packet* packet = nullptr; @@ -127,7 +123,8 @@ int main(int argc, char** argv) { uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceLastSQLPing = 0; - while (!Game::shouldShutdown) { + Game::logger->Flush(); // once immediately before main loop + while (!Game::ShouldShutdown()) { //Check if we're still connected to master: if (!Game::server->GetIsConnectedToMaster()) { framesSinceMasterDisconnect++; @@ -158,15 +155,12 @@ int main(int argc, char** argv) { //Find out the master's IP for absolutely no reason: std::string masterIP; uint32_t masterPort; - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); - auto res = stmt->executeQuery(); - while (res->next()) { - masterIP = res->getString(1).c_str(); - masterPort = res->getInt(2); - } - delete res; - delete stmt; + auto masterInfo = Database::Get()->GetMasterInfo(); + if (masterInfo) { + masterIP = masterInfo->ip; + masterPort = masterInfo->port; + } framesSinceLastSQLPing = 0; } else framesSinceLastSQLPing++; @@ -185,25 +179,13 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -dLogger* SetupLogger() { - std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string(); - bool logToConsole = false; - bool logDebugStatements = false; -#ifdef _DEBUG - logToConsole = true; - logDebugStatements = true; -#endif - - return new dLogger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { - Game::logger->Log("ChatServer", "A server has disconnected, erasing their connected players from the list."); + LOG("A server has disconnected, erasing their connected players from the list."); } if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) { - Game::logger->Log("ChatServer", "A server is connecting, awaiting user list."); + LOG("A server is connecting, awaiting user list."); } if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue. @@ -211,19 +193,19 @@ void HandlePacket(Packet* packet) { if (static_cast(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { switch (static_cast(packet->data[3])) { case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION: - playerContainer.InsertPlayer(packet); + Game::playerContainer.InsertPlayer(packet); break; case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: - playerContainer.RemovePlayer(packet); + Game::playerContainer.RemovePlayer(packet); break; case eChatInternalMessageType::MUTE_UPDATE: - playerContainer.MuteUpdate(packet); + Game::playerContainer.MuteUpdate(packet); break; case eChatInternalMessageType::CREATE_TEAM: - playerContainer.CreateTeamServer(packet); + Game::playerContainer.CreateTeamServer(packet); break; case eChatInternalMessageType::ANNOUNCEMENT: { @@ -234,18 +216,27 @@ void HandlePacket(Packet* packet) { } default: - Game::logger->Log("ChatServer", "Unknown CHAT_INTERNAL id: %i", int(packet->data[3])); + LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3])); } } if (static_cast(packet->data[1]) == eConnectionType::CHAT) { - switch (static_cast(packet->data[3])) { + eChatMessageType chat_message_type = static_cast(packet->data[3]); + switch (chat_message_type) { case eChatMessageType::GET_FRIENDS_LIST: ChatPacketHandler::HandleFriendlistRequest(packet); break; case eChatMessageType::GET_IGNORE_LIST: - Game::logger->Log("ChatServer", "Asked for ignore list, but is unimplemented right now."); + ChatIgnoreList::GetIgnoreList(packet); + break; + + case eChatMessageType::ADD_IGNORE: + ChatIgnoreList::AddIgnore(packet); + break; + + case eChatMessageType::REMOVE_IGNORE: + ChatIgnoreList::RemoveIgnore(packet); break; case eChatMessageType::TEAM_GET_STATUS: @@ -301,21 +292,73 @@ void HandlePacket(Packet* packet) { case eChatMessageType::TEAM_SET_LOOT: ChatPacketHandler::HandleTeamLootOption(packet); break; - + case eChatMessageType::GMLEVEL_UPDATE: + ChatPacketHandler::HandleGMLevelUpdate(packet); + break; + case eChatMessageType::LOGIN_SESSION_NOTIFY: + case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE: + case eChatMessageType::WORLD_DISCONNECT_REQUEST: + case eChatMessageType::WORLD_PROXIMITY_RESPONSE: + case eChatMessageType::WORLD_PARCEL_RESPONSE: + case eChatMessageType::TEAM_MISSED_INVITE_CHECK: + case eChatMessageType::GUILD_CREATE: + case eChatMessageType::GUILD_INVITE: + case eChatMessageType::GUILD_INVITE_RESPONSE: + case eChatMessageType::GUILD_LEAVE: + 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: + case eChatMessageType::PROPERTY_MODERATION_CHANGED: + case eChatMessageType::PROPERTY_BUILDMODE_CHANGED: + case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT: + case eChatMessageType::MAIL: + case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST: + case eChatMessageType::REPUTATION_UPDATE: + case eChatMessageType::SEND_CANNED_TEXT: + case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST: + 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: + case eChatMessageType::MATCH_REQUEST: + case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE: + 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(chat_message_type).data(), chat_message_type); + break; default: - Game::logger->Log("ChatServer", "Unknown CHAT id: %i", int(packet->data[3])); + LOG("Unknown CHAT Message id: %i", chat_message_type); } } if (static_cast(packet->data[1]) == eConnectionType::WORLD) { switch (static_cast(packet->data[3])) { case eWorldMessageType::ROUTE_PACKET: { - Game::logger->Log("ChatServer", "Routing packet from world"); + LOG("Routing packet from world"); break; } default: - Game::logger->Log("ChatServer", "Unknown World id: %i", int(packet->data[3])); + LOG("Unknown World id: %i", int(packet->data[3])); } } } diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index 689ffd77..dbbaeb9e 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -3,51 +3,56 @@ #include #include #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "ChatPacketHandler.h" #include "GeneralUtils.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "Database.h" #include "eConnectionType.h" #include "eChatInternalMessageType.h" +#include "ChatPackets.h" +#include "dConfig.h" -PlayerContainer::PlayerContainer() { +void PlayerContainer::Initialize() { + m_MaxNumberOfBestFriends = + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_best_friends")).value_or(m_MaxNumberOfBestFriends); + m_MaxNumberOfFriends = + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_friends")).value_or(m_MaxNumberOfFriends); } -PlayerContainer::~PlayerContainer() { - mPlayers.clear(); +TeamData::TeamData() { + lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1; } void PlayerContainer::InsertPlayer(Packet* packet) { CINSTREAM_SKIP_HEADER; - PlayerData* data = new PlayerData(); - inStream.Read(data->playerID); + LWOOBJID playerId; + if (!inStream.Read(playerId)) { + LOG("Failed to read player ID"); + return; + } + + auto& data = m_Players[playerId]; + data.playerID = playerId; uint32_t len; inStream.Read(len); for (int i = 0; i < len; i++) { char character; inStream.Read(character); - data->playerName += character; + data.playerName += character; } - inStream.Read(data->zoneID); - inStream.Read(data->muteExpire); - data->sysAddr = packet->systemAddress; + inStream.Read(data.zoneID); + inStream.Read(data.muteExpire); + inStream.Read(data.gmLevel); + data.sysAddr = packet->systemAddress; - mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName); + m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); - mPlayers.insert(std::make_pair(data->playerID, data)); - Game::logger->Log("PlayerContainer", "Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID()); + LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); - auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); - - insertLog->setInt(1, data->playerID); - insertLog->setInt(2, 0); - insertLog->setUInt64(3, time(nullptr)); - insertLog->setInt(4, data->zoneID.GetMapID()); - - insertLog->executeUpdate(); + Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID()); } void PlayerContainer::RemovePlayer(Packet* packet) { @@ -56,42 +61,36 @@ void PlayerContainer::RemovePlayer(Packet* packet) { inStream.Read(playerID); //Before they get kicked, we need to also send a message to their friends saying that they disconnected. - std::unique_ptr player(this->GetPlayerData(playerID)); + const auto& player = GetPlayerData(playerID); - if (player == nullptr) { + if (!player) { + LOG("Failed to find user: %llu", playerID); return; } - for (auto& fr : player->friends) { - auto fd = this->GetPlayerData(fr.friendID); - if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend); + for (const auto& fr : player.friends) { + const auto& fd = this->GetPlayerData(fr.friendID); + if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend); } auto* team = GetTeam(playerID); if (team != nullptr) { - const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str())); + const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 }); } } - Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID); - mPlayers.erase(playerID); + LOG("Removed user: %llu", playerID); + m_Players.erase(playerID); - auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); - - insertLog->setInt(1, playerID); - insertLog->setInt(2, 1); - insertLog->setUInt64(3, time(nullptr)); - insertLog->setInt(4, player->zoneID.GetMapID()); - - insertLog->executeUpdate(); + Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player.zoneID.GetMapID()); } void PlayerContainer::MuteUpdate(Packet* packet) { @@ -101,15 +100,15 @@ void PlayerContainer::MuteUpdate(Packet* packet) { time_t expire = 0; inStream.Read(expire); - auto* player = this->GetPlayerData(playerID); + auto& player = this->GetPlayerDataMutable(playerID); - if (player == nullptr) { - Game::logger->Log("PlayerContainer", "Failed to find user: %llu", playerID); + if (!player) { + LOG("Failed to find user: %llu", playerID); return; } - player->muteExpire = expire; + player.muteExpire = expire; BroadcastMuteUpdate(playerID, expire); } @@ -146,7 +145,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); bitStream.Write(player); bitStream.Write(time); @@ -185,7 +184,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector members) { TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) { auto* team = new TeamData(); - team->teamID = ++mTeamIDCounter; + team->teamID = ++m_TeamIDCounter; team->leaderID = leader; team->local = local; @@ -207,24 +206,32 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) { } void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { + if (team->memberIDs.size() >= 4) { + LOG("Tried to add player to team that already had 4 players"); + const auto& player = GetPlayerData(playerID); + if (!player) return; + ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!"); + return; + } + const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID); if (index != team->memberIDs.end()) return; team->memberIDs.push_back(playerID); - auto* leader = GetPlayerData(team->leaderID); - auto* member = GetPlayerData(playerID); + const auto& leader = GetPlayerData(team->leaderID); + const auto& member = GetPlayerData(playerID); - if (leader == nullptr || member == nullptr) return; + if (!leader || !member) return; - const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); - const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); + const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName); - ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName); + ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName); if (!team->local) { - ChatPacketHandler::SendTeamSetLeader(member, leader->playerID); + ChatPacketHandler::SendTeamSetLeader(member, leader.playerID); } else { ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); } @@ -232,16 +239,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { UpdateTeamsOnWorld(team, false); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); if (otherMember == member) continue; const auto otherMemberName = GetName(memberId); - ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); + ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0)); - if (otherMember != nullptr) { - ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID); + if (otherMember) { + ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID); } } } @@ -251,9 +258,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba if (index == team->memberIDs.end()) return; - auto* member = GetPlayerData(playerID); + const auto& member = GetPlayerData(playerID); - if (member != nullptr && !silent) { + if (member && !silent) { ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); } @@ -264,9 +271,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba continue; } - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName); } @@ -288,9 +295,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) { team->leaderID = newLeader; for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader); } @@ -302,14 +309,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) { if (index == mTeams.end()) return; for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; - const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName); + const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); - ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName); + ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName); } UpdateTeamsOnWorld(team, true); @@ -324,19 +331,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) { if (index == mTeams.end()) return; - auto* leader = GetPlayerData(team->leaderID); + const auto& leader = GetPlayerData(team->leaderID); - if (leader == nullptr) return; + if (!leader) return; - const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); + const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName); for (const auto memberId : team->memberIDs) { - auto* otherMember = GetPlayerData(memberId); + const auto& otherMember = GetPlayerData(memberId); - if (otherMember == nullptr) continue; + if (!otherMember) continue; if (!team->local) { - ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName); + ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName); } } @@ -345,14 +352,14 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); bitStream.Write(team->teamID); bitStream.Write(deleteTeam); if (!deleteTeam) { bitStream.Write(team->lootFlag); - bitStream.Write(static_cast(team->memberIDs.size())); + bitStream.Write(team->memberIDs.size()); for (const auto memberID : team->memberIDs) { bitStream.Write(memberID); } @@ -362,23 +369,42 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { } std::u16string PlayerContainer::GetName(LWOOBJID playerID) { - const auto& pair = mNames.find(playerID); + const auto iter = m_Names.find(playerID); - if (pair == mNames.end()) return u""; + if (iter == m_Names.end()) return u""; - return pair->second; + return iter->second; } LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { - for (const auto& pair : mNames) { - if (pair.second == playerName) { - return pair.first; + LWOOBJID toReturn = LWOOBJID_EMPTY; + + for (const auto& [id, name] : m_Names) { + if (name == playerName) { + toReturn = id; + break; } } - return LWOOBJID_EMPTY; + return toReturn; } -bool PlayerContainer::GetIsMuted(PlayerData* data) { - return data->muteExpire == 1 || data->muteExpire > time(NULL); +PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { + return m_Players[playerID]; +} + +PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) { + for (auto& [id, player] : m_Players) { + if (!player) continue; + if (player.playerName == playerName) return player; + } + return m_Players[LWOOBJID_EMPTY]; +} + +const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) { + return GetPlayerDataMutable(playerID); +} + +const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) { + return GetPlayerDataMutable(playerName); } diff --git a/dChatServer/PlayerContainer.h b/dChatServer/PlayerContainer.h index 0836eb59..3f2d783a 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -7,17 +7,49 @@ #include "dServer.h" #include -struct PlayerData { - LWOOBJID playerID; +enum class eGameMasterLevel : uint8_t; + +struct IgnoreData { + IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {} + inline bool operator==(const std::string& other) const noexcept { + return playerName == other; + } + + inline bool operator==(const LWOOBJID& other) const noexcept { + return playerId == other; + } + + LWOOBJID playerId = LWOOBJID_EMPTY; std::string playerName; - SystemAddress sysAddr; - LWOZONEID zoneID; - std::vector friends; - time_t muteExpire; +}; + +struct PlayerData { + operator bool() const noexcept { + return playerID != LWOOBJID_EMPTY; + } + + bool operator==(const PlayerData& other) const noexcept { + return playerID == other.playerID; + } + + bool GetIsMuted() const { + return muteExpire == 1 || muteExpire > time(NULL); + } + + SystemAddress sysAddr{}; + LWOZONEID zoneID{}; + LWOOBJID playerID = LWOOBJID_EMPTY; + time_t muteExpire = 0; uint8_t countOfBestFriends = 0; + std::string playerName; + std::vector friends; + std::vector ignoredPlayers; + eGameMasterLevel gmLevel = static_cast(0); // CIVILLIAN + bool isFTP = false; }; struct TeamData { + TeamData(); LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use LWOOBJID leaderID = LWOOBJID_EMPTY; std::vector memberIDs{}; @@ -28,31 +60,17 @@ struct TeamData { class PlayerContainer { public: - PlayerContainer(); - ~PlayerContainer(); - + void Initialize(); void InsertPlayer(Packet* packet); void RemovePlayer(Packet* packet); void MuteUpdate(Packet* packet); void CreateTeamServer(Packet* packet); void BroadcastMuteUpdate(LWOOBJID player, time_t time); - PlayerData* GetPlayerData(const LWOOBJID& playerID) { - auto it = mPlayers.find(playerID); - if (it != mPlayers.end()) return it->second; - return nullptr; - } - - PlayerData* GetPlayerData(const std::string& playerName) { - for (auto player : mPlayers) { - if (player.second) { - std::string pn = player.second->playerName.c_str(); - if (pn == playerName) return player.second; - } - } - - return nullptr; - } + const PlayerData& GetPlayerData(const LWOOBJID& playerID); + const PlayerData& GetPlayerData(const std::string& playerName); + PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); + PlayerData& GetPlayerDataMutable(const std::string& playerName); TeamData* CreateLocalTeam(std::vector members); TeamData* CreateTeam(LWOOBJID leader, bool local = false); @@ -65,14 +83,15 @@ public: void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam); std::u16string GetName(LWOOBJID playerID); LWOOBJID GetId(const std::u16string& playerName); - bool GetIsMuted(PlayerData* data); - - std::map& GetAllPlayerData() { return mPlayers; } + uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } + uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } private: - LWOOBJID mTeamIDCounter = 0; - std::map mPlayers; + LWOOBJID m_TeamIDCounter = 0; + std::map m_Players; std::vector mTeams; - std::unordered_map mNames; + std::unordered_map m_Names; + uint32_t m_MaxNumberOfBestFriends = 5; + uint32_t m_MaxNumberOfFriends = 50; }; diff --git a/dCommon/Amf3.h b/dCommon/Amf3.h index 4c649524..dbafba1f 100644 --- a/dCommon/Amf3.h +++ b/dCommon/Amf3.h @@ -2,7 +2,7 @@ #define __AMF3__H__ #include "dCommonVars.h" -#include "dLogger.h" +#include "Logger.h" #include "Game.h" #include @@ -31,54 +31,68 @@ enum class eAmf : uint8_t { class AMFBaseValue { public: - virtual eAmf GetValueType() { return eAmf::Undefined; }; - AMFBaseValue() {}; - virtual ~AMFBaseValue() {}; + [[nodiscard]] constexpr virtual eAmf GetValueType() const noexcept { return eAmf::Undefined; } + constexpr AMFBaseValue() noexcept = default; + constexpr virtual ~AMFBaseValue() noexcept = default; }; -template +// AMFValue template class instantiations +template class AMFValue : public AMFBaseValue { public: - AMFValue() {}; - AMFValue(ValueType value) { SetValue(value); }; - virtual ~AMFValue() override {}; + AMFValue() = default; + AMFValue(const ValueType value) { m_Data = value; } + virtual ~AMFValue() override = default; - eAmf GetValueType() override { return eAmf::Undefined; }; + [[nodiscard]] constexpr eAmf GetValueType() const noexcept override; + + [[nodiscard]] const ValueType& GetValue() const { return m_Data; } + void SetValue(const ValueType value) { m_Data = value; } - const ValueType& GetValue() { return data; }; - void SetValue(ValueType value) { data = value; }; protected: - ValueType data; + ValueType m_Data; }; +// Explicit template class instantiations +template class AMFValue; +template class AMFValue; +template class AMFValue; +template class AMFValue; +template class AMFValue; +template class AMFValue; + +// AMFValue template class member function instantiations +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::Null; } +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return m_Data ? eAmf::True : eAmf::False; } +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::Integer; } +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::Integer; } +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::String; } +template <> [[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::Double; } + +template +[[nodiscard]] constexpr eAmf AMFValue::GetValueType() const noexcept { return eAmf::Undefined; } + // As a string this is much easier to write and read from a BitStream. -template<> +template <> class AMFValue : public AMFBaseValue { public: - AMFValue() {}; - AMFValue(const char* value) { SetValue(std::string(value)); }; - virtual ~AMFValue() override {}; + AMFValue() = default; + AMFValue(const char* value) { m_Data = value; } + virtual ~AMFValue() override = default; - eAmf GetValueType() override { return eAmf::String; }; + [[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::String; } - const std::string& GetValue() { return data; }; - void SetValue(std::string value) { data = value; }; + [[nodiscard]] const std::string& GetValue() const { return m_Data; } + void SetValue(const std::string& value) { m_Data = value; } protected: - std::string data; + std::string m_Data; }; -typedef AMFValue AMFNullValue; -typedef AMFValue AMFBoolValue; -typedef AMFValue AMFIntValue; -typedef AMFValue AMFStringValue; -typedef AMFValue AMFDoubleValue; - -template<> inline eAmf AMFValue::GetValueType() { return eAmf::Null; }; -template<> inline eAmf AMFValue::GetValueType() { return this->data ? eAmf::True : eAmf::False; }; -template<> inline eAmf AMFValue::GetValueType() { return eAmf::Integer; }; -template<> inline eAmf AMFValue::GetValueType() { return eAmf::Integer; }; -template<> inline eAmf AMFValue::GetValueType() { return eAmf::String; }; -template<> inline eAmf AMFValue::GetValueType() { return eAmf::Double; }; +using AMFNullValue = AMFValue; +using AMFBoolValue = AMFValue; +using AMFIntValue = AMFValue; +using AMFStringValue = AMFValue; +using AMFDoubleValue = AMFValue; /** * The AMFArrayValue object holds 2 types of lists: @@ -89,12 +103,11 @@ template<> inline eAmf AMFValue::GetValueType() { return eAmf::Double; } * and are not to be deleted by a caller. */ class AMFArrayValue : public AMFBaseValue { - - typedef std::unordered_map AMFAssociative; - typedef std::vector AMFDense; + using AMFAssociative = std::unordered_map; + using AMFDense = std::vector; public: - eAmf GetValueType() override { return eAmf::Array; }; + [[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; } ~AMFArrayValue() override { for (auto valueToDelete : GetDense()) { @@ -109,17 +122,17 @@ public: valueToDelete.second = nullptr; } } - }; + } /** * Returns the Associative portion of the object */ - inline AMFAssociative& GetAssociative() { return this->associative; }; + [[nodiscard]] inline AMFAssociative& GetAssociative() noexcept { return this->associative; } /** * Returns the dense portion of the object */ - inline AMFDense& GetDense() { return this->dense; }; + [[nodiscard]] inline AMFDense& GetDense() noexcept { return this->dense; } /** * Inserts an AMFValue into the associative portion with the given key. @@ -135,8 +148,8 @@ public: * @return The inserted element if the type matched, * or nullptr if a key existed and was not the same type */ - template - std::pair*, bool> Insert(const std::string& key, ValueType value) { + template + [[maybe_unused]] std::pair*, bool> Insert(const std::string& key, const ValueType value) { auto element = associative.find(key); AMFValue* val = nullptr; bool found = true; @@ -148,10 +161,10 @@ public: found = false; } return std::make_pair(val, found); - }; + } // Associates an array with a string key - std::pair Insert(const std::string& key) { + [[maybe_unused]] std::pair Insert(const std::string& key) { auto element = associative.find(key); AMFArrayValue* val = nullptr; bool found = true; @@ -163,10 +176,10 @@ public: found = false; } return std::make_pair(val, found); - }; + } // Associates an array with an integer key - std::pair Insert(const uint32_t& index) { + [[maybe_unused]] std::pair Insert(const size_t index) { AMFArrayValue* val = nullptr; bool inserted = false; if (index >= dense.size()) { @@ -176,7 +189,7 @@ public: inserted = true; } return std::make_pair(dynamic_cast(dense.at(index)), inserted); - }; + } /** * @brief Inserts an AMFValue into the AMFArray key'd by index. @@ -188,8 +201,8 @@ public: * @return The inserted element, or nullptr if the type did not match * what was at the index. */ - template - std::pair*, bool> Insert(const uint32_t& index, ValueType value) { + template + [[maybe_unused]] std::pair*, bool> Insert(const size_t index, const ValueType value) { AMFValue* val = nullptr; bool inserted = false; if (index >= this->dense.size()) { @@ -199,7 +212,7 @@ public: inserted = true; } return std::make_pair(dynamic_cast*>(this->dense.at(index)), inserted); - }; + } /** * Inserts an AMFValue into the associative portion with the given key. @@ -210,7 +223,7 @@ public: * @param key The key to associate with the value * @param value The value to insert */ - void Insert(const std::string& key, AMFBaseValue* value) { + void Insert(const std::string& key, AMFBaseValue* const value) { auto element = associative.find(key); if (element != associative.end() && element->second) { delete element->second; @@ -218,7 +231,7 @@ public: } else { associative.insert(std::make_pair(key, value)); } - }; + } /** * Inserts an AMFValue into the associative portion with the given index. @@ -229,7 +242,7 @@ public: * @param key The key to associate with the value * @param value The value to insert */ - void Insert(const uint32_t index, AMFBaseValue* value) { + void Insert(const size_t index, AMFBaseValue* const value) { if (index < dense.size()) { AMFDense::iterator itr = dense.begin() + index; if (*itr) delete dense.at(index); @@ -237,7 +250,7 @@ public: dense.resize(index + 1); } dense.at(index) = value; - }; + } /** * Pushes an AMFValue into the back of the dense portion. @@ -249,10 +262,10 @@ public: * * @return The inserted pointer, or nullptr should the key already be in use. */ - template - inline AMFValue* Push(ValueType value) { + template + [[maybe_unused]] inline AMFValue* Push(const ValueType value) { return Insert(this->dense.size(), value).first; - }; + } /** * Removes the key from the associative portion @@ -261,7 +274,7 @@ public: * * @param key The key to remove from the associative portion */ - void Remove(const std::string& key, bool deleteValue = true) { + void Remove(const std::string& key, const bool deleteValue = true) { AMFAssociative::iterator it = this->associative.find(key); if (it != this->associative.end()) { if (deleteValue) delete it->second; @@ -272,7 +285,7 @@ public: /** * Pops the last element in the dense portion, deleting it in the process. */ - void Remove(const uint32_t index) { + void Remove(const size_t index) { if (!this->dense.empty() && index < this->dense.size()) { auto itr = this->dense.begin() + index; if (*itr) delete (*itr); @@ -284,29 +297,29 @@ public: if (!this->dense.empty()) Remove(this->dense.size() - 1); } - AMFArrayValue* GetArray(const std::string& key) { + [[nodiscard]] AMFArrayValue* GetArray(const std::string& key) { AMFAssociative::const_iterator it = this->associative.find(key); if (it != this->associative.end()) { return dynamic_cast(it->second); } return nullptr; - }; + } - AMFArrayValue* GetArray(const uint32_t index) { + [[nodiscard]] AMFArrayValue* GetArray(const size_t index) { return index >= this->dense.size() ? nullptr : dynamic_cast(this->dense.at(index)); - }; + } - inline AMFArrayValue* InsertArray(const std::string& key) { + [[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) { return static_cast(Insert(key).first); - }; + } - inline AMFArrayValue* InsertArray(const uint32_t index) { + [[maybe_unused]] inline AMFArrayValue* InsertArray(const size_t index) { return static_cast(Insert(index).first); - }; + } - inline AMFArrayValue* PushArray() { + [[maybe_unused]] inline AMFArrayValue* PushArray() { return static_cast(Insert(this->dense.size()).first); - }; + } /** * Gets an AMFValue by the key from the associative portion and converts it @@ -318,18 +331,18 @@ public: * @return The AMFValue */ template - AMFValue* Get(const std::string& key) const { + [[nodiscard]] AMFValue* Get(const std::string& key) const { AMFAssociative::const_iterator it = this->associative.find(key); return it != this->associative.end() ? dynamic_cast*>(it->second) : nullptr; - }; + } // Get from the array but dont cast it - AMFBaseValue* Get(const std::string& key) const { + [[nodiscard]] AMFBaseValue* Get(const std::string& key) const { AMFAssociative::const_iterator it = this->associative.find(key); return it != this->associative.end() ? it->second : nullptr; - }; + } /** * @brief Get an AMFValue object at a position in the dense portion. @@ -341,16 +354,17 @@ public: * @return The casted object, or nullptr. */ template - AMFValue* Get(uint32_t index) const { + [[nodiscard]] AMFValue* Get(const size_t index) const { return index < this->dense.size() ? dynamic_cast*>(this->dense.at(index)) : nullptr; - }; + } // Get from the dense but dont cast it - AMFBaseValue* Get(const uint32_t index) const { + [[nodiscard]] AMFBaseValue* Get(const size_t index) const { return index < this->dense.size() ? this->dense.at(index) : nullptr; - }; + } + private: /** * The associative portion. These values are key'd with strings to an AMFValue. diff --git a/dCommon/AmfSerialize.cpp b/dCommon/AmfSerialize.cpp index 79ba5e2d..3072b8e1 100644 --- a/dCommon/AmfSerialize.cpp +++ b/dCommon/AmfSerialize.cpp @@ -1,7 +1,7 @@ #include "AmfSerialize.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" // Writes an AMFValue pointer to a RakNet::BitStream template<> @@ -29,7 +29,7 @@ void RakNet::BitStream::Write(AMFBaseValue& value) { break; } default: { - Game::logger->Log("AmfSerialize", "Encountered unwritable AMFType %i!", type); + LOG("Encountered unwritable AMFType %i!", type); } case eAmf::Undefined: case eAmf::Null: @@ -54,17 +54,17 @@ void RakNet::BitStream::Write(AMFBaseValue& value) { * RakNet writes in the correct byte order - do not reverse this. */ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) { - unsigned char b4 = (unsigned char)v; + unsigned char b4 = static_cast(v); if (v < 0x00200000) { b4 = b4 & 0x7F; if (v > 0x7F) { unsigned char b3; v = v >> 7; - b3 = ((unsigned char)(v)) | 0x80; + b3 = static_cast(v) | 0x80; if (v > 0x7F) { unsigned char b2; v = v >> 7; - b2 = ((unsigned char)(v)) | 0x80; + b2 = static_cast(v) | 0x80; bs->Write(b2); } @@ -76,11 +76,11 @@ void WriteUInt29(RakNet::BitStream* bs, uint32_t v) { unsigned char b3; v = v >> 8; - b3 = ((unsigned char)(v)) | 0x80; + b3 = static_cast(v) | 0x80; v = v >> 7; - b2 = ((unsigned char)(v)) | 0x80; + b2 = static_cast(v) | 0x80; v = v >> 7; - b1 = ((unsigned char)(v)) | 0x80; + b1 = static_cast(v) | 0x80; bs->Write(b1); bs->Write(b2); @@ -105,8 +105,8 @@ void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) { * RakNet writes in the correct byte order - do not reverse this. */ void WriteAMFString(RakNet::BitStream* bs, const std::string& str) { - WriteFlagNumber(bs, (uint32_t)str.size()); - bs->Write(str.c_str(), (uint32_t)str.size()); + WriteFlagNumber(bs, static_cast(str.size())); + bs->Write(str.c_str(), static_cast(str.size())); } /** diff --git a/dCommon/AmfSerialize.h b/dCommon/AmfSerialize.h index 9a6f56f2..06abae74 100644 --- a/dCommon/AmfSerialize.h +++ b/dCommon/AmfSerialize.h @@ -4,7 +4,7 @@ #include "Amf3.h" // RakNet -#include +#include "BitStream.h" /*! \file AmfSerialize.h diff --git a/dCommon/BinaryIO.cpp b/dCommon/BinaryIO.cpp index 22e4de60..b64fb3ba 100644 --- a/dCommon/BinaryIO.cpp +++ b/dCommon/BinaryIO.cpp @@ -1,14 +1,6 @@ #include "BinaryIO.h" #include -void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) { - //BinaryWrite(outstream, uint32_t(stringToWrite.length())); - - for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) { - BinaryIO::BinaryWrite(outstream, stringToWrite[i]); - } -} - //For reading null-terminated strings std::string BinaryIO::ReadString(std::istream& instream) { std::string toReturn; @@ -23,36 +15,3 @@ std::string BinaryIO::ReadString(std::istream& instream) { return toReturn; } - -//For reading strings of a specific size -std::string BinaryIO::ReadString(std::istream& instream, size_t size) { - std::string toReturn; - char buffer; - - for (size_t i = 0; i < size; ++i) { - BinaryIO::BinaryRead(instream, buffer); - toReturn += buffer; - } - - return toReturn; -} - -std::string BinaryIO::ReadWString(std::istream& instream) { - size_t size; - BinaryRead(instream, size); - //toReturn.resize(size); - std::string test; - unsigned char buf; - - for (size_t i = 0; i < size; ++i) { - //instream.ignore(1); - BinaryRead(instream, buf); - test += buf; - } - - //printf("%s\n", test.c_str()); - - //instream.read((char*)&toReturn[0], size * 2); - //std::string str(toReturn.begin(), toReturn.end()); - return test; -} diff --git a/dCommon/BinaryIO.h b/dCommon/BinaryIO.h index a117ad0d..d1e728ca 100644 --- a/dCommon/BinaryIO.h +++ b/dCommon/BinaryIO.h @@ -1,8 +1,17 @@ #pragma once + +#ifndef __BINARYIO__H__ +#define __BINARYIO__H__ + #include #include +#include + +#include "Game.h" +#include "Logger.h" namespace BinaryIO { + template std::ostream& BinaryWrite(std::ostream& stream, const T& value) { return stream.write(reinterpret_cast(&value), sizeof(T)); @@ -15,13 +24,51 @@ namespace BinaryIO { return stream.read(reinterpret_cast(&value), sizeof(T)); } - void WriteString(const std::string& stringToWrite, std::ofstream& outstream); + enum class ReadType : int8_t { + WideString = 0, + String = 1, + }; + + template + inline void ReadString(std::istream& stream, std::u16string& value) { + static_assert(std::is_integral::value, "SizeType must be an integral type."); + + SizeType size; + BinaryRead(stream, size); + + if (!stream.good()) throw std::runtime_error("Failed to read from istream."); + value.resize(size); + stream.read(reinterpret_cast(value.data()), size * sizeof(uint16_t)); + } + + template + inline void ReadString(std::istream& stream, std::string& value, ReadType readType) { + static_assert(std::is_integral::value, "SizeType must be an integral type."); + + SizeType size; + BinaryRead(stream, size); + + if (!stream.good()) throw std::runtime_error("Failed to read from istream."); + value.resize(size); + if (readType == ReadType::WideString) { + uint16_t wideChar; + + // Faster to do this than to read a u16string and convert it to a string since we only go through allocator once + for (SizeType i = 0; i < size; ++i) { + BinaryRead(stream, wideChar); + value[i] = static_cast(wideChar); + } + } else { + stream.read(value.data(), size); + } + } + std::string ReadString(std::istream& instream); - std::string ReadString(std::istream& instream, size_t size); - std::string ReadWString(std::istream& instream); inline bool DoesFileExist(const std::string& name) { std::ifstream f(name.c_str()); return f.good(); } } + +#endif //!__BINARYIO__H__ diff --git a/dCommon/BrickByBrickFix.cpp b/dCommon/BrickByBrickFix.cpp index 15194bf9..b771c986 100644 --- a/dCommon/BrickByBrickFix.cpp +++ b/dCommon/BrickByBrickFix.cpp @@ -9,13 +9,12 @@ #include "Database.h" #include "Game.h" #include "ZCompression.h" -#include "dLogger.h" +#include "Logger.h" //! Forward declarations -std::unique_ptr GetModelsFromDatabase(); void WriteSd0Magic(char* input, uint32_t chunkSize); -bool CheckSd0Magic(sql::Blob* streamToCheck); +bool CheckSd0Magic(std::istream& streamToCheck); /** * @brief Truncates all models with broken data from the database. @@ -24,28 +23,24 @@ bool CheckSd0Magic(sql::Blob* streamToCheck); */ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { uint32_t modelsTruncated{}; - auto modelsToTruncate = GetModelsFromDatabase(); - bool previousCommitValue = Database::GetAutoCommit(); - Database::SetAutoCommit(false); - while (modelsToTruncate->next()) { - std::unique_ptr ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;")); - std::unique_ptr pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;")); + auto modelsToTruncate = Database::Get()->GetAllUgcModels(); + bool previousCommitValue = Database::Get()->GetAutoCommit(); + Database::Get()->SetAutoCommit(false); + for (auto& model : modelsToTruncate) { std::string completeUncompressedModel{}; uint32_t chunkCount{}; - uint64_t modelId = modelsToTruncate->getInt(1); - std::unique_ptr modelAsSd0(modelsToTruncate->getBlob(2)); // Check that header is sd0 by checking for the sd0 magic. - if (CheckSd0Magic(modelAsSd0.get())) { + if (CheckSd0Magic(model.lxfmlData)) { while (true) { uint32_t chunkSize{}; - modelAsSd0->read(reinterpret_cast(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream + model.lxfmlData.read(reinterpret_cast(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream // Check if good here since if at the end of an sd0 file, this will have eof flagged. - if (!modelAsSd0->good()) break; + if (!model.lxfmlData.good()) break; std::unique_ptr compressedChunk(new uint8_t[chunkSize]); for (uint32_t i = 0; i < chunkSize; i++) { - compressedChunk[i] = modelAsSd0->get(); + compressedChunk[i] = model.lxfmlData.get(); } // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size. @@ -56,17 +51,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { if (actualUncompressedSize != -1) { uint32_t previousSize = completeUncompressedModel.size(); - completeUncompressedModel.append((char*)uncompressedChunk.get()); + completeUncompressedModel.append(reinterpret_cast(uncompressedChunk.get())); completeUncompressedModel.resize(previousSize + actualUncompressedSize); } else { - Game::logger->Log("BrickByBrickFix", "Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err); + LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err); break; } chunkCount++; } std::unique_ptr document = std::make_unique(); if (!document) { - Game::logger->Log("BrickByBrickFix", "Failed to initialize tinyxml document. Aborting."); + LOG("Failed to initialize tinyxml document. Aborting."); return 0; } @@ -75,28 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { "", completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos ) { - Game::logger->Log("BrickByBrickFix", - "Brick-by-brick model %llu will be deleted!", modelId); - ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - ugcModelToDelete->execute(); - pcModelToDelete->execute(); + LOG("Brick-by-brick model %llu will be deleted!", model.id); + Database::Get()->DeleteUgcModelData(model.id); modelsTruncated++; } } } else { - Game::logger->Log("BrickByBrickFix", - "Brick-by-brick model %llu will be deleted!", modelId); - ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1)); - ugcModelToDelete->execute(); - pcModelToDelete->execute(); + LOG("Brick-by-brick model %llu will be deleted!", model.id); + Database::Get()->DeleteUgcModelData(model.id); modelsTruncated++; } } - Database::Commit(); - Database::SetAutoCommit(previousCommitValue); + Database::Get()->Commit(); + Database::Get()->SetAutoCommit(previousCommitValue); return modelsTruncated; } @@ -108,21 +95,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { */ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { uint32_t updatedModels = 0; - auto modelsToUpdate = GetModelsFromDatabase(); - auto previousAutoCommitState = Database::GetAutoCommit(); - Database::SetAutoCommit(false); - std::unique_ptr insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;")); - while (modelsToUpdate->next()) { - int64_t modelId = modelsToUpdate->getInt64(1); - std::unique_ptr oldLxfml(modelsToUpdate->getBlob(2)); + auto modelsToUpdate = Database::Get()->GetAllUgcModels(); + auto previousAutoCommitState = Database::Get()->GetAutoCommit(); + Database::Get()->SetAutoCommit(false); + for (auto& model : modelsToUpdate) { // Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib) // If it does, convert it to sd0. - if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) { - + if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) { // Get and save size of zlib compressed chunk. - oldLxfml->seekg(0, std::ios::end); - uint32_t oldLxfmlSize = static_cast(oldLxfml->tellg()); - oldLxfml->seekg(0); + model.lxfmlData.seekg(0, std::ios::end); + uint32_t oldLxfmlSize = static_cast(model.lxfmlData.tellg()); + model.lxfmlData.seekg(0); // Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size. uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9; @@ -130,36 +113,27 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize); for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) { - sd0ConvertedModel.get()[i] = oldLxfml->get(); + sd0ConvertedModel.get()[i] = model.lxfmlData.get(); } std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader); std::istringstream outputStringStream(outputString); - insertionStatement->setBlob(1, static_cast(&outputStringStream)); - insertionStatement->setInt64(2, modelId); try { - insertionStatement->executeUpdate(); - Game::logger->Log("BrickByBrickFix", "Updated model %i to sd0", modelId); + Database::Get()->UpdateUgcModelData(model.id, outputStringStream); + LOG("Updated model %i to sd0", model.id); updatedModels++; } catch (sql::SQLException exception) { - Game::logger->Log( - "BrickByBrickFix", - "Failed to update model %i. This model should be inspected manually to see why." - "The database error is %s", modelId, exception.what()); + LOG("Failed to update model %i. This model should be inspected manually to see why." + "The database error is %s", model.id, exception.what()); } } } - Database::Commit(); - Database::SetAutoCommit(previousAutoCommitState); + Database::Get()->Commit(); + Database::Get()->SetAutoCommit(previousAutoCommitState); return updatedModels; } -std::unique_ptr GetModelsFromDatabase() { - std::unique_ptr modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;")); - return std::unique_ptr(modelsRawDataQuery->executeQuery()); -} - /** * @brief Writes sd0 magic at the front of a char* * @@ -175,6 +149,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) { *reinterpret_cast(input + 5) = chunkSize; // Write the integer to the character array } -bool CheckSd0Magic(sql::Blob* streamToCheck) { - return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF; +bool CheckSd0Magic(std::istream& streamToCheck) { + return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF; } diff --git a/dCommon/CMakeLists.txt b/dCommon/CMakeLists.txt index 2517c048..c5fff63a 100644 --- a/dCommon/CMakeLists.txt +++ b/dCommon/CMakeLists.txt @@ -4,21 +4,26 @@ set(DCOMMON_SOURCES "BinaryIO.cpp" "dConfig.cpp" "Diagnostics.cpp" - "dLogger.cpp" + "Logger.cpp" + "Game.cpp" "GeneralUtils.cpp" "LDFFormat.cpp" - "MD5.cpp" "Metrics.cpp" "NiPoint3.cpp" "NiQuaternion.cpp" - "SHA512.cpp" - "Type.cpp" + "Demangler.cpp" "ZCompression.cpp" "BrickByBrickFix.cpp" "BinaryPathFinder.cpp" "FdbToSqlite.cpp" ) +# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible. +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185 +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties("FdbToSqlite.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow") +endif() + add_subdirectory(dClient) foreach(file ${DCOMMON_DCLIENT_SOURCES}) diff --git a/dCommon/Demangler.cpp b/dCommon/Demangler.cpp new file mode 100644 index 00000000..61232169 --- /dev/null +++ b/dCommon/Demangler.cpp @@ -0,0 +1,29 @@ +#include "Demangler.h" +#ifdef __GNUG__ +#include +#include +#include +#include + +std::string Demangler::Demangle(const char* name) { + // some arbitrary value to eliminate the compiler warning + // -4 is not a valid return value for __cxa_demangle so we'll use that. + int status = -4; + + // __cxa_demangle requires that we free the returned char* + std::unique_ptr res{ + abi::__cxa_demangle(name, NULL, NULL, &status), + std::free + }; + + return (status == 0) ? res.get() : ""; +} + +#else // __GNUG__ + +// does nothing if not g++ +std::string Demangler::Demangle(const char* name) { + return name; +} + +#endif // __GNUG__ diff --git a/dCommon/Demangler.h b/dCommon/Demangler.h new file mode 100644 index 00000000..84d66b40 --- /dev/null +++ b/dCommon/Demangler.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace Demangler { + // Given a char* containing a mangled name, return a std::string containing the demangled name. + // If the function fails for any reason, it returns an empty string. + std::string Demangle(const char* name); +} diff --git a/dCommon/Diagnostics.cpp b/dCommon/Diagnostics.cpp index 58d558de..46c17e43 100644 --- a/dCommon/Diagnostics.cpp +++ b/dCommon/Diagnostics.cpp @@ -1,6 +1,6 @@ #include "Diagnostics.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" // If we're on Win32, we'll include our minidump writer #ifdef _WIN32 @@ -9,7 +9,7 @@ #include #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void make_minidump(EXCEPTION_POINTERS* e) { auto hDbgHelp = LoadLibraryA("dbghelp"); @@ -28,7 +28,7 @@ void make_minidump(EXCEPTION_POINTERS* e) { "_%4d%02d%02d_%02d%02d%02d.dmp", t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); } - Game::logger->Log("Diagnostics", "Creating crash dump %s", name); + LOG("Creating crash dump %s", name); auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); if (hFile == INVALID_HANDLE_VALUE) return; @@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) { #include #include -#if defined(__include_backtrace__) +#if defined(INCLUDE_BACKTRACE) #include #include @@ -83,7 +83,7 @@ struct bt_ctx { static inline void Bt(struct backtrace_state* state) { std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; - Game::logger->Log("Diagnostics", "backtrace is enabled, crash dump located at %s", fileName.c_str()); + LOG("backtrace is enabled, crash dump located at %s", fileName.c_str()); FILE* file = fopen(fileName.c_str(), "w+"); if (file != nullptr) { backtrace_print(state, 2, file); @@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) { } #endif -#include "Type.h" +#include "Demangler.h" void GenerateDump() { std::string cmd = "sudo gcore " + std::to_string(getpid()); @@ -115,58 +115,66 @@ void GenerateDump() { } void CatchUnhandled(int sig) { -#ifndef __include_backtrace__ + std::exception_ptr eptr = std::current_exception(); + try { + if (eptr) std::rethrow_exception(eptr); + } catch(const std::exception& e) { + LOG("Caught exception: '%s'", e.what()); + } + +#ifndef INCLUDE_BACKTRACE std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; - Game::logger->Log("Diagnostics", "Encountered signal %i, creating crash dump %s", sig, fileName.c_str()); + LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str()); if (Diagnostics::GetProduceMemoryDump()) { GenerateDump(); } - - void* array[10]; + constexpr uint8_t MaxStackTrace = 32; + void* array[MaxStackTrace]; size_t size; // get void*'s for all entries on the stack - size = backtrace(array, 10); + size = backtrace(array, MaxStackTrace); -#if defined(__GNUG__) and defined(__dynamic) +# if defined(__GNUG__) // Loop through the returned addresses, and get the symbols to be demangled char** strings = backtrace_symbols(array, size); + FILE* file = fopen(fileName.c_str(), "w+"); + if (file != NULL) { + fprintf(file, "Error: signal %d:\n", sig); + } // Print the stack trace for (size_t i = 0; i < size; i++) { - // Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name + // Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' + // and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name std::string functionName = strings[i]; std::string::size_type start = functionName.find('('); std::string::size_type end = functionName.find('+'); if (start != std::string::npos && end != std::string::npos) { std::string demangled = functionName.substr(start + 1, end - start - 1); - demangled = demangle(functionName.c_str()); + demangled = Demangler::Demangle(demangled.c_str()); - if (demangled.empty()) { - Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str()); - } else { - Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str()); + // If the demangled string is not empty, then we can replace the mangled string with the demangled one + if (!demangled.empty()) { + demangled.push_back('('); + demangled += functionName.substr(end); + functionName = demangled; } - } else { - Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str()); + } + + LOG("[%02zu] %s", i, functionName.c_str()); + if (file != NULL) { + fprintf(file, "[%02zu] %s\n", i, functionName.c_str()); } } -#else +# else // defined(__GNUG__) backtrace_symbols_fd(array, size, STDOUT_FILENO); -#endif +# endif // defined(__GNUG__) - FILE* file = fopen(fileName.c_str(), "w+"); - if (file != NULL) { - // print out all the frames to stderr - fprintf(file, "Error: signal %d:\n", sig); - backtrace_symbols_fd(array, size, fileno(file)); - fclose(file); - } - -#else +#else // INCLUDE_BACKTRACE struct backtrace_state* state = backtrace_create_state( Diagnostics::GetProcessFileName().c_str(), @@ -177,7 +185,7 @@ void CatchUnhandled(int sig) { struct bt_ctx ctx = { state, 0 }; Bt(state); -#endif +#endif // INCLUDE_BACKTRACE exit(EXIT_FAILURE); } @@ -196,10 +204,10 @@ void MakeBacktrace() { sigact.sa_sigaction = CritErrHdlr; sigact.sa_flags = SA_RESTART | SA_SIGINFO; - if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 || - sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 || - sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 || - sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) { + if (sigaction(SIGSEGV, &sigact, nullptr) != 0 || + sigaction(SIGFPE, &sigact, nullptr) != 0 || + sigaction(SIGABRT, &sigact, nullptr) != 0 || + sigaction(SIGILL, &sigact, nullptr) != 0) { fprintf(stderr, "error setting signal handler for %d (%s)\n", SIGSEGV, strsignal(SIGSEGV)); diff --git a/dCommon/FdbToSqlite.cpp b/dCommon/FdbToSqlite.cpp index e05286a9..d8409cd5 100644 --- a/dCommon/FdbToSqlite.cpp +++ b/dCommon/FdbToSqlite.cpp @@ -9,7 +9,7 @@ #include "CDClientDatabase.h" #include "GeneralUtils.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "AssetManager.h" #include "eSqliteDataType.h" @@ -28,23 +28,21 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) { this->m_BinaryOutPath = binaryOutPath; } -bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) { +bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) { if (m_ConversionStarted) return false; - std::istream cdClientBuffer(&buffer); - this->m_ConversionStarted = true; try { CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite"); CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); - int32_t numberOfTables = ReadInt32(cdClientBuffer); - ReadTables(numberOfTables, cdClientBuffer); + int32_t numberOfTables = ReadInt32(buffer); + ReadTables(numberOfTables, buffer); CDClientDatabase::ExecuteQuery("COMMIT;"); } catch (CppSQLite3Exception& e) { - Game::logger->Log("FdbToSqlite", "Encountered error %s converting FDB to SQLite", e.errorMessage()); + LOG("Encountered error %s converting FDB to SQLite", e.errorMessage()); return false; } diff --git a/dCommon/FdbToSqlite.h b/dCommon/FdbToSqlite.h index 7aad2703..b0d20aee 100644 --- a/dCommon/FdbToSqlite.h +++ b/dCommon/FdbToSqlite.h @@ -7,7 +7,7 @@ #include #include -class AssetMemoryBuffer; +#include "AssetManager.h" enum class eSqliteDataType : int32_t; @@ -27,7 +27,7 @@ namespace FdbToSqlite { * * @return true if the database was converted properly, false otherwise. */ - bool ConvertDatabase(AssetMemoryBuffer& buffer); + bool ConvertDatabase(AssetStream& buffer); /** * @brief Reads a 32 bit int from the fdb file. diff --git a/dCommon/Game.cpp b/dCommon/Game.cpp new file mode 100644 index 00000000..b057478f --- /dev/null +++ b/dCommon/Game.cpp @@ -0,0 +1,7 @@ +#include "Game.h" + +namespace Game { + void OnSignal(int signal) { + lastSignal = signal; + } +} diff --git a/dCommon/Game.h b/dCommon/Game.h index 09ac6f6e..d8113497 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -1,9 +1,11 @@ #pragma once +#include #include +#include class dServer; -class dLogger; +class Logger; class InstanceManager; class dChatFilter; class dConfig; @@ -12,9 +14,11 @@ class AssetManager; struct SystemAddress; class EntityManager; class dZoneManager; +class PlayerContainer; namespace Game { - extern dLogger* logger; + using signal_t = volatile std::sig_atomic_t; + extern Logger* logger; extern dServer* server; extern InstanceManager* im; extern dChatFilter* chatFilter; @@ -23,7 +27,14 @@ namespace Game { extern RakPeerInterface* chatServer; extern AssetManager* assetManager; extern SystemAddress chatSysAddr; - extern bool shouldShutdown; + extern signal_t lastSignal; extern EntityManager* entityManager; extern dZoneManager* zoneManager; + extern PlayerContainer playerContainer; + extern std::string projectVersion; + + inline bool ShouldShutdown() { + return lastSignal != 0; + } + void OnSignal(int signal); } diff --git a/dCommon/GeneralUtils.cpp b/dCommon/GeneralUtils.cpp index 99ca687a..78cf4f48 100644 --- a/dCommon/GeneralUtils.cpp +++ b/dCommon/GeneralUtils.cpp @@ -53,7 +53,7 @@ bool _IsSuffixChar(uint8_t c) { bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) { size_t rem = slice.length(); if (slice.empty()) return false; - const uint8_t* bytes = (const uint8_t*)&slice.front(); + const uint8_t* bytes = reinterpret_cast(&slice.front()); if (rem > 0) { uint8_t first = bytes[0]; if (first < 0x80) { // 1 byte character @@ -319,7 +319,3 @@ std::vector GeneralUtils::GetSqlFileNamesFromFolder(const std::stri return sortedFiles; } - -bool GeneralUtils::TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst) { - return TryParse(x.c_str(), dst.x) && TryParse(y.c_str(), dst.y) && TryParse(z.c_str(), dst.z); -} diff --git a/dCommon/GeneralUtils.h b/dCommon/GeneralUtils.h index e9e20ba0..15659912 100644 --- a/dCommon/GeneralUtils.h +++ b/dCommon/GeneralUtils.h @@ -1,19 +1,22 @@ #pragma once // C++ -#include +#include +#include #include -#include +#include #include -#include +#include +#include #include #include #include -#include +#include "BitStream.h" #include "NiPoint3.h" +#include "dPlatforms.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" enum eInventoryType : uint32_t; enum class eObjectBits : size_t; @@ -123,80 +126,125 @@ namespace GeneralUtils { std::vector GetSqlFileNamesFromFolder(const std::string& folder); + // Concept constraining to enum types template - T Parse(const char* value); - - template <> - inline int32_t Parse(const char* value) { - return std::stoi(value); - } - - template <> - inline int64_t Parse(const char* value) { - return std::stoll(value); - } - - template <> - inline float Parse(const char* value) { - return std::stof(value); - } - - template <> - inline double Parse(const char* value) { - return std::stod(value); - } - - template <> - inline uint32_t Parse(const char* value) { - return std::stoul(value); - } - - template <> - inline uint64_t Parse(const char* value) { - return std::stoull(value); - } - - template <> - inline eInventoryType Parse(const char* value) { - return static_cast(std::stoul(value)); - } - - template <> - inline eReplicaComponentType Parse(const char* value) { - return static_cast(std::stoul(value)); - } + concept Enum = std::is_enum_v; + // Concept constraining to numeric types template - bool TryParse(const char* value, T& dst) { - try { - dst = Parse(value); + concept Numeric = std::integral || Enum || std::floating_point; - return true; - } catch (...) { - return false; + // Concept trickery to enable parsing underlying numeric types + template + struct numeric_parse { using type = T; }; + + // If an enum, present an alias to its underlying type for parsing + template requires Enum + struct numeric_parse { using type = std::underlying_type_t; }; + + // If a boolean, present an alias to an intermediate integral type for parsing + template requires std::same_as + struct numeric_parse { using type = uint32_t; }; + + // Shorthand type alias + template + using numeric_parse_t = numeric_parse::type; + + /** + * For numeric values: Parses a string_view and returns an optional variable depending on the result. + * @param str The string_view to be evaluated + * @returns An std::optional containing the desired value if it is equivalent to the string + */ + template + [[nodiscard]] std::optional TryParse(const std::string_view str) { + numeric_parse_t result; + + const char* const strEnd = str.data() + str.size(); + const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result); + const bool isParsed = parseEnd == strEnd && ec == std::errc{}; + + return isParsed ? static_cast(result) : std::optional{}; + } + +#ifdef DARKFLAME_PLATFORM_MACOS + + // Anonymous namespace containing MacOS floating-point parse function specializations + namespace { + template + [[nodiscard]] T Parse(const std::string_view str, size_t* parseNum); + + template <> + [[nodiscard]] float Parse(const std::string_view str, size_t* parseNum) { + return std::stof(std::string{ str }, parseNum); + } + + template <> + [[nodiscard]] double Parse(const std::string_view str, size_t* parseNum) { + return std::stod(std::string{ str }, parseNum); + } + + template <> + [[nodiscard]] long double Parse(const std::string_view str, size_t* parseNum) { + return std::stold(std::string{ str }, parseNum); } } + /** + * For floating-point values: Parses a string_view and returns an optional variable depending on the result. + * Note that this function overload is only included for MacOS, as from_chars will fulfill its purpose otherwise. + * @param str The string_view to be evaluated + * @returns An std::optional containing the desired value if it is equivalent to the string + */ + template + [[nodiscard]] std::optional TryParse(const std::string_view str) noexcept try { + size_t parseNum; + const T result = Parse(str, &parseNum); + const bool isParsed = str.length() == parseNum; + + return isParsed ? result : std::optional{}; + } catch (...) { + return std::nullopt; + } + +#endif + + /** + * The TryParse overload for handling NiPoint3 by passing 3 seperate string references + * @param strX The string representing the X coordinate + * @param strY The string representing the Y coordinate + * @param strZ The string representing the Z coordinate + * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters + */ template - T Parse(const std::string& value) { - return Parse(value.c_str()); + [[nodiscard]] std::optional TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) { + const auto x = TryParse(strX); + if (!x) return std::nullopt; + + const auto y = TryParse(strY); + if (!y) return std::nullopt; + + const auto z = TryParse(strZ); + return z ? std::make_optional(x.value(), y.value(), z.value()) : std::nullopt; + } + + /** + * 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 + [[nodiscard]] std::optional TryParse(const std::vector& str) { + return (str.size() == 3) ? TryParse(str[0], str[1], str[2]) : std::nullopt; } template - bool TryParse(const std::string& value, T& dst) { - return TryParse(value.c_str(), dst); - } - - bool TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst); - - template std::u16string to_u16string(T value) { return GeneralUtils::ASCIIToUTF16(std::to_string(value)); } // From boost::hash_combine template - void hash_combine(std::size_t& s, const T& v) { + constexpr void hash_combine(std::size_t& s, const T& v) { std::hash h; s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2); } @@ -224,18 +272,28 @@ namespace GeneralUtils { return T(); } -// 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 + /** + * Casts the value of an enum entry to its underlying type + * @param entry Enum entry to cast + * @returns The enum entry's value in its underlying type + */ + template + constexpr typename std::underlying_type_t CastUnderlyingType(const eType entry) noexcept { + return static_cast>(entry); + } + + // 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 template inline T GenerateRandomNumber() { // Make sure it is a numeric type static_assert(std::is_arithmetic::value, "Not an arithmetic type"); - + return GenerateRandomNumber(std::numeric_limits::min(), std::numeric_limits::max()); } } diff --git a/dCommon/LDFFormat.cpp b/dCommon/LDFFormat.cpp index cb921842..da28ae6e 100644 --- a/dCommon/LDFFormat.cpp +++ b/dCommon/LDFFormat.cpp @@ -4,7 +4,7 @@ #include "GeneralUtils.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" // C++ #include @@ -48,7 +48,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { try { type = static_cast(strtol(ldfTypeAndValue.first.data(), &storage, 10)); } catch (std::exception) { - Game::logger->Log("LDFFormat", "Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data()); + LOG("Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data()); return nullptr; } @@ -61,35 +61,33 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { } case LDF_TYPE_S32: { - try { - int32_t data = static_cast(strtoul(ldfTypeAndValue.second.data(), &storage, 10)); - returnValue = new LDFData(key, data); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto data = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!data) { + LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + returnValue = new LDFData(key, data.value()); + break; } case LDF_TYPE_FLOAT: { - try { - float data = strtof(ldfTypeAndValue.second.data(), &storage); - returnValue = new LDFData(key, data); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto data = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!data) { + LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + returnValue = new LDFData(key, data.value()); break; } case LDF_TYPE_DOUBLE: { - try { - double data = strtod(ldfTypeAndValue.second.data(), &storage); - returnValue = new LDFData(key, data); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto data = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!data) { + LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + returnValue = new LDFData(key, data.value()); break; } @@ -102,12 +100,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { } else if (ldfTypeAndValue.second == "false") { data = 0; } else { - try { - data = static_cast(strtoul(ldfTypeAndValue.second.data(), &storage, 10)); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto dataOptional = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!dataOptional) { + LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + data = dataOptional.value(); } returnValue = new LDFData(key, data); @@ -122,12 +120,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { } else if (ldfTypeAndValue.second == "false") { data = false; } else { - try { - data = static_cast(strtol(ldfTypeAndValue.second.data(), &storage, 10)); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto dataOptional = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!dataOptional) { + LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + data = dataOptional.value(); } returnValue = new LDFData(key, data); @@ -135,24 +133,22 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { } case LDF_TYPE_U64: { - try { - uint64_t data = static_cast(strtoull(ldfTypeAndValue.second.data(), &storage, 10)); - returnValue = new LDFData(key, data); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto data = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!data) { + LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + returnValue = new LDFData(key, data.value()); break; } case LDF_TYPE_OBJID: { - try { - LWOOBJID data = static_cast(strtoll(ldfTypeAndValue.second.data(), &storage, 10)); - returnValue = new LDFData(key, data); - } catch (std::exception) { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + const auto data = GeneralUtils::TryParse(ldfTypeAndValue.second); + if (!data) { + LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); return nullptr; } + returnValue = new LDFData(key, data.value()); break; } @@ -163,12 +159,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) { } case LDF_TYPE_UNKNOWN: { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); + LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); break; } default: { - Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data()); + LOG("Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data()); break; } } diff --git a/dCommon/LDFFormat.h b/dCommon/LDFFormat.h index 0921d04c..3a4f2ea7 100644 --- a/dCommon/LDFFormat.h +++ b/dCommon/LDFFormat.h @@ -63,15 +63,15 @@ private: //! Writes the key to the packet void WriteKey(RakNet::BitStream* packet) { - packet->Write(static_cast(this->key.length() * sizeof(uint16_t))); + packet->Write(this->key.length() * sizeof(uint16_t)); for (uint32_t i = 0; i < this->key.length(); ++i) { - packet->Write(static_cast(this->key[i])); + packet->Write(this->key[i]); } } //! Writes the value to the packet void WriteValue(RakNet::BitStream* packet) { - packet->Write(static_cast(this->GetValueType())); + packet->Write(this->GetValueType()); packet->Write(this->value); } @@ -162,7 +162,7 @@ public: return new LDFData(key, value); } - inline static T Default = {}; + inline static const T Default = {}; }; // LDF Types @@ -179,30 +179,30 @@ template<> inline eLDFType LDFData::GetValueType(void) { return LDF // The specialized version for std::u16string (UTF-16) template<> inline void LDFData::WriteValue(RakNet::BitStream* packet) { - packet->Write(static_cast(this->GetValueType())); + packet->Write(this->GetValueType()); - packet->Write(static_cast(this->value.length())); + packet->Write(this->value.length()); for (uint32_t i = 0; i < this->value.length(); ++i) { - packet->Write(static_cast(this->value[i])); + packet->Write(this->value[i]); } } // The specialized version for bool template<> inline void LDFData::WriteValue(RakNet::BitStream* packet) { - packet->Write(static_cast(this->GetValueType())); + packet->Write(this->GetValueType()); - packet->Write(static_cast(this->value)); + packet->Write(this->value); } // The specialized version for std::string (UTF-8) template<> inline void LDFData::WriteValue(RakNet::BitStream* packet) { - packet->Write(static_cast(this->GetValueType())); + packet->Write(this->GetValueType()); - packet->Write(static_cast(this->value.length())); + packet->Write(this->value.length()); for (uint32_t i = 0; i < this->value.length(); ++i) { - packet->Write(static_cast(this->value[i])); + packet->Write(this->value[i]); } } diff --git a/dCommon/Logger.cpp b/dCommon/Logger.cpp new file mode 100644 index 00000000..1888f1eb --- /dev/null +++ b/dCommon/Logger.cpp @@ -0,0 +1,98 @@ +#include "Logger.h" + +#include +#include +#include +#include + +Writer::~Writer() { + // Flush before we close + Flush(); + // Dont try to close stdcout... + if (!m_Outfile || m_IsConsoleWriter) return; + + fclose(m_Outfile); + m_Outfile = NULL; +} + +void Writer::Log(const char* time, const char* message) { + if (!m_Outfile || !m_Enabled) return; + + fputs(time, m_Outfile); + fputs(message, m_Outfile); +} + +void Writer::Flush() { + if (!m_Outfile) return; + fflush(m_Outfile); +} + +FileWriter::FileWriter(const char* outpath) { + m_Outfile = fopen(outpath, "wt"); + if (!m_Outfile) printf("Couldn't open %s for writing!\n", outpath); + m_Outpath = outpath; + m_IsConsoleWriter = false; +} + +ConsoleWriter::ConsoleWriter(bool enabled) { + m_Enabled = enabled; + m_Outfile = stdout; + m_IsConsoleWriter = true; +} + +Logger::Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements) { + m_logDebugStatements = logDebugStatements; + std::filesystem::path outpathPath(outpath); + if (!std::filesystem::exists(outpathPath.parent_path())) std::filesystem::create_directories(outpathPath.parent_path()); + m_Writers.push_back(std::make_unique(outpath)); + m_Writers.push_back(std::make_unique(logToConsole)); +} + +void Logger::vLog(const char* format, va_list args) { + time_t t = time(NULL); + struct tm* time = localtime(&t); + char timeStr[70]; + strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); + char message[2048]; + vsnprintf(message, 2048, format, args); + for (const auto& writer : m_Writers) { + writer->Log(timeStr, message); + } +} + +void Logger::Log(const char* className, const char* format, ...) { + va_list args; + std::string log = std::string(className) + "] " + std::string(format) + "\n"; + va_start(args, format); + vLog(log.c_str(), args); + va_end(args); +} + +void Logger::LogDebug(const char* className, const char* format, ...) { + if (!m_logDebugStatements) return; + va_list args; + std::string log = std::string(className) + "] " + std::string(format) + "\n"; + va_start(args, format); + vLog(log.c_str(), args); + va_end(args); +} + +void Logger::Flush() { + for (const auto& writer : m_Writers) { + writer->Flush(); + } +} + +void Logger::SetLogToConsole(bool logToConsole) { + for (const auto& writer : m_Writers) { + if (writer->IsConsoleWriter()) writer->SetEnabled(logToConsole); + } +} + +bool Logger::GetLogToConsole() const { + bool toReturn = false; + for (const auto& writer : m_Writers) { + if (writer->IsConsoleWriter()) toReturn |= writer->GetEnabled(); + } + return toReturn; +} diff --git a/dCommon/Logger.h b/dCommon/Logger.h new file mode 100644 index 00000000..5754d9ac --- /dev/null +++ b/dCommon/Logger.h @@ -0,0 +1,89 @@ +#pragma once + +#include +#include +#include + +#define STRINGIFY_IMPL(x) #x + +#define STRINGIFY(x) STRINGIFY_IMPL(x) + +#define GET_FILE_NAME(x, y) GetFileNameFromAbsolutePath(__FILE__ x y) + +#define FILENAME_AND_LINE GET_FILE_NAME(":", STRINGIFY(__LINE__)) + +// Calculate the filename at compile time from the path. +// We just do this by scanning the path for the last '/' or '\' character and returning the string after it. +constexpr const char* GetFileNameFromAbsolutePath(const char* path) { + const char* file = path; + while (*path) { + char nextChar = *path++; + if (nextChar == '/' || nextChar == '\\') { + file = path; + } + } + return file; +} + +// These have to have a constexpr variable to store the filename_and_line result in a local variable otherwise +// they will not be valid constexpr and will be evaluated at runtime instead of compile time! +// The full string is still stored in the binary, however the offset of the filename in the absolute paths +// is used in the instruction instead of the start of the absolute path. +#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0) +#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0) + +// Writer class for writing data to files. +class Writer { +public: + Writer(bool enabled = true) : m_Enabled(enabled) {}; + virtual ~Writer(); + + virtual void Log(const char* time, const char* message); + virtual void Flush(); + + void SetEnabled(bool disabled) { m_Enabled = disabled; } + bool GetEnabled() const { return m_Enabled; } + + bool IsConsoleWriter() { return m_IsConsoleWriter; } +public: + bool m_Enabled = true; + bool m_IsConsoleWriter = false; + FILE* m_Outfile; +}; + +// FileWriter class for writing data to a file on a disk. +class FileWriter : public Writer { +public: + FileWriter(const char* outpath); + FileWriter(const std::string& outpath) : FileWriter(outpath.c_str()) {}; +private: + std::string m_Outpath; +}; + +// ConsoleWriter class for writing data to the console. +class ConsoleWriter : public Writer { +public: + ConsoleWriter(bool enabled); +}; + +class Logger { +public: + Logger() = delete; + Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements); + + void Log(const char* filenameAndLine, const char* format, ...); + void LogDebug(const char* filenameAndLine, const char* format, ...); + + void Flush(); + + bool GetLogToConsole() const; + void SetLogToConsole(bool logToConsole); + + void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; } + +private: + void vLog(const char* format, va_list args); + + bool m_logDebugStatements; + std::vector> m_Writers; +}; diff --git a/dCommon/Metrics.cpp b/dCommon/Metrics.cpp index b97b5435..5232cf78 100644 --- a/dCommon/Metrics.cpp +++ b/dCommon/Metrics.cpp @@ -107,7 +107,7 @@ void Metrics::EndMeasurement(MetricVariable variable) { } float Metrics::ToMiliseconds(int64_t nanoseconds) { - return (float)nanoseconds / 1e6; + return static_cast(nanoseconds) / 1e6; } std::string Metrics::MetricVariableToString(MetricVariable variable) { @@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() { /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); - return (size_t)info.PeakWorkingSetSize; + return static_cast(info.PeakWorkingSetSize); #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) /* AIX and Solaris ------------------------------------------ */ struct psinfo psinfo; int fd = -1; if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) - return (size_t)0L; /* Can't open? */ + return static_cast(0L); /* Can't open? */ if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { close(fd); - return (size_t)0L; /* Can't read? */ + return static_cast(0L); /* Can't read? */ } close(fd); - return (size_t)(psinfo.pr_rssize * 1024L); + return static_cast(psinfo.pr_rssize * 1024L); #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) /* BSD, Linux, and OSX -------------------------------------- */ struct rusage rusage; getrusage(RUSAGE_SELF, &rusage); #if defined(__APPLE__) && defined(__MACH__) - return (size_t)rusage.ru_maxrss; + return static_cast(rusage.ru_maxrss); #else - return (size_t)(rusage.ru_maxrss * 1024L); + return static_cast(rusage.ru_maxrss * 1024L); #endif #else /* Unknown OS ----------------------------------------------- */ - return (size_t)0L; /* Unsupported. */ + return static_cast(0L); /* Unsupported. */ #endif } @@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() { /* Windows -------------------------------------------------- */ PROCESS_MEMORY_COUNTERS info; GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); - return (size_t)info.WorkingSetSize; + return static_cast(info.WorkingSetSize); #elif defined(__APPLE__) && defined(__MACH__) /* OSX ------------------------------------------------------ */ struct mach_task_basic_info info; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, - (task_info_t)&info, &infoCount) != KERN_SUCCESS) - return (size_t)0L; /* Can't access? */ - return (size_t)info.resident_size; + reinterpret_cast(&info), &infoCount) != KERN_SUCCESS) + return static_cast(0L); /* Can't access? */ + return static_cast(info.resident_size); #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) /* Linux ---------------------------------------------------- */ long rss = 0L; FILE* fp = NULL; if ((fp = fopen("/proc/self/statm", "r")) == NULL) - return (size_t)0L; /* Can't open? */ + return static_cast(0L); /* Can't open? */ if (fscanf(fp, "%*s%ld", &rss) != 1) { fclose(fp); - return (size_t)0L; /* Can't read? */ + return static_cast(0L); /* Can't read? */ } fclose(fp); - return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); + return static_cast(rss) * static_cast(sysconf(_SC_PAGESIZE)); #else /* AIX, BSD, Solaris, and Unknown OS ------------------------ */ - return (size_t)0L; /* Unsupported. */ + return static_cast(0L); /* Unsupported. */ #endif } diff --git a/dCommon/NiPoint3.cpp b/dCommon/NiPoint3.cpp index 20780815..b8546fdd 100644 --- a/dCommon/NiPoint3.cpp +++ b/dCommon/NiPoint3.cpp @@ -1,203 +1,24 @@ #include "NiPoint3.h" -#include "NiQuaternion.h" // C++ #include -// Static Variables -const NiPoint3 NiPoint3::ZERO(0.0f, 0.0f, 0.0f); -const NiPoint3 NiPoint3::UNIT_X(1.0f, 0.0f, 0.0f); -const NiPoint3 NiPoint3::UNIT_Y(0.0f, 1.0f, 0.0f); -const NiPoint3 NiPoint3::UNIT_Z(0.0f, 0.0f, 1.0f); -const NiPoint3 NiPoint3::UNIT_ALL(1.0f, 1.0f, 1.0f); - -//! Initializer -NiPoint3::NiPoint3(void) { - this->x = 0; - this->y = 0; - this->z = 0; -} - -//! Initializer -NiPoint3::NiPoint3(float x, float y, float z) { - this->x = x; - this->y = y; - this->z = z; -} - -//! Copy Constructor -NiPoint3::NiPoint3(const NiPoint3& point) { - this->x = point.x; - this->y = point.y; - this->z = point.z; -} - -//! Destructor -NiPoint3::~NiPoint3(void) {} - -// MARK: Getters / Setters - -//! Gets the X coordinate -float NiPoint3::GetX(void) const { - return this->x; -} - -//! Sets the X coordinate -void NiPoint3::SetX(float x) { - this->x = x; -} - -//! Gets the Y coordinate -float NiPoint3::GetY(void) const { - return this->y; -} - -//! Sets the Y coordinate -void NiPoint3::SetY(float y) { - this->y = y; -} - -//! Gets the Z coordinate -float NiPoint3::GetZ(void) const { - return this->z; -} - -//! Sets the Z coordinate -void NiPoint3::SetZ(float z) { - this->z = z; -} - -// MARK: Functions +// MARK: Member Functions //! Gets the length of the vector -float NiPoint3::Length(void) const { - return sqrt(x * x + y * y + z * z); -} - -//! Gets the squared length of a vector -float NiPoint3::SquaredLength(void) const { - return (x * x + y * y + z * z); -} - -//! Returns the dot product of the vector dotted with another vector -float NiPoint3::DotProduct(const Vector3& vec) const { - return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z)); -} - -//! Returns the cross product of the vector crossed with another vector -Vector3 NiPoint3::CrossProduct(const Vector3& vec) const { - return Vector3(((this->y * vec.z) - (this->z * vec.y)), - ((this->z * vec.x) - (this->x * vec.z)), - ((this->x * vec.y) - (this->y * vec.x))); +float NiPoint3::Length() const { + return std::sqrt(x * x + y * y + z * z); } //! Unitize the vector -NiPoint3 NiPoint3::Unitize(void) const { +NiPoint3 NiPoint3::Unitize() const { float length = this->Length(); - return length != 0 ? *this / length : NiPoint3::ZERO; + return length != 0 ? *this / length : NiPoint3Constant::ZERO; } - -// MARK: Operators - -//! Operator to check for equality -bool NiPoint3::operator==(const NiPoint3& point) const { - return point.x == this->x && point.y == this->y && point.z == this->z; -} - -//! Operator to check for inequality -bool NiPoint3::operator!=(const NiPoint3& point) const { - return !(*this == point); -} - -//! Operator for subscripting -float& NiPoint3::operator[](int i) { - float* base = &x; - return (float&)base[i]; -} - -//! Operator for subscripting -const float& NiPoint3::operator[](int i) const { - const float* base = &x; - return (float&)base[i]; -} - -//! Operator for addition of vectors -NiPoint3 NiPoint3::operator+(const NiPoint3& point) const { - return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z); -} - -//! Operator for addition of vectors -NiPoint3& NiPoint3::operator+=(const NiPoint3& point) { - this->x += point.x; - this->y += point.y; - this->z += point.z; - return *this; -} - -//! Operator for subtraction of vectors -NiPoint3 NiPoint3::operator-(const NiPoint3& point) const { - return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z); -} - -//! Operator for addition of a scalar on all vector components -NiPoint3 NiPoint3::operator+(float fScalar) const { - return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar); -} - -//! Operator for subtraction of a scalar on all vector components -NiPoint3 NiPoint3::operator-(float fScalar) const { - return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar); -} - -//! Operator for scalar multiplication of a vector -NiPoint3 NiPoint3::operator*(float fScalar) const { - return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar); -} - -//! Operator for scalar division of a vector -NiPoint3 NiPoint3::operator/(float fScalar) const { - float retX = this->x != 0 ? this->x / fScalar : 0; - float retY = this->y != 0 ? this->y / fScalar : 0; - float retZ = this->z != 0 ? this->z / fScalar : 0; - return NiPoint3(retX, retY, retZ); -} - - // MARK: Helper Functions -//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box -bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) { - if (this->x < minPoint.x) return false; - if (this->x > maxPoint.x) return false; - if (this->y < minPoint.y) return false; - if (this->y > maxPoint.y) return false; - - return (this->z < maxPoint.z&& this->z > minPoint.z); -} - -//! Checks to see if the point (or vector) is within a sphere -bool NiPoint3::IsWithinSpehere(const NiPoint3& sphereCenter, float radius) { - Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ()); - return (diffVec.SquaredLength() <= (radius * radius)); -} - -NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) { - if (a == b) return a; - - const auto pa = p - a; - const auto ab = b - a; - - const auto t = pa.DotProduct(ab) / ab.SquaredLength(); - - if (t <= 0.0f) return a; - - if (t >= 1.0f) return b; - - return a + ab * t; -} - float NiPoint3::Angle(const NiPoint3& a, const NiPoint3& b) { const auto dot = a.DotProduct(b); const auto lenA = a.SquaredLength(); @@ -213,47 +34,24 @@ float NiPoint3::Distance(const NiPoint3& a, const NiPoint3& b) { return std::sqrt(dx * dx + dy * dy + dz * dz); } -float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) { - const auto dx = a.x - b.x; - const auto dy = a.y - b.y; - const auto dz = a.z - b.z; - - return dx * dx + dy * dy + dz * dz; -} - -NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta) { +NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta) { float dx = target.x - current.x; float dy = target.y - current.y; float dz = target.z - current.z; - float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz); - if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta) + + float lengthSquared = static_cast( + static_cast(dx) * static_cast(dx) + + static_cast(dy) * static_cast(dy) + + static_cast(dz) * static_cast(dz) + ); + + if (static_cast(lengthSquared) == 0.0 + || static_cast(maxDistanceDelta) >= 0.0 + && static_cast(lengthSquared) + <= static_cast(maxDistanceDelta) * static_cast(maxDistanceDelta)) { return target; - float length = (float)std::sqrt((double)lengthSquared); + } + + float length = std::sqrt(lengthSquared); return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta); } - -//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible. -NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) { - Vector3 vector; - float num12 = rotation.x + rotation.x; - float num2 = rotation.y + rotation.y; - float num = rotation.z + rotation.z; - float num11 = rotation.w * num12; - float num10 = rotation.w * num2; - float num9 = rotation.w * num; - float num8 = rotation.x * num12; - float num7 = rotation.x * num2; - float num6 = rotation.x * num; - float num5 = rotation.y * num2; - float num4 = rotation.y * num; - float num3 = rotation.z * num; - - NiPoint3 value = *this; - float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10)); - float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11)); - float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5)); - vector.x = num15; - vector.y = num14; - vector.z = num13; - return vector; -} diff --git a/dCommon/NiPoint3.h b/dCommon/NiPoint3.h index c956b654..00d09083 100644 --- a/dCommon/NiPoint3.h +++ b/dCommon/NiPoint3.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef __NIPOINT3_H__ +#define __NIPOINT3_H__ /*! \file NiPoint3.hpp @@ -12,13 +13,13 @@ typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoin //! A custom class the defines a point in space class NiPoint3 { public: - float x; //!< The x position - float y; //!< The y position - float z; //!< The z position + float x{ 0 }; //!< The x position + float y{ 0 }; //!< The y position + float z{ 0 }; //!< The z position //! Initializer - NiPoint3(void); + constexpr NiPoint3() = default; //! Initializer /*! @@ -26,23 +27,21 @@ public: \param y The y coordinate \param z The z coordinate */ - NiPoint3(float x, float y, float z); + constexpr NiPoint3(const float x, const float y, const float z) noexcept + : x{ x } + , y{ y } + , z{ z } { + } //! Copy Constructor /*! \param point The point to copy */ - NiPoint3(const NiPoint3& point); - - //! Destructor - ~NiPoint3(void); - - // MARK: Constants - static const NiPoint3 ZERO; //!< Point(0, 0, 0) - static const NiPoint3 UNIT_X; //!< Point(1, 0, 0) - static const NiPoint3 UNIT_Y; //!< Point(0, 1, 0) - static const NiPoint3 UNIT_Z; //!< Point(0, 0, 1) - static const NiPoint3 UNIT_ALL; //!< Point(1, 1, 1) + constexpr NiPoint3(const NiPoint3& point) noexcept + : x{ point.x } + , y{ point.y } + , z{ point.z } { + } // MARK: Getters / Setters @@ -50,38 +49,37 @@ public: /*! \return The x coordinate */ - float GetX(void) const; + [[nodiscard]] constexpr float GetX() const noexcept; //! Sets the X coordinate /*! \param x The x coordinate */ - void SetX(float x); + constexpr void SetX(const float x) noexcept; //! Gets the Y coordinate /*! \return The y coordinate */ - float GetY(void) const; + [[nodiscard]] constexpr float GetY() const noexcept; //! Sets the Y coordinate /*! \param y The y coordinate */ - void SetY(float y); + constexpr void SetY(const float y) noexcept; //! Gets the Z coordinate /*! \return The z coordinate */ - float GetZ(void) const; + [[nodiscard]] constexpr float GetZ() const noexcept; //! Sets the Z coordinate /*! \param z The z coordinate */ - void SetZ(float z); - + constexpr void SetZ(const float z) noexcept; // MARK: Member Functions @@ -89,70 +87,70 @@ public: /*! \return The scalar length of the vector */ - float Length(void) const; + [[nodiscard]] float Length() const; //! Gets the squared length of a vector /*! \return The squared length of a vector */ - float SquaredLength(void) const; + [[nodiscard]] constexpr float SquaredLength() const noexcept; //! Returns the dot product of the vector dotted with another vector /*! \param vec The second vector \return The dot product of the two vectors */ - float DotProduct(const Vector3& vec) const; + [[nodiscard]] constexpr float DotProduct(const Vector3& vec) const noexcept; //! Returns the cross product of the vector crossed with another vector /*! \param vec The second vector \return The cross product of the two vectors */ - Vector3 CrossProduct(const Vector3& vec) const; + [[nodiscard]] constexpr Vector3 CrossProduct(const Vector3& vec) const noexcept; //! Unitize the vector /*! \returns The current vector */ - NiPoint3 Unitize(void) const; - + [[nodiscard]] NiPoint3 Unitize() const; // MARK: Operators //! Operator to check for equality - bool operator==(const NiPoint3& point) const; + constexpr bool operator==(const NiPoint3& point) const noexcept; //! Operator to check for inequality - bool operator!=(const NiPoint3& point) const; + constexpr bool operator!=(const NiPoint3& point) const noexcept; //! Operator for subscripting - float& operator[](int i); + constexpr float& operator[](const int i) noexcept; //! Operator for subscripting - const float& operator[](int i) const; + constexpr const float& operator[](const int i) const noexcept; //! Operator for addition of vectors - NiPoint3 operator+(const NiPoint3& point) const; + constexpr NiPoint3 operator+(const NiPoint3& point) const noexcept; //! Operator for addition of vectors - NiPoint3& operator+=(const NiPoint3& point); + constexpr NiPoint3& operator+=(const NiPoint3& point) noexcept; + + constexpr NiPoint3& operator*=(const float scalar) noexcept; //! Operator for subtraction of vectors - NiPoint3 operator-(const NiPoint3& point) const; + constexpr NiPoint3 operator-(const NiPoint3& point) const noexcept; //! Operator for addition of a scalar on all vector components - NiPoint3 operator+(float fScalar) const; + constexpr NiPoint3 operator+(const float fScalar) const noexcept; //! Operator for subtraction of a scalar on all vector components - NiPoint3 operator-(float fScalar) const; + constexpr NiPoint3 operator-(const float fScalar) const noexcept; //! Operator for scalar multiplication of a vector - NiPoint3 operator*(float fScalar) const; + constexpr NiPoint3 operator*(const float fScalar) const noexcept; //! Operator for scalar division of a vector - NiPoint3 operator/(float fScalar) const; - + constexpr NiPoint3 operator/(const float fScalar) const noexcept; // MARK: Helper Functions @@ -162,14 +160,14 @@ public: \param maxPoint The maximum point of the bounding box \return Whether or not this point lies within the box */ - bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint); + [[nodiscard]] constexpr bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept; //! Checks to see if the point (or vector) is within a sphere /*! \param sphereCenter The sphere center \param radius The radius */ - bool IsWithinSpehere(const NiPoint3& sphereCenter, float radius); + [[nodiscard]] constexpr bool IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept; /*! \param a Start of line @@ -177,15 +175,30 @@ public: \param p Refrence point \return The point of line AB which is closest to P */ - static NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p); + [[nodiscard]] static constexpr NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept; - static float Angle(const NiPoint3& a, const NiPoint3& b); + [[nodiscard]] static float Angle(const NiPoint3& a, const NiPoint3& b); - static float Distance(const NiPoint3& a, const NiPoint3& b); + [[nodiscard]] static float Distance(const NiPoint3& a, const NiPoint3& b); - static float DistanceSquared(const NiPoint3& a, const NiPoint3& b); + [[nodiscard]] static constexpr float DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept; - static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta); + [[nodiscard]] static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta); - NiPoint3 RotateByQuaternion(const NiQuaternion& rotation); + //This code is yoinked from the MS XNA code, so it should be right, even if it's horrible. + [[nodiscard]] constexpr NiPoint3 RotateByQuaternion(const NiQuaternion& rotation) noexcept; }; + +// Static Variables +namespace NiPoint3Constant { + constexpr NiPoint3 ZERO(0.0f, 0.0f, 0.0f); + constexpr NiPoint3 UNIT_X(1.0f, 0.0f, 0.0f); + constexpr NiPoint3 UNIT_Y(0.0f, 1.0f, 0.0f); + constexpr NiPoint3 UNIT_Z(0.0f, 0.0f, 1.0f); + constexpr NiPoint3 UNIT_ALL(1.0f, 1.0f, 1.0f); +} + +// .inl file needed for code organization and to circumvent circular dependency issues +#include "NiPoint3.inl" + +#endif // !__NIPOINT3_H__ diff --git a/dCommon/NiPoint3.inl b/dCommon/NiPoint3.inl new file mode 100644 index 00000000..48aa3ae1 --- /dev/null +++ b/dCommon/NiPoint3.inl @@ -0,0 +1,196 @@ +#pragma once +#ifndef __NIPOINT3_H__ +#error "This should only be included inline in NiPoint3.h: Do not include directly!" +#endif + +#include "NiQuaternion.h" + +// MARK: Getters / Setters + +//! Gets the X coordinate +constexpr float NiPoint3::GetX() const noexcept { + return this->x; +} + +//! Sets the X coordinate +constexpr void NiPoint3::SetX(const float x) noexcept { + this->x = x; +} + +//! Gets the Y coordinate +constexpr float NiPoint3::GetY() const noexcept { + return this->y; +} + +//! Sets the Y coordinate +constexpr void NiPoint3::SetY(const float y) noexcept { + this->y = y; +} + +//! Gets the Z coordinate +constexpr float NiPoint3::GetZ() const noexcept { + return this->z; +} + +//! Sets the Z coordinate +constexpr void NiPoint3::SetZ(const float z) noexcept { + this->z = z; +} + +// MARK: Member Functions + +//! Gets the squared length of a vector +constexpr float NiPoint3::SquaredLength() const noexcept { + return (x * x + y * y + z * z); +} + +//! Returns the dot product of the vector dotted with another vector +constexpr float NiPoint3::DotProduct(const Vector3& vec) const noexcept { + return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z)); +} + +//! Returns the cross product of the vector crossed with another vector +constexpr Vector3 NiPoint3::CrossProduct(const Vector3& vec) const noexcept { + return Vector3(((this->y * vec.z) - (this->z * vec.y)), + ((this->z * vec.x) - (this->x * vec.z)), + ((this->x * vec.y) - (this->y * vec.x))); +} + +// MARK: Operators + +//! Operator to check for equality +constexpr bool NiPoint3::operator==(const NiPoint3& point) const noexcept { + return point.x == this->x && point.y == this->y && point.z == this->z; +} + +//! Operator to check for inequality +constexpr bool NiPoint3::operator!=(const NiPoint3& point) const noexcept { + return !(*this == point); +} + +//! Operator for subscripting +constexpr float& NiPoint3::operator[](const int i) noexcept { + float* base = &x; + return base[i]; +} + +//! Operator for subscripting +constexpr const float& NiPoint3::operator[](const int i) const noexcept { + const float* base = &x; + return base[i]; +} + +//! Operator for addition of vectors +constexpr NiPoint3 NiPoint3::operator+(const NiPoint3& point) const noexcept { + return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z); +} + +//! Operator for addition of vectors +constexpr NiPoint3& NiPoint3::operator+=(const NiPoint3& point) noexcept { + this->x += point.x; + this->y += point.y; + this->z += point.z; + return *this; +} + +constexpr NiPoint3& NiPoint3::operator*=(const float scalar) noexcept { + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; + return *this; +} + +//! Operator for subtraction of vectors +constexpr NiPoint3 NiPoint3::operator-(const NiPoint3& point) const noexcept { + return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z); +} + +//! Operator for addition of a scalar on all vector components +constexpr NiPoint3 NiPoint3::operator+(const float fScalar) const noexcept { + return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar); +} + +//! Operator for subtraction of a scalar on all vector components +constexpr NiPoint3 NiPoint3::operator-(const float fScalar) const noexcept { + return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar); +} + +//! Operator for scalar multiplication of a vector +constexpr NiPoint3 NiPoint3::operator*(const float fScalar) const noexcept { + return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar); +} + +//! Operator for scalar division of a vector +constexpr NiPoint3 NiPoint3::operator/(const float fScalar) const noexcept { + float retX = this->x != 0 ? this->x / fScalar : 0; + float retY = this->y != 0 ? this->y / fScalar : 0; + float retZ = this->z != 0 ? this->z / fScalar : 0; + return NiPoint3(retX, retY, retZ); +} + +// MARK: Helper Functions + +//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box +constexpr bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept { + if (this->x < minPoint.x) return false; + if (this->x > maxPoint.x) return false; + if (this->y < minPoint.y) return false; + if (this->y > maxPoint.y) return false; + + return (this->z < maxPoint.z && this->z > minPoint.z); +} + +//! Checks to see if the point (or vector) is within a sphere +constexpr bool NiPoint3::IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept { + Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ()); + return (diffVec.SquaredLength() <= (radius * radius)); +} + +constexpr NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept { + if (a == b) return a; + + const auto pa = p - a; + const auto ab = b - a; + + const auto t = pa.DotProduct(ab) / ab.SquaredLength(); + + if (t <= 0.0f) return a; + + if (t >= 1.0f) return b; + + return a + ab * t; +} + +constexpr float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept { + const auto dx = a.x - b.x; + const auto dy = a.y - b.y; + const auto dz = a.z - b.z; + + return dx * dx + dy * dy + dz * dz; +} + +//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible. +constexpr NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) noexcept { + Vector3 vector; + float num12 = rotation.x + rotation.x; + float num2 = rotation.y + rotation.y; + float num = rotation.z + rotation.z; + float num11 = rotation.w * num12; + float num10 = rotation.w * num2; + float num9 = rotation.w * num; + float num8 = rotation.x * num12; + float num7 = rotation.x * num2; + float num6 = rotation.x * num; + float num5 = rotation.y * num2; + float num4 = rotation.y * num; + float num3 = rotation.z * num; + + NiPoint3 value = *this; + float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10)); + float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11)); + float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5)); + vector.x = num15; + vector.y = num14; + vector.z = num13; + return vector; +} diff --git a/dCommon/NiQuaternion.cpp b/dCommon/NiQuaternion.cpp index 33c5c976..b12d3991 100644 --- a/dCommon/NiQuaternion.cpp +++ b/dCommon/NiQuaternion.cpp @@ -3,89 +3,8 @@ // C++ #include -// Static Variables -const NiQuaternion NiQuaternion::IDENTITY(1, 0, 0, 0); - -//! The initializer -NiQuaternion::NiQuaternion(void) { - this->w = 1; - this->x = 0; - this->y = 0; - this->z = 0; -} - -//! The initializer -NiQuaternion::NiQuaternion(float w, float x, float y, float z) { - this->w = w; - this->x = x; - this->y = y; - this->z = z; -} - -//! Destructor -NiQuaternion::~NiQuaternion(void) {} - - -// MARK: Setters / Getters - -//! Gets the W coordinate -float NiQuaternion::GetW(void) const { - return this->w; -} - -//! Sets the W coordinate -void NiQuaternion::SetW(float w) { - this->w = w; -} - -//! Gets the X coordinate -float NiQuaternion::GetX(void) const { - return this->x; -} - -//! Sets the X coordinate -void NiQuaternion::SetX(float x) { - this->x = x; -} - -//! Gets the Y coordinate -float NiQuaternion::GetY(void) const { - return this->y; -} - -//! Sets the Y coordinate -void NiQuaternion::SetY(float y) { - this->y = y; -} - -//! Gets the Z coordinate -float NiQuaternion::GetZ(void) const { - return this->z; -} - -//! Sets the Z coordinate -void NiQuaternion::SetZ(float z) { - this->z = z; -} - - // MARK: Member Functions -//! Returns the forward vector from the quaternion -Vector3 NiQuaternion::GetForwardVector(void) const { - return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y)); -} - -//! Returns the up vector from the quaternion -Vector3 NiQuaternion::GetUpVector(void) const { - return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x)); -} - -//! Returns the right vector from the quaternion -Vector3 NiQuaternion::GetRightVector(void) const { - return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y)); -} - Vector3 NiQuaternion::GetEulerAngles() const { Vector3 angles; @@ -111,22 +30,9 @@ Vector3 NiQuaternion::GetEulerAngles() const { return angles; } -// MARK: Operators - -//! Operator to check for equality -bool NiQuaternion::operator==(const NiQuaternion& rot) const { - return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w; -} - -//! Operator to check for inequality -bool NiQuaternion::operator!=(const NiQuaternion& rot) const { - return !(*this == rot); -} - - // MARK: Helper Functions -//! Look from a specific point in space to another point in space +//! Look from a specific point in space to another point in space (Y-locked) NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { //To make sure we don't orient around the X/Z axis: NiPoint3 source = sourcePoint; @@ -136,7 +42,7 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d NiPoint3 forwardVector = NiPoint3(dest - source).Unitize(); - NiPoint3 posZ = NiPoint3::UNIT_Z; + NiPoint3 posZ = NiPoint3Constant::UNIT_Z; NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize(); float dot = posZ.DotProduct(forwardVector); @@ -148,10 +54,11 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle); } +//! Look from a specific point in space to another point in space NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize(); - NiPoint3 posZ = NiPoint3::UNIT_Z; + NiPoint3 posZ = NiPoint3Constant::UNIT_Z; NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize(); float dot = posZ.DotProduct(forwardVector); diff --git a/dCommon/NiQuaternion.h b/dCommon/NiQuaternion.h index b7d60f4e..482b86fa 100644 --- a/dCommon/NiQuaternion.h +++ b/dCommon/NiQuaternion.h @@ -1,4 +1,5 @@ -#pragma once +#ifndef __NIQUATERNION_H__ +#define __NIQUATERNION_H__ // Custom Classes #include "NiPoint3.h" @@ -14,14 +15,14 @@ typedef NiQuaternion Quaternion; //!< A typedef for a shorthand version o //! A class that defines a rotation in space class NiQuaternion { public: - float w; //!< The w coordinate - float x; //!< The x coordinate - float y; //!< The y coordinate - float z; //!< The z coordinate + float w{ 1 }; //!< The w coordinate + float x{ 0 }; //!< The x coordinate + float y{ 0 }; //!< The y coordinate + float z{ 0 }; //!< The z coordinate //! The initializer - NiQuaternion(void); + constexpr NiQuaternion() = default; //! The initializer /*! @@ -30,13 +31,12 @@ public: \param y The y coordinate \param z The z coordinate */ - NiQuaternion(float w, float x, float y, float z); - - //! Destructor - ~NiQuaternion(void); - - // MARK: Constants - static const NiQuaternion IDENTITY; //!< Quaternion(1, 0, 0, 0) + constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept + : w{ w } + , x{ x } + , y{ y } + , z{ z } { + } // MARK: Setters / Getters @@ -44,50 +44,49 @@ public: /*! \return The w coordinate */ - float GetW(void) const; + [[nodiscard]] constexpr float GetW() const noexcept; //! Sets the W coordinate /*! \param w The w coordinate */ - void SetW(float w); + constexpr void SetW(const float w) noexcept; //! Gets the X coordinate /*! \return The x coordinate */ - float GetX(void) const; + [[nodiscard]] constexpr float GetX() const noexcept; //! Sets the X coordinate /*! \param x The x coordinate */ - void SetX(float x); + constexpr void SetX(const float x) noexcept; //! Gets the Y coordinate /*! \return The y coordinate */ - float GetY(void) const; + [[nodiscard]] constexpr float GetY() const noexcept; //! Sets the Y coordinate /*! \param y The y coordinate */ - void SetY(float y); + constexpr void SetY(const float y) noexcept; //! Gets the Z coordinate /*! \return The z coordinate */ - float GetZ(void) const; + [[nodiscard]] constexpr float GetZ() const noexcept; //! Sets the Z coordinate /*! \param z The z coordinate */ - void SetZ(float z); - + constexpr void SetZ(const float z) noexcept; // MARK: Member Functions @@ -95,31 +94,29 @@ public: /*! \return The forward vector of the quaternion */ - Vector3 GetForwardVector(void) const; + [[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept; //! Returns the up vector from the quaternion /*! \return The up vector fo the quaternion */ - Vector3 GetUpVector(void) const; + [[nodiscard]] constexpr Vector3 GetUpVector() const noexcept; //! Returns the right vector from the quaternion /*! \return The right vector of the quaternion */ - Vector3 GetRightVector(void) const; - - Vector3 GetEulerAngles() const; + [[nodiscard]] constexpr Vector3 GetRightVector() const noexcept; + [[nodiscard]] Vector3 GetEulerAngles() const; // MARK: Operators //! Operator to check for equality - bool operator==(const NiQuaternion& rot) const; + constexpr bool operator==(const NiQuaternion& rot) const noexcept; //! Operator to check for inequality - bool operator!=(const NiQuaternion& rot) const; - + constexpr bool operator!=(const NiQuaternion& rot) const noexcept; // MARK: Helper Functions @@ -129,7 +126,7 @@ public: \param destPoint The destination location \return The Quaternion with the rotation towards the destination */ - static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint); + [[nodiscard]] static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint); //! Look from a specific point in space to another point in space /*! @@ -137,7 +134,7 @@ public: \param destPoint The destination location \return The Quaternion with the rotation towards the destination */ - static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint); + [[nodiscard]] static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint); //! Creates a Quaternion from a specific axis and angle relative to that axis /*! @@ -145,7 +142,17 @@ public: \param angle The angle relative to this axis \return A quaternion created from the axis and angle */ - static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle); + [[nodiscard]] static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle); - static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles); + [[nodiscard]] static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles); }; + +// Static Variables +namespace NiQuaternionConstant { + constexpr NiQuaternion IDENTITY(1, 0, 0, 0); +} + +// Include constexpr and inline function definitions in a seperate file for readability +#include "NiQuaternion.inl" + +#endif // !__NIQUATERNION_H__ diff --git a/dCommon/NiQuaternion.inl b/dCommon/NiQuaternion.inl new file mode 100644 index 00000000..9ed84ffc --- /dev/null +++ b/dCommon/NiQuaternion.inl @@ -0,0 +1,75 @@ +#pragma once +#ifndef __NIQUATERNION_H__ +#error "This should only be included inline in NiQuaternion.h: Do not include directly!" +#endif + +// MARK: Setters / Getters + +//! Gets the W coordinate +constexpr float NiQuaternion::GetW() const noexcept { + return this->w; +} + +//! Sets the W coordinate +constexpr void NiQuaternion::SetW(const float w) noexcept { + this->w = w; +} + +//! Gets the X coordinate +constexpr float NiQuaternion::GetX() const noexcept { + return this->x; +} + +//! Sets the X coordinate +constexpr void NiQuaternion::SetX(const float x) noexcept { + this->x = x; +} + +//! Gets the Y coordinate +constexpr float NiQuaternion::GetY() const noexcept { + return this->y; +} + +//! Sets the Y coordinate +constexpr void NiQuaternion::SetY(const float y) noexcept { + this->y = y; +} + +//! Gets the Z coordinate +constexpr float NiQuaternion::GetZ() const noexcept { + return this->z; +} + +//! Sets the Z coordinate +constexpr void NiQuaternion::SetZ(const float z) noexcept { + this->z = z; +} + +// MARK: Member Functions + +//! Returns the forward vector from the quaternion +constexpr Vector3 NiQuaternion::GetForwardVector() const noexcept { + return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y)); +} + +//! Returns the up vector from the quaternion +constexpr Vector3 NiQuaternion::GetUpVector() const noexcept { + return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x)); +} + +//! Returns the right vector from the quaternion +constexpr Vector3 NiQuaternion::GetRightVector() const noexcept { + return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y)); +} + +// MARK: Operators + +//! Operator to check for equality +constexpr bool NiQuaternion::operator==(const NiQuaternion& rot) const noexcept { + return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w; +} + +//! Operator to check for inequality +constexpr bool NiQuaternion::operator!=(const NiQuaternion& rot) const noexcept { + return !(*this == rot); +} diff --git a/dCommon/PositionUpdate.h b/dCommon/PositionUpdate.h new file mode 100644 index 00000000..4d591a97 --- /dev/null +++ b/dCommon/PositionUpdate.h @@ -0,0 +1,50 @@ +#ifndef __POSITIONUPDATE__H__ +#define __POSITIONUPDATE__H__ + +#include "NiPoint3.h" +#include "NiQuaternion.h" + + +struct RemoteInputInfo { + RemoteInputInfo() { + m_RemoteInputX = 0; + m_RemoteInputY = 0; + m_IsPowersliding = false; + m_IsModified = false; + } + + void operator=(const RemoteInputInfo& other) { + m_RemoteInputX = other.m_RemoteInputX; + m_RemoteInputY = other.m_RemoteInputY; + m_IsPowersliding = other.m_IsPowersliding; + m_IsModified = other.m_IsModified; + } + + bool operator==(const RemoteInputInfo& other) { + return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; + } + + float m_RemoteInputX; + float m_RemoteInputY; + bool m_IsPowersliding; + bool m_IsModified; +}; + +struct LocalSpaceInfo { + LWOOBJID objectId = LWOOBJID_EMPTY; + NiPoint3 position = NiPoint3Constant::ZERO; + NiPoint3 linearVelocity = NiPoint3Constant::ZERO; +}; + +struct PositionUpdate { + NiPoint3 position = NiPoint3Constant::ZERO; + NiQuaternion rotation = NiQuaternionConstant::IDENTITY; + bool onGround = false; + bool onRail = false; + NiPoint3 velocity = NiPoint3Constant::ZERO; + NiPoint3 angularVelocity = NiPoint3Constant::ZERO; + LocalSpaceInfo localSpaceInfo; + RemoteInputInfo remoteInputInfo; +}; + +#endif //!__POSITIONUPDATE__H__ diff --git a/dCommon/SHA512.cpp b/dCommon/SHA512.cpp deleted file mode 100644 index f5a71d99..00000000 --- a/dCommon/SHA512.cpp +++ /dev/null @@ -1,152 +0,0 @@ -#include "SHA512.h" - -#include -#include - -const unsigned long long SHA512::sha512_k[80] = //ULL = uint64 -{ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, - 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, - 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, - 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, - 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, - 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, - 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, - 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, - 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, - 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, - 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, - 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, - 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, - 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, - 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, - 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, - 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, - 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, - 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, - 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, - 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, - 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, - 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, - 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, - 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, - 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, - 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, - 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, - 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, - 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, - 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, - 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, - 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, - 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, - 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, - 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, - 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, - 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, - 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, - 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL }; - -void SHA512::transform(const unsigned char* message, unsigned int block_nb) { - uint64 w[80]; - uint64 wv[8]; - uint64 t1, t2; - const unsigned char* sub_block; - int i, j; - for (i = 0; i < (int)block_nb; i++) { - sub_block = message + (i << 7); - for (j = 0; j < 16; j++) { - SHA2_PACK64(&sub_block[j << 3], &w[j]); - } - for (j = 16; j < 80; j++) { - w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16]; - } - for (j = 0; j < 8; j++) { - wv[j] = m_h[j]; - } - for (j = 0; j < 80; j++) { - t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6]) - + sha512_k[j] + w[j]; - t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - for (j = 0; j < 8; j++) { - m_h[j] += wv[j]; - } - - } -} - -void SHA512::init() { - m_h[0] = 0x6a09e667f3bcc908ULL; - m_h[1] = 0xbb67ae8584caa73bULL; - m_h[2] = 0x3c6ef372fe94f82bULL; - m_h[3] = 0xa54ff53a5f1d36f1ULL; - m_h[4] = 0x510e527fade682d1ULL; - m_h[5] = 0x9b05688c2b3e6c1fULL; - m_h[6] = 0x1f83d9abfb41bd6bULL; - m_h[7] = 0x5be0cd19137e2179ULL; - m_len = 0; - m_tot_len = 0; -} - -void SHA512::update(const unsigned char* message, unsigned int len) { - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char* shifted_message; - tmp_len = SHA384_512_BLOCK_SIZE - m_len; - rem_len = len < tmp_len ? len : tmp_len; - memcpy(&m_block[m_len], message, rem_len); - if (m_len + len < SHA384_512_BLOCK_SIZE) { - m_len += len; - return; - } - new_len = len - rem_len; - block_nb = new_len / SHA384_512_BLOCK_SIZE; - shifted_message = message + rem_len; - transform(m_block, 1); - transform(shifted_message, block_nb); - rem_len = new_len % SHA384_512_BLOCK_SIZE; - memcpy(m_block, &shifted_message[block_nb << 7], rem_len); - m_len = rem_len; - m_tot_len += (block_nb + 1) << 7; -} - -void SHA512::final(unsigned char* digest) { - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - int i; - block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17) - < (m_len % SHA384_512_BLOCK_SIZE)); - len_b = (m_tot_len + m_len) << 3; - pm_len = block_nb << 7; - memset(m_block + m_len, 0, pm_len - m_len); - m_block[m_len] = 0x80; - SHA2_UNPACK32(len_b, m_block + pm_len - 4); - transform(m_block, block_nb); - for (i = 0; i < 8; i++) { - SHA2_UNPACK64(m_h[i], &digest[i << 3]); - } -} - -std::string sha512(std::string input) { - unsigned char digest[SHA512::DIGEST_SIZE]; - memset(digest, 0, SHA512::DIGEST_SIZE); - class SHA512 ctx; - ctx.init(); - ctx.update((unsigned char*)input.c_str(), input.length()); - ctx.final(digest); - - char buf[2 * SHA512::DIGEST_SIZE + 1]; - buf[2 * SHA512::DIGEST_SIZE] = 0; - for (int i = 0; i < SHA512::DIGEST_SIZE; i++) - sprintf(buf + i * 2, "%02x", digest[i]); - - return std::string(buf); -} diff --git a/dCommon/SHA512.h b/dCommon/SHA512.h deleted file mode 100644 index 512fa645..00000000 --- a/dCommon/SHA512.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -// C++ -#include - -class SHA512 { -protected: - typedef unsigned char uint8; - typedef unsigned int uint32; - typedef unsigned long long uint64; - - const static uint64 sha512_k[]; - static const unsigned int SHA384_512_BLOCK_SIZE = (1024 / 8); - -public: - void init(); - void update(const unsigned char* message, unsigned int len); - void final(unsigned char* digest); - static const unsigned int DIGEST_SIZE = (512 / 8); - -protected: - void transform(const unsigned char* message, unsigned int block_nb); - unsigned int m_tot_len; - unsigned int m_len; - unsigned char m_block[2 * SHA384_512_BLOCK_SIZE]; - uint64 m_h[8]; -}; - -std::string sha512(std::string input); - -#define SHA2_SHFR(x, n) (x >> n) -#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n))) -#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z)) -#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) -#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39)) -#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41)) -#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7)) -#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6)) -#define SHA2_UNPACK32(x, str) \ -{ \ -*((str) + 3) = (uint8) ((x) ); \ -*((str) + 2) = (uint8) ((x) >> 8); \ -*((str) + 1) = (uint8) ((x) >> 16); \ -*((str) + 0) = (uint8) ((x) >> 24); \ -} -#define SHA2_UNPACK64(x, str) \ -{ \ -*((str) + 7) = (uint8) ((x) ); \ -*((str) + 6) = (uint8) ((x) >> 8); \ -*((str) + 5) = (uint8) ((x) >> 16); \ -*((str) + 4) = (uint8) ((x) >> 24); \ -*((str) + 3) = (uint8) ((x) >> 32); \ -*((str) + 2) = (uint8) ((x) >> 40); \ -*((str) + 1) = (uint8) ((x) >> 48); \ -*((str) + 0) = (uint8) ((x) >> 56); \ -} -#define SHA2_PACK64(str, x) \ -{ \ -*(x) = ((uint64) *((str) + 7) ) \ -| ((uint64) *((str) + 6) << 8) \ -| ((uint64) *((str) + 5) << 16) \ -| ((uint64) *((str) + 4) << 24) \ -| ((uint64) *((str) + 3) << 32) \ -| ((uint64) *((str) + 2) << 40) \ -| ((uint64) *((str) + 1) << 48) \ -| ((uint64) *((str) + 0) << 56); \ -} diff --git a/dCommon/Type.cpp b/dCommon/Type.cpp deleted file mode 100644 index 5cce9d66..00000000 --- a/dCommon/Type.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "Type.h" -#ifdef __GNUG__ -#include -#include -#include - -std::string demangle(const char* name) { - - int status = -4; // some arbitrary value to eliminate the compiler warning - - // enable c++11 by passing the flag -std=c++11 to g++ - std::unique_ptr res{ - abi::__cxa_demangle(name, NULL, NULL, &status), - std::free - }; - - return (status == 0) ? res.get() : name; -} - -#else - -// does nothing if not g++ -std::string demangle(const char* name) { - return name; -} - -#endif diff --git a/dCommon/Type.h b/dCommon/Type.h deleted file mode 100644 index 552bb09f..00000000 --- a/dCommon/Type.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include -#include - -std::string demangle(const char* name); - -template -std::string type(const T& t) { - - return demangle(typeid(t).name()); -} diff --git a/dCommon/ZCompression.cpp b/dCommon/ZCompression.cpp index d5d4c126..e5b3c8fb 100644 --- a/dCommon/ZCompression.cpp +++ b/dCommon/ZCompression.cpp @@ -1,6 +1,6 @@ #include "ZCompression.h" -#include +#include "zlib.h" namespace ZCompression { int32_t GetMaxCompressedLength(int32_t nLenSrc) { diff --git a/dCommon/dClient/AssetManager.cpp b/dCommon/dClient/AssetManager.cpp index 00cedba9..59427ee5 100644 --- a/dCommon/dClient/AssetManager.cpp +++ b/dCommon/dClient/AssetManager.cpp @@ -2,9 +2,9 @@ #include "AssetManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" -#include +#include "zlib.h" AssetManager::AssetManager(const std::filesystem::path& path) { if (!std::filesystem::is_directory(path)) { @@ -81,8 +81,8 @@ bool AssetManager::HasFile(const char* name) { std::replace(fixedName.begin(), fixedName.end(), '/', '\\'); if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName; - uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size()); - crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4); + uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast(const_cast(fixedName.c_str())), fixedName.size()); + crc = crc32b(crc, reinterpret_cast(const_cast("\0\0\0\0")), 4); for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { if (item.m_Crc == crc) { @@ -113,7 +113,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) { #endif fseek(file, 0, SEEK_END); *len = ftell(file); - *data = (char*)malloc(*len); + *data = static_cast(malloc(*len)); fseek(file, 0, SEEK_SET); int32_t readInData = fread(*data, sizeof(uint8_t), *len, file); fclose(file); @@ -129,8 +129,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) { fixedName = "client\\res\\" + fixedName; } int32_t packIndex = -1; - uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size()); - crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4); + uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast(const_cast(fixedName.c_str())), fixedName.size()); + crc = crc32b(crc, reinterpret_cast(const_cast("\0\0\0\0")), 4); for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { if (item.m_Crc == crc) { @@ -152,13 +152,12 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) { return success; } -AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) { - char* buf; - uint32_t len; +AssetStream AssetManager::GetFile(const char* name) { + char* buf; uint32_t len; bool success = this->GetFile(name, &buf, &len); - return AssetMemoryBuffer(buf, len, success); + return AssetStream(buf, len, success); } uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) { @@ -168,7 +167,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) { crc = base; for (i = 0; i < l; i++) { // xor next byte to upper bits of crc - crc ^= (((unsigned int)message[i]) << 24); + crc ^= (static_cast(message[i]) << 24); for (j = 0; j < 8; j++) { // Do eight times. msb = crc >> 31; crc <<= 1; diff --git a/dCommon/dClient/AssetManager.h b/dCommon/dClient/AssetManager.h index bc7e5ff7..2116f223 100644 --- a/dCommon/dClient/AssetManager.h +++ b/dCommon/dClient/AssetManager.h @@ -25,6 +25,10 @@ struct AssetMemoryBuffer : std::streambuf { this->setg(base, base, base + n); } + ~AssetMemoryBuffer() { + if (m_Success) free(m_Base); + } + pos_type seekpos(pos_type sp, std::ios_base::openmode which) override { return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which); } @@ -40,9 +44,17 @@ struct AssetMemoryBuffer : std::streambuf { setg(eback(), eback() + off, egptr()); return gptr() - eback(); } +}; - void close() { - delete m_Base; +struct AssetStream : std::istream { + AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {} + + ~AssetStream() { + delete rdbuf(); + } + + operator bool() { + return reinterpret_cast(rdbuf())->m_Success; } }; @@ -56,7 +68,7 @@ public: bool HasFile(const char* name); bool GetFile(const char* name, char** data, uint32_t* len); - AssetMemoryBuffer GetFileAsBuffer(const char* name); + AssetStream GetFile(const char* name); private: void LoadPackIndex(); diff --git a/dCommon/dClient/CMakeLists.txt b/dCommon/dClient/CMakeLists.txt index 69bb1712..017a22a5 100644 --- a/dCommon/dClient/CMakeLists.txt +++ b/dCommon/dClient/CMakeLists.txt @@ -1,6 +1,6 @@ set(DCOMMON_DCLIENT_SOURCES + "AssetManager.cpp" "PackIndex.cpp" "Pack.cpp" - "AssetManager.cpp" PARENT_SCOPE ) diff --git a/dCommon/dClient/ClientVersion.h b/dCommon/dClient/ClientVersion.h new file mode 100644 index 00000000..393103ab --- /dev/null +++ b/dCommon/dClient/ClientVersion.h @@ -0,0 +1,12 @@ +#ifndef __CLIENTVERSION_H__ +#define __CLIENTVERSION_H__ + +#include + +namespace ClientVersion { + constexpr uint16_t major = 1; + constexpr uint16_t current = 10; + constexpr uint16_t minor = 64; +} + +#endif // !__CLIENTVERSION_H__ diff --git a/dCommon/dClient/Pack.cpp b/dCommon/dClient/Pack.cpp index 11345fc9..cf32a53f 100644 --- a/dCommon/dClient/Pack.cpp +++ b/dCommon/dClient/Pack.cpp @@ -76,7 +76,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) { fseek(file, pos, SEEK_SET); if (!isCompressed) { - char* tempData = (char*)malloc(pkRecord.m_UncompressedSize); + char* tempData = static_cast(malloc(pkRecord.m_UncompressedSize)); int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file); *data = tempData; @@ -90,7 +90,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) { fseek(file, pos, SEEK_SET); - char* decompressedData = (char*)malloc(pkRecord.m_UncompressedSize); + char* decompressedData = static_cast(malloc(pkRecord.m_UncompressedSize)); uint32_t currentReadPos = 0; while (true) { @@ -100,12 +100,12 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) { int32_t readInData = fread(&size, sizeof(uint32_t), 1, file); pos += 4; // Move pointer position 4 to the right - char* chunk = (char*)malloc(size); + char* chunk = static_cast(malloc(size)); int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file); pos += size; // Move pointer position the amount of bytes read to the right int32_t err; - currentReadPos += ZCompression::Decompress((uint8_t*)chunk, size, reinterpret_cast(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); + currentReadPos += ZCompression::Decompress(reinterpret_cast(chunk), size, reinterpret_cast(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); free(chunk); } diff --git a/dCommon/dClient/Pack.h b/dCommon/dClient/Pack.h index 3e95b00a..8113ed55 100644 --- a/dCommon/dClient/Pack.h +++ b/dCommon/dClient/Pack.h @@ -3,6 +3,7 @@ #include #include #include +#include #pragma pack(push, 1) struct PackRecord { diff --git a/dCommon/dClient/PackIndex.cpp b/dCommon/dClient/PackIndex.cpp index 5d96abeb..0d7a7fe9 100644 --- a/dCommon/dClient/PackIndex.cpp +++ b/dCommon/dClient/PackIndex.cpp @@ -1,28 +1,17 @@ #include "PackIndex.h" #include "BinaryIO.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" PackIndex::PackIndex(const std::filesystem::path& filePath) { m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary); BinaryIO::BinaryRead(m_FileStream, m_Version); BinaryIO::BinaryRead(m_FileStream, m_PackPathCount); - - for (int i = 0; i < m_PackPathCount; i++) { - uint32_t stringLen = 0; - BinaryIO::BinaryRead(m_FileStream, stringLen); - - std::string path; - - for (int j = 0; j < stringLen; j++) { - char inChar; - BinaryIO::BinaryRead(m_FileStream, inChar); - - path += inChar; - } - - m_PackPaths.push_back(path); + + m_PackPaths.resize(m_PackPathCount); + for (auto& item : m_PackPaths) { + BinaryIO::ReadString(m_FileStream, item, BinaryIO::ReadType::String); } BinaryIO::BinaryRead(m_FileStream, m_PackFileIndexCount); @@ -34,7 +23,7 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) { m_PackFileIndices.push_back(packFileIndex); } - Game::logger->Log("PackIndex", "Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size()); + LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size()); for (auto& item : m_PackPaths) { std::replace(item.begin(), item.end(), '\\', '/'); diff --git a/dCommon/dConfig.cpp b/dCommon/dConfig.cpp index f09a44c1..56a43848 100644 --- a/dCommon/dConfig.cpp +++ b/dCommon/dConfig.cpp @@ -10,8 +10,23 @@ dConfig::dConfig(const std::string& filepath) { LoadConfig(); } +std::filesystem::path GetConfigDir() { + std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir(); + if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) { + config_dir /= env_p; + } + return config_dir; +} + +const bool dConfig::Exists(const std::string& filepath) { + std::filesystem::path config_dir = GetConfigDir(); + return std::filesystem::exists(config_dir / filepath); +} + void dConfig::LoadConfig() { - std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath); + std::filesystem::path config_dir = GetConfigDir(); + + std::ifstream in(config_dir / m_ConfigFilePath); if (!in.good()) return; std::string line{}; @@ -19,7 +34,7 @@ void dConfig::LoadConfig() { if (!line.empty() && line.front() != '#') ProcessLine(line); } - std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in); + std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in); if (!sharedConfig.good()) return; line.clear(); @@ -34,17 +49,20 @@ void dConfig::ReloadConfig() { } const std::string& dConfig::GetValue(std::string key) { + std::string upper_key(key); + std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper); + if (const char* env_p = std::getenv(upper_key.c_str())) { + this->m_ConfigValues[key] = env_p; + } return this->m_ConfigValues[key]; } void dConfig::ProcessLine(const std::string& line) { - auto splitLine = GeneralUtils::SplitString(line, '='); - - if (splitLine.size() != 2) return; + auto splitLoc = line.find('='); + auto key = line.substr(0, splitLoc); + auto value = line.substr(splitLoc + 1); //Make sure that on Linux, we remove special characters: - auto& key = splitLine.at(0); - auto& value = splitLine.at(1); if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1); if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return; diff --git a/dCommon/dConfig.h b/dCommon/dConfig.h index 562c1ce9..65415eea 100644 --- a/dCommon/dConfig.h +++ b/dCommon/dConfig.h @@ -7,6 +7,11 @@ class dConfig { public: dConfig(const std::string& filepath); + /** + * Checks whether the specified filepath exists + */ + static const bool Exists(const std::string& filepath); + /** * Gets the specified key from the config. Returns an empty string if the value is not found. * diff --git a/dCommon/dEnums/StringifiedEnum.h b/dCommon/dEnums/StringifiedEnum.h new file mode 100644 index 00000000..1816d705 --- /dev/null +++ b/dCommon/dEnums/StringifiedEnum.h @@ -0,0 +1,29 @@ +#ifndef __STRINGIFIEDENUM_H__ +#define __STRINGIFIEDENUM_H__ + +#include +#include "magic_enum.hpp" + +namespace StringifiedEnum { + template + const std::string_view ToString(const T e) { + static_assert(std::is_enum_v, "Not an enum"); // Check type + + constexpr auto& sv = magic_enum::enum_entries(); + + const auto it = std::lower_bound( + sv.begin(), sv.end(), e, + [&](const std::pair& lhs, const T rhs) { return lhs.first < rhs; } + ); + + std::string_view output; + if (it != sv.end() && it->first == e) { + output = it->second; + } else { + output = "UNKNOWN"; + } + return output; + } +} + +#endif // !__STRINGIFIEDENUM_H__ diff --git a/dCommon/dEnums/dCommonVars.h b/dCommon/dEnums/dCommonVars.h index f67145da..d871e267 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -9,6 +9,7 @@ #include "BitStream.h" #include "eConnectionType.h" #include "eClientMessageType.h" +#include "BitStreamUtils.h" #pragma warning (disable:4251) //Disables SQL warnings @@ -32,92 +33,90 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate); #define CBITSTREAM RakNet::BitStream bitStream; #define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false); #define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits()); -#define CMSGHEADER PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); +#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); #define SEND_PACKET Game::server->Send(&bitStream, sysAddr, false); #define SEND_PACKET_BROADCAST Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); //=========== TYPEDEFS ========== -typedef int32_t LOT; //!< A LOT -typedef int64_t LWOOBJID; //!< An object ID (should be unsigned actually but ok) -typedef int32_t TSkillID; //!< A skill ID -typedef uint32_t LWOCLONEID; //!< Used for Clone IDs -typedef uint16_t LWOMAPID; //!< Used for Map IDs -typedef uint16_t LWOINSTANCEID; //!< Used for Instance IDs -typedef uint32_t PROPERTYCLONELIST; //!< Used for Property Clone IDs -typedef uint32_t StripId; +using LOT = int32_t; //!< A LOT +using LWOOBJID = int64_t; //!< An object ID (should be unsigned actually but ok) +using TSkillID = int32_t; //!< A skill ID +using LWOCLONEID = uint32_t; //!< Used for Clone IDs +using LWOMAPID = uint16_t; //!< Used for Map IDs +using LWOINSTANCEID = uint16_t; //!< Used for Instance IDs +using PROPERTYCLONELIST = uint32_t; //!< Used for Property Clone IDs +using StripId = uint32_t; -const LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID -const LOT LOT_NULL = -1; //!< A null LOT -const int32_t LOOTTYPE_NONE = 0; //!< No loot type available -const float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority -const uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size -const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID -const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID -const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID -const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID +constexpr LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID +constexpr LOT LOT_NULL = -1; //!< A null LOT +constexpr int32_t LOOTTYPE_NONE = 0; //!< No loot type available +constexpr float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority +constexpr uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size +constexpr LWOCLONEID LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID +constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID +constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID +constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID -const float PI = 3.14159f; +constexpr float PI = 3.14159f; //============ STRUCTS ============== struct LWOSCENEID { public: - LWOSCENEID() { m_sceneID = -1; m_layerID = 0; } - LWOSCENEID(int sceneID) { m_sceneID = sceneID; m_layerID = 0; } - LWOSCENEID(int sceneID, unsigned int layerID) { m_sceneID = sceneID; m_layerID = layerID; } + constexpr LWOSCENEID() noexcept { m_sceneID = -1; m_layerID = 0; } + constexpr LWOSCENEID(int32_t sceneID) noexcept { m_sceneID = sceneID; m_layerID = 0; } + constexpr LWOSCENEID(int32_t sceneID, uint32_t layerID) noexcept { m_sceneID = sceneID; m_layerID = layerID; } - LWOSCENEID& operator=(const LWOSCENEID& rhs) { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; } - LWOSCENEID& operator=(const int rhs) { m_sceneID = rhs; m_layerID = 0; return *this; } + constexpr LWOSCENEID& operator=(const LWOSCENEID& rhs) noexcept { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; } + constexpr LWOSCENEID& operator=(const int32_t rhs) noexcept { m_sceneID = rhs; m_layerID = 0; return *this; } - bool operator<(const LWOSCENEID& rhs) const { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); } - bool operator<(const int rhs) const { return m_sceneID < rhs; } + constexpr bool operator<(const LWOSCENEID& rhs) const noexcept { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); } + constexpr bool operator<(const int32_t rhs) const noexcept { return m_sceneID < rhs; } - bool operator==(const LWOSCENEID& rhs) const { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); } - bool operator==(const int rhs) const { return m_sceneID == rhs; } + constexpr bool operator==(const LWOSCENEID& rhs) const noexcept { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); } + constexpr bool operator==(const int32_t rhs) const noexcept { return m_sceneID == rhs; } - const int GetSceneID() const { return m_sceneID; } - const unsigned int GetLayerID() const { return m_layerID; } + constexpr int32_t GetSceneID() const noexcept { return m_sceneID; } + constexpr uint32_t GetLayerID() const noexcept { return m_layerID; } - void SetSceneID(const int sceneID) { m_sceneID = sceneID; } - void SetLayerID(const unsigned int layerID) { m_layerID = layerID; } + constexpr void SetSceneID(const int32_t sceneID) noexcept { m_sceneID = sceneID; } + constexpr void SetLayerID(const uint32_t layerID) noexcept { m_layerID = layerID; } private: - int m_sceneID; - unsigned int m_layerID; + int32_t m_sceneID; + uint32_t m_layerID; }; struct LWOZONEID { public: - const LWOMAPID& GetMapID() const { return m_MapID; } - const LWOINSTANCEID& GetInstanceID() const { return m_InstanceID; } - const LWOCLONEID& GetCloneID() const { return m_CloneID; } + constexpr const LWOMAPID& GetMapID() const noexcept { return m_MapID; } + constexpr const LWOINSTANCEID& GetInstanceID() const noexcept { return m_InstanceID; } + constexpr const LWOCLONEID& GetCloneID() const noexcept { return m_CloneID; } //In order: def constr, constr, assign op - LWOZONEID() { m_MapID = LWOMAPID_INVALID; m_InstanceID = LWOINSTANCEID_INVALID; m_CloneID = LWOCLONEID_INVALID; } - LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; } - LWOZONEID(const LWOZONEID& replacement) { *this = replacement; } + constexpr LWOZONEID() noexcept = default; + constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; } + constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; } private: - LWOMAPID m_MapID; //1000 for VE, 1100 for AG, etc... - LWOINSTANCEID m_InstanceID; //Instances host the same world, but on a different dWorld process. - LWOCLONEID m_CloneID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds. + LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc... + LWOINSTANCEID m_InstanceID = LWOINSTANCEID_INVALID; //Instances host the same world, but on a different dWorld process. + LWOCLONEID m_CloneID = LWOCLONEID_INVALID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds. }; -const LWOSCENEID LWOSCENEID_INVALID = -1; +constexpr LWOSCENEID LWOSCENEID_INVALID = -1; struct LWONameValue { uint32_t length = 0; //!< The length of the name std::u16string name; //!< The name - LWONameValue(void) {} + LWONameValue() = default; LWONameValue(const std::u16string& name) { this->name = name; this->length = static_cast(name.length()); } - - ~LWONameValue(void) {} }; struct FriendData { @@ -129,7 +128,7 @@ public: LWOOBJID friendID; std::string friendName; - void Serialize(RakNet::BitStream& bitStream) { + void Serialize(RakNet::BitStream& bitStream) const { bitStream.Write(isOnline); bitStream.Write(isBestFriend); bitStream.Write(isFTP); @@ -147,11 +146,11 @@ public: if (size > maxSize) size = maxSize; for (uint32_t i = 0; i < size; ++i) { - bitStream.Write(static_cast(friendName[i])); + bitStream.Write(friendName[i]); } for (uint32_t j = 0; j < remSize; ++j) { - bitStream.Write(static_cast(0)); + bitStream.Write(0); } bitStream.Write(0); //??? diff --git a/dCommon/dEnums/eGameMessageType.h b/dCommon/dEnums/eGameMessageType.h index d5751b51..e3fc22b6 100644 --- a/dCommon/dEnums/eGameMessageType.h +++ b/dCommon/dEnums/eGameMessageType.h @@ -3,1603 +3,1611 @@ #include +#include "magic_enum.hpp" + enum class eGameMessageType : uint16_t { - GET_POSITION = 0, - GET_ROTATION = 1, - GET_LINEAR_VELOCITY = 2, - GET_ANGULAR_VELOCITY = 3, - GET_FORWARD_VELOCITY = 4, - GET_PLAYER_FORWARD = 5, - GET_FORWARD_VECTOR = 6, - SET_POSITION = 7, - SET_LOCAL_POSITION = 8, - SET_ROTATION = 9, - SET_LINEAR_VELOCITY = 10, - MODIFY_LINEAR_VELOCITY = 11, - SET_ANGULAR_VELOCITY = 12, - MODIFY_ANGULAR_VELOCITY = 13, - DEFLECT = 14, - SEND_POSITION_UPDATE = 15, - SET_OBJECT_SCALE = 16, - GET_OBJECT_SCALE = 17, - TIMED_SCALE_FINISHED = 18, - TELEPORT = 19, - TOGGLE_PLAYER_FWD_TO_CAMERA = 20, - LOCK_PLAYER_ROT_TO_CAMERA = 21, - UNLOCK_PLAYER_ROT_FROM_CAMERA = 22, - TOGGLE_PLAYER_ROT_LOCK_TO_MOUSE = 23, - LOCK_PLAYER_ROT_TO_MOUSE = 24, - UNLOCK_PLAYER_ROT_FROM_MOUSE = 25, - SET_PLAYER_CONTROL_SCHEME = 26, - GET_PLAYER_CONTROL_SCHEME = 27, - RESET_PLAYER_CONTROL_SCHEME = 28, - PLAYER_TO_PREVIOUS_CONTROL_SCHEME = 29, - DROP_CLIENT_LOOT = 30, - GET_SPEED = 34, - GET_ROT_SPEED = 35, - IS_DEAD = 36, - DIE = 37, - REQUEST_DIE = 38, - ADD_OBJECT = 39, - PLAY_EMOTE = 41, - PRELOAD_ANIMATION = 42, - PLAY_ANIMATION = 43, - ANIMATION_COMPLETE = 44, - ENABLE_HIGHLIGHT = 45, - DISABLE_HIGHLIGHT = 46, - GET_ANIMATION_NAMES = 47, - CONTROL_BEHAVIORS = 48, - BLEND_PRIMARY_ANIMATION = 52, - SET_OFFSCREEN_ANIMATION = 53, - GET_MOVEMENT_INPUT_VALUES = 54, - SWAP_TEXTURE = 55, - SWAP_COLOR = 56, - ATTACH_HAIR = 57, - GET_ENTITY_STRUCT = 58, - SET_ENTITY_STRUCT = 59, - SET_ATTR = 60, - GET_ATTR = 61, - ON_HIT = 62, - HIT_OR_HEAL_RESULT = 63, - SHOW_ATTACK = 64, - GO_TO = 65, - GET_CONFIG_DATA = 66, - SET_CONFIG_DATA = 68, - GET_INVENTORY_EXTRA_INFO = 69, - GET_DISPLAY_NAME = 70, - GET_NAME = 71, - SET_NAME = 72, - IS_NAME_LOCALIZED = 73, - GET_HAIR_COLOR = 74, - SET_HAIR_COLOR = 75, - GET_HAIR_STYLE = 76, - SET_HAIR_STYLE = 77, - GET_HEAD = 78, - SET_HEAD = 79, - GET_TORSO = 80, - SET_TORSO = 81, - GET_LEGS = 82, - SET_LEGS = 83, - SET_PROXIMITY_RADIUS = 84, - PROXIMITY_UPDATE = 85, - GET_PROXIMITY_OBJECTS = 86, - UNSET_PROXIMITY_RADIUS = 87, - CLEAR_PROXIMITY_RADIUS = 88, - GET_PROXIMITY_DATA = 89, - SET_PROXIMITY_RADIUS_ICON = 90, - TOGGLE_TAC_ARC = 93, - CAST_SKILL = 95, - CAST_LOCAL_SKILL = 96, - ECHO_LOCAL_SKILL = 97, - QUEUE_AI_SKILL = 98, - ADD_THREAT_RATING = 99, - GET_THREAT_RATING = 100, - CLEAR_THREAT_LIST = 103, - GET_TIME_FOR_NPC_SKILL = 111, - ENEMY_HEAL_NOTIFICATION = 112, - RESET_SCRIPTED_AI_STATE = 113, - ENABLE_COMBAT_AI_COMPONENT = 114, - COMBAT_AI_FORCE_TETHER = 115, - SUSPEND_MOVEMENT_AI = 116, - NOTIFY_SCRIPT_VARS_INITIALIZED = 117, - ECHO_START_SKILL = 118, - START_SKILL = 119, - CASTER_DEAD = 120, - VERIFY_ACK = 121, - ADD_PENDING_VERIFY = 122, - MAP_SKILL = 123, - SELECT_SKILL = 124, - CAST_ACTIVE_SKILL = 125, - MODIFY_SKILL_COOLDOWN = 126, - ADD_SKILL = 127, - REMOVE_SKILL = 128, - LOG = 129, - LOG_CHAT = 130, - SET_MAX_CURRENCY = 131, - GET_MAX_CURRENCY = 132, - SET_CURRENCY = 133, - GET_CURRENCY = 134, - ADD_PENDING_CURRENCY = 136, - PICKUP_CURRENCY = 137, - SERVER_DELETE_LOOT_ITEM = 138, - PICKUP_ITEM = 139, - TEAM_PICKUP_ITEM = 140, - CLIENT_DELETE_LOOT_ITEM = 141, - CLIENT_SET_LOOT_ITEM_FFA = 143, - COLLISION_PHANTOM = 144, - OFF_COLLISION_PHANTOM = 145, - COLLISION_PROXIMITY = 146, - OFF_COLLISION_PROXIMITY = 147, - COLLISION = 148, - OFF_COLLISION = 149, - GET_SKILLS = 150, - CLEAR_FX_SINGLE_EFFECT = 152, - GET_FX_EXIST_EFFECT = 153, - PLAY_FX_EFFECT = 154, - STOP_FX_EFFECT = 155, - CLEAR_FX_ALL_CREATE_EFFECTS = 156, - UPDATE_FX_ALL_CREATE_EFFECTS = 157, - REQUEST_RESURRECT = 159, - RESURRECT = 160, - UPDATE_FROM_GHOST = 162, - FETCH_GHOST = 163, - KFM_LOADED = 164, - NIF_LOADED = 165, - HKX_LOADED = 166, - MOVE_TO_DELETE_QUEUE = 167, - RESTORE_FROM_DELETE_QUEUE = 168, - IS_ENEMY = 169, - GET_FACTION = 170, - SET_IMAGINATION = 171, - GET_IMAGINATION = 172, - SET_MAX_IMAGINATION = 173, - GET_MAX_IMAGINATION = 174, - MODIFY_IMAGINATION = 175, - MODIFY_MAX_IMAGINATION = 176, - SET_HEALTH = 177, - GET_HEALTH = 178, - SET_MAX_HEALTH = 179, - GET_MAX_HEALTH = 180, - MODIFY_HEALTH = 181, - MODIFY_MAX_HEALTH = 182, - SET_ARMOR = 183, - GET_ARMOR = 184, - SET_MAX_ARMOR = 185, - GET_MAX_ARMOR = 186, - MODIFY_ARMOR = 187, - MODIFY_MAX_ARMOR = 188, - POP_HEALTH_STATE = 190, - PUSH_EQUIPPED_ITEMS_STATE = 191, - POP_EQUIPPED_ITEMS_STATE = 192, - SET_GM_LEVEL = 193, - GET_GM_LEVEL = 194, - ADD_STATUS_EFFECT = 196, - REMOVE_STATUS_EFFECT = 197, - SET_STUNNED = 198, - GET_STUNNED = 199, - SET_STUN_IMMUNITY = 200, - GET_STUN_IMMUNITY = 201, - KNOCKBACK = 202, - SET_VISIBLE = 203, - GET_VISIBLE = 204, - REPORT_ITEM_INFO = 205, - GET_REBUILD_STATE = 207, - REBUILD_CANCEL = 209, - REBUILD_START = 211, - ENABLE_REBUILD = 213, - SKILL_FAILURE = 214, - IS_ATTACK_STANCE = 216, - SET_OBJECT_RENDER = 217, - REQUEST_MAPPED_SKILLS = 218, - UI_SELECT_MAPPED_SKILL = 219, - GET_INVENTORY_ITEM_IN_SLOT = 220, - GET_FIRST_INVENTORY_ITEM_BY_LOT = 221, - GET_SMALLEST_INVENTORY_STACK_BY_LOT = 222, - MOVE_ITEM_IN_INVENTORY = 224, - ADD_ITEM_TO_INVENTORY_CLIENT_SYNC = 227, - GET_EQUIPPED_ITEMS = 229, - REMOVE_ITEM_FROM_INVENTORY = 230, - EQUIP_INVENTORY = 231, - UN_EQUIP_INVENTORY = 233, - EQUIP_ITEM = 234, - UN_EQUIP_ITEM = 235, - IS_ITEM_RESPOND = 236, - IS_ITEM_EQUIPPED = 237, - ATTACH_ITEM = 238, - DETACH_ITEM = 239, - GET_NODE = 240, - GET_LOT = 241, - IS_ITEM_EQUIPPABLE = 242, - GET_CURRENT_ANIMATION = 243, - GET_INV_ITEM_COUNT = 244, - POST_LOAD_EQUIP = 245, - SET_PHYSICS_ACTIVE_STATE = 246, - GET_CURRENT_SKILL_TAC_ARC = 247, - OFFER_MISSION = 248, - RESPOND_TO_MISSION = 249, - GET_MISSION_STATE = 250, - GET_MISSION_COMPLETE_TIMESTAMP = 251, - NOTIFY_MISSION = 254, - NOTIFY_MISSION_TASK = 255, - ARE_GFX_LOADED = 257, - ADDED_TO_WORLD = 258, - REMOVE_EXTRA_GFX_FROM_PIPE = 259, - HIDE_EQUIPED_WEAPON = 260, - UN_HIDE_EQUIPED_WEAPON = 261, - GET_ITEM_SLOT = 262, - IS_CHARACTER = 263, - SET_IMMUNITY = 264, - TOGGLE_TOOLTIPS = 266, - GET_TOOLTIPS_DISABLED = 267, - GET_BOUNDING_INFO = 268, - OVERRIDE_BOUNDING_RADIUS = 269, - GET_OFFSCREEN = 270, - USE_STATE_MACHINE = 271, - ADD_STATE = 272, - ADD_SUB_STATE = 273, - SET_STATE = 274, - SET_SUB_STATE = 275, - ADD_MESSAGE = 276, - RELOAD_SCRIPT = 277, - RELOAD_ALL_SCRIPTS = 278, - FRIEND_INVITE_MSG = 279, - ADD_FRIEND_REPOSNSE_MSG = 280, - REMOVE_FRIEND_RESPONSE_MSG = 281, - ADD_FRIEND_FROM_UI_MSG = 282, - GET_CACHED_FRIENDS_LIST_MSG = 283, - REQUEST_NEW_FRIENDS_LIST_MSG = 284, - REPOPULATE_FRIENDS_LIST_MSG = 285, - ADD_IGNORE_REPONSE_MSG = 286, - REMOVE_IGNORE_RESPONSE_MSG = 287, - ADD_IGNORE_FROM_UI_MSG = 288, - GET_CACHED_IGNORE_LIST_MSG = 289, - REQUEST_NEW_IGNORE_LIST_MSG = 290, - REMOVE_FRIEND_BY_NAME = 291, - REMOVE_IGNORE_BY_NAME = 292, - IS_PLAYER_IN_IGNORE_LIST_MSG = 293, - REPOPULATE_IGNORE_LIST_MSG = 294, - GET_INVENTORY_LIST = 295, - UPDATE_FRIEND_MSG = 296, - UPDATE_FRIEND_NAME_MSG = 297, - UPDATE_IGNORE_NAME_MSG = 298, - DEPARTED = 299, - ARRIVED = 300, - TEMPLATE_CHANGE_WAYPOINTS = 301, - CANCELLED = 302, - FLUSH_CACHED_GRAPHICS = 303, - FOLLOW_TARGET = 304, - TIMER_DONE = 305, - TIMER_CANCELLED = 306, - SET_TETHER_POINT = 307, - GET_TETHER_POINT = 308, - LEFT_TETHER_RADIUS = 309, - GET_SCRIPT_VARS_PTR = 310, - FACE_TARGET = 311, - ROTATE_BY_DEGREES = 312, - STRING_RENDERED = 313, - RESET_PRIMARY_ANIMATION = 314, - FACE_PLAY_STREAM = 315, - TORSO_PLAY_STREAM = 316, - CAN_PICKUP = 317, - GET_INVENTORY_SIZE = 318, - GET_INVENTORY_COUNT = 319, - GET_OBJECTS_IN_GROUP = 320, - HIDE_ITEM = 321, - IS_OBJECT_IN_FOV = 322, - GET_TYPE = 323, - TEAM_INVITE_MSG = 324, - TEAM_GET_SIZE = 325, - TEAM_REQUEST_SET_LOOT = 326, - TEAM_REMOVE_PLAYER_MSG = 327, - TEAM_UPDATE_PLAYER_NAME_MSG = 328, - SET_UPDATABLE = 329, - REQUEST_TEAM_UI_UPDATE = 330, - SET_COLLISION_GROUP = 331, - GET_COLLISION_GROUP = 332, - GET_ORIGINAL_COLLISION_GROUP = 333, - SET_COLLISION_GROUP_TO_ORIGINAL = 334, - GET_OBJECT_RADIUS = 335, - REBUILD_NOTIFY_STATE = 336, - GET_PLAYER_INTERACTION = 337, - SET_PLAYER_INTERACTION = 338, - FORCE_PLAYER_TO_INTERACT = 339, - GET_SELECTED_POTENTIAL_INTERACTION = 340, - SET_SELECTED_POTENTIAL_INTERACTION = 341, - GET_INTERACTION_DISTANCE = 342, - SET_INTERACTION_DISTANCE = 343, - CALCULATE_INTERACTION_DISTANCE = 344, - INTERACTION_ATTEMPT_FROM_OUT_OF_RANGE = 345, - SET_PICKING_TARGET = 346, - CLIENT_UNUSE = 347, - BEGIN_PET_INTERACTION = 348, - WANTS_INTERACTION_ICON = 349, - PROPERTY_EDIT_ICON_INTERACTION = 350, - PROPERTY_MODEL_INTERACTION = 351, - GET_INTERACTION_DETAILS = 352, - GET_DISABLED_INTERACTION_TYPES = 353, - GET_INTERACTION_INFO = 354, - INTERACTION_GAME_STATE_CHANGE = 355, - TOGGLE_INTERACTION_UPDATES = 356, - TERMINATE_INTERACTION = 357, - SERVER_TERMINATE_INTERACTION = 358, - GET_PLAYERS_TARGET_FOR_SELECTION = 359, - PROCESS_INTERACTION_UNDER_CURSOR = 360, - HANDLE_INTERACT_ACTION = 361, - ATTEMPT_INTERACTION = 362, - HANDLE_INTERACTION_CAMERA = 363, - REQUEST_USE = 364, - CLIENT_USE = 366, - GET_PLAYER_MULTI_INTERACTION = 367, - GET_MULTI_INTERACTION_STATE = 368, - VENDOR_OPEN_WINDOW = 369, - VENDOR_CLOSE_WINDOW = 370, - EMOTE_PLAYED = 371, - EMOTE_RECEIVED = 372, - BUY_FROM_VENDOR = 373, - SELL_TO_VENDOR = 374, - ADD_DONATION_ITEM = 375, - REMOVE_DONATION_ITEM = 376, - CONFIRM_DONATION_ON_PLAYER = 378, - CANCEL_DONATION_ON_PLAYER = 379, - TEAM_GET_LEADER = 380, - TEAM_GET_ON_WORLD_MEMBERS = 381, - TEAM_GET_ALL_MEMBERS = 382, - TEAM_SET_OFF_WORLD_FLAG = 383, - SET_TRANSPARENCY = 385, - GET_PREFERS_FADE = 386, - PROJECTILE_IMPACT = 387, - SET_PROJECTILE_PARAMS = 388, - SET_INVENTORY_SIZE = 389, - ACKNOWLEDGE_POSSESSION = 391, - SET_POSSESSED_OBJECT = 392, - CHANGE_POSSESSOR = 393, - GET_POSSESSION_TYPE = 395, - GET_POSSESSED_OBJECT = 396, - GET_POSSESSOR = 397, - IS_POSSESSED = 398, - ENABLE_ACTIVITY = 399, - SET_SHOOTING_GALLERY_PARAMS = 400, - OPEN_ACTIVITY_START_DIALOG = 401, - REQUEST_ACTIVITY_START_STOP = 402, - REQUEST_ACTIVITY_ENTER = 403, - REQUEST_ACTIVITY_EXIT = 404, - ACTIVITY_ENTER = 405, - ACTIVITY_EXIT = 406, - ACTIVITY_START = 407, - ACTIVITY_STOP = 408, - SHOOTING_GALLERY_CLIENT_AIM_UPDATE = 409, - ROTATE_TO_POINT = 410, - SHOOTING_GALLERY_FIRE = 411, - CALCULATE_FIRING_PARAMETERS = 412, - GET_MUZZLE_OFFSET = 413, - GET_ACTIVITY_POINTS = 414, - TEAM_IS_ON_WORLD_MEMBER = 415, - REQUEST_VENDOR_STATUS_UPDATE = 416, - VENDOR_STATUS_UPDATE = 417, - CANCEL_MISSION = 418, - RESET_MISSIONS = 419, - RENDER_COMPONENT_READY = 420, - SEND_MINIFIG_DECALS = 421, - PHYSICS_COMPONENT_READY = 422, - ENTER_STANDBY_MODE = 423, - LEAVE_STANDBY_MODE = 424, - NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE = 425, - REQUEST_CONSUME_ITEM = 426, - CONSUME_CLIENT_ITEM = 427, - CLIENT_ITEM_CONSUMED = 428, - QUERY_STANDBY_MODE = 429, - GET_NI_BOUND = 430, - MISSION_FAILURE = 431, - GET_ANIMATION_TIME = 432, - GET_CURRENT_ACTIVITY = 434, - SET_EYEBROWS = 435, - GET_EYEBROWS = 436, - SET_EYES = 437, - GET_EYES = 438, - SET_MOUTH = 439, - GET_MOUTH = 440, - IS_OBJECT_SMASHABLE = 441, - SMASHABLE_STATE_CHANGED = 443, - USE_STATE_LOGGER = 444, - ROTATE_SUB_NODE = 445, - GET_SUB_NODE_POSITION = 446, - GET_SUB_NODE = 447, - UPDATE_SHOOTING_GALLERY_ROTATION = 448, - RENDER_FLOATING_TEXT = 449, - REQUEST2_D_TEXT_ELEMENT = 450, - UPDATE2_D_TEXT_ELEMENT = 451, - REMOVE2_D_TEXT_ELEMENT = 452, - SET_COLOR = 453, - GET_COLOR = 454, - HKX_CHARACTER_LOADED = 455, - ACTIVATE_PHYSICS = 457, - SET_ICON_ABOVE_HEAD = 458, - ADD_ICON_COMPOSITE = 459, - CLEAR_ICON_COMPOSITES = 460, - ICON_NIF_LOADED = 461, - ICON_KFM_LOADED = 462, - GET_OVERHEAD_ICON_PROPERTIES_FROM_PARENT = 463, - BOUNCE_PLAYER = 464, - SET_USER_CTRL_COMP_PAUSE = 466, - HAS_COLLIDED = 467, - GET_TOOLTIP_FLAG = 468, - SET_TOOLTIP_FLAG = 469, - GET_FLAG = 470, - SET_FLAG = 471, - NOTIFY_CLIENT_FLAG_CHANGE = 472, - CURSOR_ON = 473, - CURSOR_OFF = 474, - HELP = 475, - VENDOR_TRANSACTION_RESULT = 476, - PERFORM_SPECIAL_DEATH = 477, - GET_SHADER_ID = 478, - GET_RENDER_ENVIRONMENT = 479, - FINISHED_LOADING_SCENE = 480, - GET_SKILL_INFO = 481, - ACTIVITY_CANCEL = 482, - MISSION_USES_OBJECT = 483, - GET_POSITIONAL_ID = 484, - SET_COLLECTIBLE_STATUS = 485, - HAS_BEEN_COLLECTED = 486, - HAS_BEEN_COLLECTED_BY_CLIENT = 487, - GET_POS_UPDATE_STATS = 488, - GET_NUM_VIEWERS_SCOPING_THIS = 489, - GET_ACTIVITY_USER = 490, - GET_ALL_ACTIVITY_USERS = 491, - GET_MISSION_FOR_PLAYER = 492, - SET_FACTION = 493, - SET_PLATFORM_IDLE_STATE = 494, - DISPLAY_CHAT_BUBBLE = 495, - REQUEST_CHAT_BUBBLE_ELEMENT = 496, - GET_MISSION_DATA = 497, - SPAWN_PET = 498, - DESPAWN_PET = 499, - SET_LOCAL_SPACE_STATE = 500, - GET_LOCAL_SPACE_STATE = 501, - SET_POSITION_TO_LOCAL_POSITION = 502, - ALLOW_LOCAL_SPACE_UPDATE = 503, - TOGGLE_FREE_CAM_MODE = 504, - PLAYER_LOADED = 505, - PLAYER_ADDED_TO_WORLD_LOCAL = 506, - OBJECT_LOADED = 507, - GET_PLAYER_READY = 508, - PLAYER_READY = 509, - SET_SMASHABLE_PARAMS = 510, - IS_LOOTABLE_CHEST = 511, - LOOT_OPEN_WINDOW = 512, - LOOT_SELECTION_UPDATE = 513, - TAKE_LOOT_CHEST_ITEM = 514, - REQUEST_LINKED_MISSION = 515, - TRANSFER_TO_ZONE = 516, - TRANSFER_TO_ZONE_CHECKED_IM = 517, - SECURED_TRANSFER_TO_ZONE = 518, - INVALID_ZONE_TRANSFER_LIST = 519, - MISSION_DIALOGUE_OK = 520, - GET_OBJECT_IN_SCOPE = 521, - SET_LAUNCHED_STATE = 522, - P_CREATE_EFFECT_FINISHED = 523, - SMASHED_OBJECT = 524, - CHECK_SMASHCHAIN_OVERRIDE = 525, - DISPLAY_REBUILD_ACTIVATOR = 526, - TRANSFER_TO_LAST_NON_INSTANCE = 527, - SET_ACTIVE_LOCAL_CHARACTER_ID = 528, - DISPLAY_MESSAGE_BOX = 529, - MESSAGE_BOX_RESPOND = 530, - CHOICE_BOX_RESPOND = 531, - SERVER_SET_USER_CTRL_COMP_PAUSE = 532, - SET_CHARACTER_AUTO_RUN = 533, - FOLLOW_WAYPOINTS = 534, - SWAP_DECAL_AND_COLOR = 535, - CONTINUE_WAYPOINTS = 536, - SMASH = 537, - UN_SMASH = 538, - GET_IS_SMASHED = 539, - GET_UP_VECTOR = 540, - SET_GRAVITY_SCALE = 541, - SET_GRAVITY_SCALE_FOR_RIGID_BODY = 542, - STOP_MOVING = 543, - SET_PATHING_SPEED = 544, - SET_SHIELDED = 545, - SET_SHOOTING_GALLERY_RETICULE_EFFECT = 546, - PLACE_MODEL_RESPONSE = 547, - SET_DODGE_INFO = 548, - GET_DODGE_INFO = 549, - SET_SKILL_ATTACK_SPEED = 550, - GET_SKILL_COOLDOWN_GROUP = 551, - GET_INITIAL_SKILL_COOLDOWN = 552, - GET_SKILL_COOLDOWN_REMAINING = 553, - GET_GLOBAL_COOLDOWN = 554, - SET_GLOBAL_COOLDOWN = 555, - RESET_GLOBAL_COOLDOWN = 556, - FINDINVENTORY_ITEM = 558, - PATH_STUCK = 559, - SET_CURRENT_PATH = 560, - SET_JET_PACK_MODE = 561, - SET_JET_PACK_TIME = 562, - PET_FOLLOW_OWNER = 563, - PLAYER_DIED = 564, - REGISTER_PET_ID = 565, - REGISTER_PET_DBID = 566, - GET_PET_ID = 567, - SHOW_ACTIVITY_COUNTDOWN = 568, - DISPLAY_TOOLTIP = 569, - SET_PHANTOM_BASE = 570, - GET_MOTION_STATE = 571, - GET_MOTION_CONFIG = 572, - SET_ACTIVE_PROJECTILE_SKILL = 573, - INITIALIZE_MISSION_VISUALS = 574, - GET_MISSIONS = 575, - START_ACTIVITY_TIME = 576, - ADD_ACTIVITY_TIME = 577, - GUILD_GET_SIZE = 578, - GUILD_CAN_WE_INVITE = 579, - GUILD_CAN_WE_KICK = 580, - SET_CHAR_GUILD_INFO = 581, - GET_CHAR_GUILD_INFO = 582, - GET_CHAR_IS_IN_GUILD = 583, - RE_RENDER_NAME_BILLBOARD = 584, - IS_IN_LOCAL_CHAR_PROXIMITY = 585, - GUILD_SET_STATUS = 586, - GUILD_ADD_PLAYER = 587, - GUILD_REMOVE_PLAYER = 588, - GUILD_UPDATE_PLAYER_NAME = 589, - GUILD_SET_PLAYER_RANK = 590, - GUILD_SET_ONLINE_STATUS = 591, - GUILD_INVITE = 592, - REQUEST_GUILD_DATA = 593, - POPULATE_GUILD_DATA = 594, - GET_CACHED_GUILD_DATA = 595, - GUILD_RENDER_NAME = 596, - GET_IS_SUPPORTED = 600, - CHARACTER_SUPPORT_CHANGED = 601, - ACTIVITY_PAUSE = 602, - USE_NON_EQUIPMENT_ITEM = 603, - REQUEST_USE_ITEM_ON = 604, - REQUEST_USE_ITEM_ON_TARGET = 605, - USE_ITEM_ON = 606, - USE_ITEM_RESULT = 607, - GET_PARENT_OBJ = 608, - SET_PARENT_OBJ = 609, - GET_UPDATES_WITH_PARENT_POSITION = 610, - PARENT_REMOVED = 611, - PARENT_LEFT_SCOPE = 612, - PARENT_ENTERED_SCOPE = 613, - CHILD_LOADED = 614, - CHILD_REMOVED = 615, - CHILD_DETACHED = 616, - CHILD_ENTERED_SCOPE = 617, - CHILD_LEFT_SCOPE = 618, - GET_CHILD_OBJECTS = 619, - ZONE_TRANSFER_FINISHED = 621, - CHAT_CONNECTION_UPDATE = 622, - PLATFORM_AT_LAST_WAYPOINT = 623, - LOOT_TAKE_ALL = 624, - GET_EQUIPPED_ITEM_INFO = 625, - DISPLAY_GUILD_CREATE_BOX = 626, - GET_EDITOR_LEVEL = 627, - GET_ACCOUNT_ID = 628, - GET_LAST_LOGOUT = 629, - GET_LAST_PROP_MOD_DISPLAY_TIME = 630, - SET_LAST_PROP_MOD_DISPLAY_TIME = 631, - SHOW_ACTIVITY_SUMMARY = 632, - CAN_RECEIVE_ALL_REWARDS = 633, - GET_ACTIVITY_REWARD = 634, - LOOT_CLOSE_WINDOW = 635, - GET_BLUEPRINT_ID = 636, - NOTIFY_BLUEPRINT_UPDATE = 637, - FETCH_MODEL_METADATA_REQUEST = 638, - FETCH_MODEL_METADATA_RESPONSE = 639, - COMMAND_PET = 640, - PET_RESPONSE = 641, - GET_ICON_ABOVE_HEAD_STATE = 642, - GET_ICON_ABOVE_HEAD = 643, - ICON_FINISHED_LOADING = 644, - ADD_PET_STATE = 645, - REMOVE_PET_STATE = 646, - SET_PET_STATE = 647, - REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA = 648, - SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA = 649, - SET_ON_TEAM = 650, - GET_PET_HAS_STATE = 651, - FIND_PROPERTY = 652, - SET_PET_MOVEMENT_STATE = 653, - GET_ITEM_TYPE = 654, - GET_ITEM_INFO_KEY = 655, - NOTIFY_OBJECT = 656, - IS_PET_WILD = 657, - CLIENT_NOTIFY_PET = 659, - NOTIFY_PET = 660, - NOTIFY_PET_TAMING_MINIGAME = 661, - START_SERVER_PET_MINIGAME_TIMER = 662, - CLIENT_EXIT_TAMING_MINIGAME = 663, - GET_BUILDMODE_ACTIVE = 664, - GET_PET_TAMING_MINIGAME_ACTIVE = 665, - PET_TAMING_OBJECT_PICKED = 666, - PET_TAMING_MINIGAME_RESULT = 667, - PET_TAMING_TRY_BUILD_RESULT = 668, - SET_PET_TAMING_MODEL = 669, - GET_PET_TAMING_MODEL = 670, - PET_ON_SWITCH = 671, - PET_OFF_SWITCH = 672, - NOTIFY_TAMING_BUILD_SUCCESS = 673, - NOTIFY_TAMING_MODEL_LOADED_ON_SERVER = 674, - NOTIFY_TAMING_PUZZLE_SELECTED = 675, - GET_INSTRUCTION_COUNT = 676, - GET_IS_NPC = 677, - ACTIVATE_BUBBLE_BUFF = 678, - DECTIVATE_BUBBLE_BUFF = 679, // thanks netdevil - EXHIBIT_VOTE = 680, - ADD_PET_TO_PLAYER = 681, - REMOVE_PET_FROM_PLAYER = 682, - REQUEST_SET_PET_NAME = 683, - SET_PET_NAME = 684, - PET_NAME_CHANGED = 686, - GET_PET_AT_INDEX = 687, - GET_LOT_FOR_PET_BY_DBID = 688, - GET_NAME_FOR_PET_BY_DBID = 689, - GET_ACTIVE_PET_OBJ_ID = 690, - GET_ACTIVE_PET_INVENTORY_OBJ_ID = 691, - SHOW_PET_ACTION_BUTTON = 692, - SET_EMOTE_LOCK_STATE = 693, - GET_EMOTE_LOCK_STATE = 694, - LEAVE_TEAM_MSG = 695, - TEAM_KICK_PLAYER_MSG = 697, - TEAM_SET_LEADER_SEND_MSG = 698, - USE_ITEM_ON_CLIENT = 699, - DOES_FORWARD_TARGET_CLICKING = 700, - CHECK_USE_REQUIREMENTS = 701, - USE_REQUIREMENTS_RESPONSE = 702, - USE_ITEM_REQUIREMENTS_RESPONSE = 703, - PET_ADDED_TO_WORLD = 704, - BOUNCER_TRIGGERED = 705, - EXHIBIT_QUERY_CURRENT_MODEL = 706, - EXHIBIT_QUERY_CURRENT_MODEL_RESPONSE = 707, - EXHIBIT_ATTEMPT_VOTE = 708, - EXHIBIT_VOTE_RESPONSE = 709, - EHIBIT_REQUERYMODELS = 710, - IS_SKILL_ACTIVE = 711, - TOGGLE_ACTIVE_SKILL = 712, - PLAY_EMBEDDED_EFFECT_ON_ALL_CLIENTS_NEAR_OBJECT = 713, - EXHIBIT_GET_INFO = 714, - GET_PROPERTY_DATA = 715, - DOWNLOAD_PROPERTY_DATA = 716, - QUERY_PROPERTY_DATA = 717, - MODEL_MODERATION_ACTION = 719, - NOTIFY_SERVER_UGC_REVIEW_READY = 720, - NOTIFY_CLIENT_UGC_REVIEW_READY = 721, - OLD_USE_ITEM_ON = 722, - FIND_PROPERTY_FOR_SALE_RESPONSE = 723, - PROPERTY_EDITOR_BEGIN = 724, - PROPERTY_EDITOR_END = 725, - PROPERTY_EDITOR_SET_MODE = 726, - TOGGLE_TRIGGER = 727, - FIRE_EVENT = 728, - IS_MINIFIG_IN_A_BUBBLE = 729, - GET_ITEM_INFO = 730, - MISSION_NEEDS_LOT = 731, - STOP_PATHING = 732, - START_PATHING = 733, - ACTIVATE_BUBBLE_BUFF_FROM_SERVER = 734, - DEACTIVATE_BUBBLE_BUFF_FROM_SERVER = 735, - HAS_SKILL = 736, - NOTIFY_CLIENT_ZONE_OBJECT = 737, - MOVE_OBJECT = 738, - ROTATE_OBJECT = 739, - GET_SPAWNER_CONFIG_DATA = 740, - UPDATE_SPAWNER_CONFIG_DATA = 741, - TURN_AROUND = 743, - GO_FORWARD = 744, - GO_BACKWARD = 745, - UPDATE_REPUTATION = 746, - GET_REPUTATION = 747, - ADD_REPUTATION = 748, - UPDATE_PROPERTY_DATA = 749, - PROPERTY_RENTAL_RESPONSE = 750, - EXHIBIT_PLACEMENT_RESPONSE = 751, - SQUIRT_WITH_WATERGUN = 752, - GET_VOTES_LEFT = 753, - ADJUST_VOTES_LEFT = 754, - EVADE_TARGET = 755, - STOPPED_EVADING = 756, - GET_PET_HAS_ABILITY = 757, - REQUEST_PLATFORM_RESYNC = 760, - PLATFORM_RESYNC = 761, - PLAY_CINEMATIC = 762, - END_CINEMATIC = 763, - CINEMATIC_UPDATE = 764, - ATTACH_CAMERA_TO_RAIL = 765, - DETACH_CAMERA_FROM_RAIL = 766, - TOGGLE_GHOST_REFERENCE_OVERRIDE = 767, - SET_GHOST_REFERENCE_POSITION = 768, - GET_GHOST_REFERENCE_POSITION = 769, - FIRE_EVENT_SERVER_SIDE = 770, - GET_PET_ABILITY_OBJECT = 771, - TEAM_INVITE_MSG_FROM_UI = 772, - ADD_CAMERA_EFFECT = 773, - REMOVE_CAMERA_EFFECT = 774, - REMOVE_ALL_CAMERA_EFFECTS = 775, - GET_MY_PROPERTIES_IN_THIS_ZONE = 776, - IS_MODEL_WITHIN_PROPERTY_BOUNDS = 777, - PROPERTY_DATA_RESULTS = 778, - ON_UN_SERIALIZE = 779, - SCRIPT_NETWORK_VAR_UPDATE = 781, - ADD_OBJECT_TO_GROUP = 783, - REMOVE_OBJECT_FROM_GROUP = 784, - IS_OBJECT_STATIC = 785, - GET_HAS_MISSION = 786, - GET_MISSION_TARGET_LOT = 787, - GET_MISSION_OFFERER_LOT = 788, - USE_UNIQUE_ITEM = 789, - GET_IS_PET = 790, - DELETE_PROPERTY = 791, - CREATEMODEL_FROM_CLIENT = 792, - UPDATE_MODEL_FROM_CLIENT = 793, - DELETE_MODEL_FROM_CLIENT = 794, - SHOW_PROPERTY_BOUNDS = 795, - SET_PROPERTY_I_DS = 796, - PLAY_FACE_DECAL_ANIMATION = 797, - ADD_ACTIVITY_USER = 798, - REMOVE_ACTIVITY_USER = 799, - GET_NUM_ACTIVITY_USERS = 800, - ACTIVITY_USER_EXISTS = 801, - DO_COMPLETE_ACTIVITY_EVENTS = 805, - SET_ACTIVITY_PARAMS = 806, - SET_ACTIVITY_USER_DATA = 807, - GET_ACTIVITY_USER_DATA = 808, - DO_CALCULATE_ACTIVITY_RATING = 809, - ND_AUDIO_POST_SETUP = 812, - ND_AUDIO_PRE_SHUTDOWN = 813, - SET_ND_AUDION_LISTENER_STANCE = 814, - SET_UP_ND_AUDIO_EMIITTER = 815, - SHUT_DOWN_ND_AUDIO_EMITTER = 816, - METAIFY_ND_AUDIO_EMITTER = 817, - UN_METAIFY_ND_AUDIO_EMITTER = 818, - METAIFY_ND_AUDIO_EMITTERS = 819, - UN_METAIFY_ND_AUDIO_EMITTERS = 820, - PLAY_ND_AUDIO_EMITTER = 821, - STOP_ND_AUDIO_EMITTER = 822, - STOP_ND_AUDIO_EMITTER_ALL = 823, - SET_ND_AUDIO_EMITTER_PARAMETER = 824, - SET_ND_AUDIO_EMITTERS_PARAMETER = 825, - ND_AUDIO_CALLBACK = 826, - ACTIVATE_ND_AUDIO_MUSIC_CUE = 827, - DEACTIVATE_ND_AUDIO_MUSIC_CUE = 828, - FLASH_ND_AUDIO_MUSIC_CUE = 829, - SET_ND_AUDIO_MUSIC_PARAMETER = 830, - PLAY2_D_AMBIENT_SOUND = 831, - STOP2_D_AMBIENT_SOUND = 832, - PLAY3_D_AMBIENT_SOUND = 834, - STOP3_D_AMBIENT_SOUND = 835, - ACTIVATE_ND_AUDIO_MIXER_PROGRAM = 836, - DEACTIVATE_ND_AUDIO_MIXER_PROGRAM = 837, - UPDATE_ACTIVITY_LEADERBOARD = 838, - ACTIVITY_LEADERBOARD_UPDATED = 839, - ENTER_PROPERTY1 = 840, - ENTER_PROPERTY2 = 841, - PROPERTY_ENTRANCE_SYNC = 842, - SEND_PROPERTY_POPULATION_TO_CLIENT = 843, - SEN_PROPERTY_PLAQUE_VIS_UPDATE = 844, - PROPERTY_SELECT_QUERY = 845, - CREATE_POSITION_STRING = 848, - GET_PARALLEL_POSITION = 849, - PARSE_CHAT_MESSAGE = 850, - SET_MISSION_TYPE_STATE = 851, - GET_LOCATIONS_VISITED = 852, - GET_MISSION_TYPE_STATES = 853, - GET_TIME_PLAYED = 854, - SET_MISSION_VIEWED = 855, - SLASH_COMMAND_TEXT_FEEDBACK = 856, - HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857, - BROADCAST_TEXT_TO_CHATBOX = 858, - OPEN_PROPERTY_MANAGEMENT = 860, - OPEN_PROPERTY_VENDOR = 861, - VOTE_ON_PROPERTY = 862, - UPDATE_PROPERTY_OR_MODEL_FOR_FILTER_CHECK = 863, - NOTIFY_PLAYER_OF_PROPERTY_SUBMISSION = 865, - NOTIFY_PLAYER_OF_MODEL_SUBMISSION = 866, - PHYSICS_SYSTEM_LOADED = 867, - CLIENT_TRADE_REQUEST = 868, + GET_POSITION = 0, + GET_ROTATION = 1, + GET_LINEAR_VELOCITY = 2, + GET_ANGULAR_VELOCITY = 3, + GET_FORWARD_VELOCITY = 4, + GET_PLAYER_FORWARD = 5, + GET_FORWARD_VECTOR = 6, + SET_POSITION = 7, + SET_LOCAL_POSITION = 8, + SET_ROTATION = 9, + SET_LINEAR_VELOCITY = 10, + MODIFY_LINEAR_VELOCITY = 11, + SET_ANGULAR_VELOCITY = 12, + MODIFY_ANGULAR_VELOCITY = 13, + DEFLECT = 14, + SEND_POSITION_UPDATE = 15, + SET_OBJECT_SCALE = 16, + GET_OBJECT_SCALE = 17, + TIMED_SCALE_FINISHED = 18, + TELEPORT = 19, + TOGGLE_PLAYER_FWD_TO_CAMERA = 20, + LOCK_PLAYER_ROT_TO_CAMERA = 21, + UNLOCK_PLAYER_ROT_FROM_CAMERA = 22, + TOGGLE_PLAYER_ROT_LOCK_TO_MOUSE = 23, + LOCK_PLAYER_ROT_TO_MOUSE = 24, + UNLOCK_PLAYER_ROT_FROM_MOUSE = 25, + SET_PLAYER_CONTROL_SCHEME = 26, + GET_PLAYER_CONTROL_SCHEME = 27, + RESET_PLAYER_CONTROL_SCHEME = 28, + PLAYER_TO_PREVIOUS_CONTROL_SCHEME = 29, + DROP_CLIENT_LOOT = 30, + GET_SPEED = 34, + GET_ROT_SPEED = 35, + IS_DEAD = 36, + DIE = 37, + REQUEST_DIE = 38, + ADD_OBJECT = 39, + PLAY_EMOTE = 41, + PRELOAD_ANIMATION = 42, + PLAY_ANIMATION = 43, + ANIMATION_COMPLETE = 44, + ENABLE_HIGHLIGHT = 45, + DISABLE_HIGHLIGHT = 46, + GET_ANIMATION_NAMES = 47, + CONTROL_BEHAVIORS = 48, + BLEND_PRIMARY_ANIMATION = 52, + SET_OFFSCREEN_ANIMATION = 53, + GET_MOVEMENT_INPUT_VALUES = 54, + SWAP_TEXTURE = 55, + SWAP_COLOR = 56, + ATTACH_HAIR = 57, + GET_ENTITY_STRUCT = 58, + SET_ENTITY_STRUCT = 59, + SET_ATTR = 60, + GET_ATTR = 61, + ON_HIT = 62, + HIT_OR_HEAL_RESULT = 63, + SHOW_ATTACK = 64, + GO_TO = 65, + GET_CONFIG_DATA = 66, + SET_CONFIG_DATA = 68, + GET_INVENTORY_EXTRA_INFO = 69, + GET_DISPLAY_NAME = 70, + GET_NAME = 71, + SET_NAME = 72, + IS_NAME_LOCALIZED = 73, + GET_HAIR_COLOR = 74, + SET_HAIR_COLOR = 75, + GET_HAIR_STYLE = 76, + SET_HAIR_STYLE = 77, + GET_HEAD = 78, + SET_HEAD = 79, + GET_TORSO = 80, + SET_TORSO = 81, + GET_LEGS = 82, + SET_LEGS = 83, + SET_PROXIMITY_RADIUS = 84, + PROXIMITY_UPDATE = 85, + GET_PROXIMITY_OBJECTS = 86, + UNSET_PROXIMITY_RADIUS = 87, + CLEAR_PROXIMITY_RADIUS = 88, + GET_PROXIMITY_DATA = 89, + SET_PROXIMITY_RADIUS_ICON = 90, + TOGGLE_TAC_ARC = 93, + CAST_SKILL = 95, + CAST_LOCAL_SKILL = 96, + ECHO_LOCAL_SKILL = 97, + QUEUE_AI_SKILL = 98, + ADD_THREAT_RATING = 99, + GET_THREAT_RATING = 100, + CLEAR_THREAT_LIST = 103, + GET_TIME_FOR_NPC_SKILL = 111, + ENEMY_HEAL_NOTIFICATION = 112, + RESET_SCRIPTED_AI_STATE = 113, + ENABLE_COMBAT_AI_COMPONENT = 114, + COMBAT_AI_FORCE_TETHER = 115, + SUSPEND_MOVEMENT_AI = 116, + NOTIFY_SCRIPT_VARS_INITIALIZED = 117, + ECHO_START_SKILL = 118, + START_SKILL = 119, + CASTER_DEAD = 120, + VERIFY_ACK = 121, + ADD_PENDING_VERIFY = 122, + MAP_SKILL = 123, + SELECT_SKILL = 124, + CAST_ACTIVE_SKILL = 125, + MODIFY_SKILL_COOLDOWN = 126, + ADD_SKILL = 127, + REMOVE_SKILL = 128, + LOG = 129, + LOG_CHAT = 130, + SET_MAX_CURRENCY = 131, + GET_MAX_CURRENCY = 132, + SET_CURRENCY = 133, + GET_CURRENCY = 134, + ADD_PENDING_CURRENCY = 136, + PICKUP_CURRENCY = 137, + SERVER_DELETE_LOOT_ITEM = 138, + PICKUP_ITEM = 139, + TEAM_PICKUP_ITEM = 140, + CLIENT_DELETE_LOOT_ITEM = 141, + CLIENT_SET_LOOT_ITEM_FFA = 143, + COLLISION_PHANTOM = 144, + OFF_COLLISION_PHANTOM = 145, + COLLISION_PROXIMITY = 146, + OFF_COLLISION_PROXIMITY = 147, + COLLISION = 148, + OFF_COLLISION = 149, + GET_SKILLS = 150, + CLEAR_FX_SINGLE_EFFECT = 152, + GET_FX_EXIST_EFFECT = 153, + PLAY_FX_EFFECT = 154, + STOP_FX_EFFECT = 155, + CLEAR_FX_ALL_CREATE_EFFECTS = 156, + UPDATE_FX_ALL_CREATE_EFFECTS = 157, + REQUEST_RESURRECT = 159, + RESURRECT = 160, + UPDATE_FROM_GHOST = 162, + FETCH_GHOST = 163, + KFM_LOADED = 164, + NIF_LOADED = 165, + HKX_LOADED = 166, + MOVE_TO_DELETE_QUEUE = 167, + RESTORE_FROM_DELETE_QUEUE = 168, + IS_ENEMY = 169, + GET_FACTION = 170, + SET_IMAGINATION = 171, + GET_IMAGINATION = 172, + SET_MAX_IMAGINATION = 173, + GET_MAX_IMAGINATION = 174, + MODIFY_IMAGINATION = 175, + MODIFY_MAX_IMAGINATION = 176, + SET_HEALTH = 177, + GET_HEALTH = 178, + SET_MAX_HEALTH = 179, + GET_MAX_HEALTH = 180, + MODIFY_HEALTH = 181, + MODIFY_MAX_HEALTH = 182, + SET_ARMOR = 183, + GET_ARMOR = 184, + SET_MAX_ARMOR = 185, + GET_MAX_ARMOR = 186, + MODIFY_ARMOR = 187, + MODIFY_MAX_ARMOR = 188, + POP_HEALTH_STATE = 190, + PUSH_EQUIPPED_ITEMS_STATE = 191, + POP_EQUIPPED_ITEMS_STATE = 192, + SET_GM_LEVEL = 193, + GET_GM_LEVEL = 194, + ADD_STATUS_EFFECT = 196, + REMOVE_STATUS_EFFECT = 197, + SET_STUNNED = 198, + GET_STUNNED = 199, + SET_STUN_IMMUNITY = 200, + GET_STUN_IMMUNITY = 201, + KNOCKBACK = 202, + SET_VISIBLE = 203, + GET_VISIBLE = 204, + REPORT_ITEM_INFO = 205, + GET_REBUILD_STATE = 207, + REBUILD_CANCEL = 209, + REBUILD_START = 211, + ENABLE_REBUILD = 213, + SKILL_FAILURE = 214, + IS_ATTACK_STANCE = 216, + SET_OBJECT_RENDER = 217, + REQUEST_MAPPED_SKILLS = 218, + UI_SELECT_MAPPED_SKILL = 219, + GET_INVENTORY_ITEM_IN_SLOT = 220, + GET_FIRST_INVENTORY_ITEM_BY_LOT = 221, + GET_SMALLEST_INVENTORY_STACK_BY_LOT = 222, + MOVE_ITEM_IN_INVENTORY = 224, + ADD_ITEM_TO_INVENTORY_CLIENT_SYNC = 227, + GET_EQUIPPED_ITEMS = 229, + REMOVE_ITEM_FROM_INVENTORY = 230, + EQUIP_INVENTORY = 231, + UN_EQUIP_INVENTORY = 233, + EQUIP_ITEM = 234, + UN_EQUIP_ITEM = 235, + IS_ITEM_RESPOND = 236, + IS_ITEM_EQUIPPED = 237, + ATTACH_ITEM = 238, + DETACH_ITEM = 239, + GET_NODE = 240, + GET_LOT = 241, + IS_ITEM_EQUIPPABLE = 242, + GET_CURRENT_ANIMATION = 243, + GET_INV_ITEM_COUNT = 244, + POST_LOAD_EQUIP = 245, + SET_PHYSICS_ACTIVE_STATE = 246, + GET_CURRENT_SKILL_TAC_ARC = 247, + OFFER_MISSION = 248, + RESPOND_TO_MISSION = 249, + GET_MISSION_STATE = 250, + GET_MISSION_COMPLETE_TIMESTAMP = 251, + NOTIFY_MISSION = 254, + NOTIFY_MISSION_TASK = 255, + ARE_GFX_LOADED = 257, + ADDED_TO_WORLD = 258, + REMOVE_EXTRA_GFX_FROM_PIPE = 259, + HIDE_EQUIPED_WEAPON = 260, + UN_HIDE_EQUIPED_WEAPON = 261, + GET_ITEM_SLOT = 262, + IS_CHARACTER = 263, + SET_IMMUNITY = 264, + TOGGLE_TOOLTIPS = 266, + GET_TOOLTIPS_DISABLED = 267, + GET_BOUNDING_INFO = 268, + OVERRIDE_BOUNDING_RADIUS = 269, + GET_OFFSCREEN = 270, + USE_STATE_MACHINE = 271, + ADD_STATE = 272, + ADD_SUB_STATE = 273, + SET_STATE = 274, + SET_SUB_STATE = 275, + ADD_MESSAGE = 276, + RELOAD_SCRIPT = 277, + RELOAD_ALL_SCRIPTS = 278, + FRIEND_INVITE_MSG = 279, + ADD_FRIEND_REPOSNSE_MSG = 280, + REMOVE_FRIEND_RESPONSE_MSG = 281, + ADD_FRIEND_FROM_UI_MSG = 282, + GET_CACHED_FRIENDS_LIST_MSG = 283, + REQUEST_NEW_FRIENDS_LIST_MSG = 284, + REPOPULATE_FRIENDS_LIST_MSG = 285, + ADD_IGNORE_REPONSE_MSG = 286, + REMOVE_IGNORE_RESPONSE_MSG = 287, + ADD_IGNORE_FROM_UI_MSG = 288, + GET_CACHED_IGNORE_LIST_MSG = 289, + REQUEST_NEW_IGNORE_LIST_MSG = 290, + REMOVE_FRIEND_BY_NAME = 291, + REMOVE_IGNORE_BY_NAME = 292, + IS_PLAYER_IN_IGNORE_LIST_MSG = 293, + REPOPULATE_IGNORE_LIST_MSG = 294, + GET_INVENTORY_LIST = 295, + UPDATE_FRIEND_MSG = 296, + UPDATE_FRIEND_NAME_MSG = 297, + UPDATE_IGNORE_NAME_MSG = 298, + DEPARTED = 299, + ARRIVED = 300, + TEMPLATE_CHANGE_WAYPOINTS = 301, + CANCELLED = 302, + FLUSH_CACHED_GRAPHICS = 303, + FOLLOW_TARGET = 304, + TIMER_DONE = 305, + TIMER_CANCELLED = 306, + SET_TETHER_POINT = 307, + GET_TETHER_POINT = 308, + LEFT_TETHER_RADIUS = 309, + GET_SCRIPT_VARS_PTR = 310, + FACE_TARGET = 311, + ROTATE_BY_DEGREES = 312, + STRING_RENDERED = 313, + RESET_PRIMARY_ANIMATION = 314, + FACE_PLAY_STREAM = 315, + TORSO_PLAY_STREAM = 316, + CAN_PICKUP = 317, + GET_INVENTORY_SIZE = 318, + GET_INVENTORY_COUNT = 319, + GET_OBJECTS_IN_GROUP = 320, + HIDE_ITEM = 321, + IS_OBJECT_IN_FOV = 322, + GET_TYPE = 323, + TEAM_INVITE_MSG = 324, + TEAM_GET_SIZE = 325, + TEAM_REQUEST_SET_LOOT = 326, + TEAM_REMOVE_PLAYER_MSG = 327, + TEAM_UPDATE_PLAYER_NAME_MSG = 328, + SET_UPDATABLE = 329, + REQUEST_TEAM_UI_UPDATE = 330, + SET_COLLISION_GROUP = 331, + GET_COLLISION_GROUP = 332, + GET_ORIGINAL_COLLISION_GROUP = 333, + SET_COLLISION_GROUP_TO_ORIGINAL = 334, + GET_OBJECT_RADIUS = 335, + REBUILD_NOTIFY_STATE = 336, + GET_PLAYER_INTERACTION = 337, + SET_PLAYER_INTERACTION = 338, + FORCE_PLAYER_TO_INTERACT = 339, + GET_SELECTED_POTENTIAL_INTERACTION = 340, + SET_SELECTED_POTENTIAL_INTERACTION = 341, + GET_INTERACTION_DISTANCE = 342, + SET_INTERACTION_DISTANCE = 343, + CALCULATE_INTERACTION_DISTANCE = 344, + INTERACTION_ATTEMPT_FROM_OUT_OF_RANGE = 345, + SET_PICKING_TARGET = 346, + CLIENT_UNUSE = 347, + BEGIN_PET_INTERACTION = 348, + WANTS_INTERACTION_ICON = 349, + PROPERTY_EDIT_ICON_INTERACTION = 350, + PROPERTY_MODEL_INTERACTION = 351, + GET_INTERACTION_DETAILS = 352, + GET_DISABLED_INTERACTION_TYPES = 353, + GET_INTERACTION_INFO = 354, + INTERACTION_GAME_STATE_CHANGE = 355, + TOGGLE_INTERACTION_UPDATES = 356, + TERMINATE_INTERACTION = 357, + SERVER_TERMINATE_INTERACTION = 358, + GET_PLAYERS_TARGET_FOR_SELECTION = 359, + PROCESS_INTERACTION_UNDER_CURSOR = 360, + HANDLE_INTERACT_ACTION = 361, + ATTEMPT_INTERACTION = 362, + HANDLE_INTERACTION_CAMERA = 363, + REQUEST_USE = 364, + CLIENT_USE = 366, + GET_PLAYER_MULTI_INTERACTION = 367, + GET_MULTI_INTERACTION_STATE = 368, + VENDOR_OPEN_WINDOW = 369, + VENDOR_CLOSE_WINDOW = 370, + EMOTE_PLAYED = 371, + EMOTE_RECEIVED = 372, + BUY_FROM_VENDOR = 373, + SELL_TO_VENDOR = 374, + ADD_DONATION_ITEM = 375, + REMOVE_DONATION_ITEM = 376, + CONFIRM_DONATION_ON_PLAYER = 378, + CANCEL_DONATION_ON_PLAYER = 379, + TEAM_GET_LEADER = 380, + TEAM_GET_ON_WORLD_MEMBERS = 381, + TEAM_GET_ALL_MEMBERS = 382, + TEAM_SET_OFF_WORLD_FLAG = 383, + SET_TRANSPARENCY = 385, + GET_PREFERS_FADE = 386, + PROJECTILE_IMPACT = 387, + SET_PROJECTILE_PARAMS = 388, + SET_INVENTORY_SIZE = 389, + ACKNOWLEDGE_POSSESSION = 391, + SET_POSSESSED_OBJECT = 392, + CHANGE_POSSESSOR = 393, + GET_POSSESSION_TYPE = 395, + GET_POSSESSED_OBJECT = 396, + GET_POSSESSOR = 397, + IS_POSSESSED = 398, + ENABLE_ACTIVITY = 399, + SET_SHOOTING_GALLERY_PARAMS = 400, + OPEN_ACTIVITY_START_DIALOG = 401, + REQUEST_ACTIVITY_START_STOP = 402, + REQUEST_ACTIVITY_ENTER = 403, + REQUEST_ACTIVITY_EXIT = 404, + ACTIVITY_ENTER = 405, + ACTIVITY_EXIT = 406, + ACTIVITY_START = 407, + ACTIVITY_STOP = 408, + SHOOTING_GALLERY_CLIENT_AIM_UPDATE = 409, + ROTATE_TO_POINT = 410, + SHOOTING_GALLERY_FIRE = 411, + CALCULATE_FIRING_PARAMETERS = 412, + GET_MUZZLE_OFFSET = 413, + GET_ACTIVITY_POINTS = 414, + TEAM_IS_ON_WORLD_MEMBER = 415, + REQUEST_VENDOR_STATUS_UPDATE = 416, + VENDOR_STATUS_UPDATE = 417, + CANCEL_MISSION = 418, + RESET_MISSIONS = 419, + RENDER_COMPONENT_READY = 420, + SEND_MINIFIG_DECALS = 421, + PHYSICS_COMPONENT_READY = 422, + ENTER_STANDBY_MODE = 423, + LEAVE_STANDBY_MODE = 424, + NOTIFY_CLIENT_SHOOTING_GALLERY_SCORE = 425, + REQUEST_CONSUME_ITEM = 426, + CONSUME_CLIENT_ITEM = 427, + CLIENT_ITEM_CONSUMED = 428, + QUERY_STANDBY_MODE = 429, + GET_NI_BOUND = 430, + MISSION_FAILURE = 431, + GET_ANIMATION_TIME = 432, + GET_CURRENT_ACTIVITY = 434, + SET_EYEBROWS = 435, + GET_EYEBROWS = 436, + SET_EYES = 437, + GET_EYES = 438, + SET_MOUTH = 439, + GET_MOUTH = 440, + IS_OBJECT_SMASHABLE = 441, + SMASHABLE_STATE_CHANGED = 443, + USE_STATE_LOGGER = 444, + ROTATE_SUB_NODE = 445, + GET_SUB_NODE_POSITION = 446, + GET_SUB_NODE = 447, + UPDATE_SHOOTING_GALLERY_ROTATION = 448, + RENDER_FLOATING_TEXT = 449, + REQUEST2_D_TEXT_ELEMENT = 450, + UPDATE2_D_TEXT_ELEMENT = 451, + REMOVE2_D_TEXT_ELEMENT = 452, + SET_COLOR = 453, + GET_COLOR = 454, + HKX_CHARACTER_LOADED = 455, + ACTIVATE_PHYSICS = 457, + SET_ICON_ABOVE_HEAD = 458, + ADD_ICON_COMPOSITE = 459, + CLEAR_ICON_COMPOSITES = 460, + ICON_NIF_LOADED = 461, + ICON_KFM_LOADED = 462, + GET_OVERHEAD_ICON_PROPERTIES_FROM_PARENT = 463, + BOUNCE_PLAYER = 464, + SET_USER_CTRL_COMP_PAUSE = 466, + HAS_COLLIDED = 467, + GET_TOOLTIP_FLAG = 468, + SET_TOOLTIP_FLAG = 469, + GET_FLAG = 470, + SET_FLAG = 471, + NOTIFY_CLIENT_FLAG_CHANGE = 472, + CURSOR_ON = 473, + CURSOR_OFF = 474, + HELP = 475, + VENDOR_TRANSACTION_RESULT = 476, + PERFORM_SPECIAL_DEATH = 477, + GET_SHADER_ID = 478, + GET_RENDER_ENVIRONMENT = 479, + FINISHED_LOADING_SCENE = 480, + GET_SKILL_INFO = 481, + ACTIVITY_CANCEL = 482, + MISSION_USES_OBJECT = 483, + GET_POSITIONAL_ID = 484, + SET_COLLECTIBLE_STATUS = 485, + HAS_BEEN_COLLECTED = 486, + HAS_BEEN_COLLECTED_BY_CLIENT = 487, + GET_POS_UPDATE_STATS = 488, + GET_NUM_VIEWERS_SCOPING_THIS = 489, + GET_ACTIVITY_USER = 490, + GET_ALL_ACTIVITY_USERS = 491, + GET_MISSION_FOR_PLAYER = 492, + SET_FACTION = 493, + SET_PLATFORM_IDLE_STATE = 494, + DISPLAY_CHAT_BUBBLE = 495, + REQUEST_CHAT_BUBBLE_ELEMENT = 496, + GET_MISSION_DATA = 497, + SPAWN_PET = 498, + DESPAWN_PET = 499, + SET_LOCAL_SPACE_STATE = 500, + GET_LOCAL_SPACE_STATE = 501, + SET_POSITION_TO_LOCAL_POSITION = 502, + ALLOW_LOCAL_SPACE_UPDATE = 503, + TOGGLE_FREE_CAM_MODE = 504, + PLAYER_LOADED = 505, + PLAYER_ADDED_TO_WORLD_LOCAL = 506, + OBJECT_LOADED = 507, + GET_PLAYER_READY = 508, + PLAYER_READY = 509, + SET_SMASHABLE_PARAMS = 510, + IS_LOOTABLE_CHEST = 511, + LOOT_OPEN_WINDOW = 512, + LOOT_SELECTION_UPDATE = 513, + TAKE_LOOT_CHEST_ITEM = 514, + REQUEST_LINKED_MISSION = 515, + TRANSFER_TO_ZONE = 516, + TRANSFER_TO_ZONE_CHECKED_IM = 517, + SECURED_TRANSFER_TO_ZONE = 518, + INVALID_ZONE_TRANSFER_LIST = 519, + MISSION_DIALOGUE_OK = 520, + GET_OBJECT_IN_SCOPE = 521, + SET_LAUNCHED_STATE = 522, + P_CREATE_EFFECT_FINISHED = 523, + SMASHED_OBJECT = 524, + CHECK_SMASHCHAIN_OVERRIDE = 525, + DISPLAY_REBUILD_ACTIVATOR = 526, + TRANSFER_TO_LAST_NON_INSTANCE = 527, + SET_ACTIVE_LOCAL_CHARACTER_ID = 528, + DISPLAY_MESSAGE_BOX = 529, + MESSAGE_BOX_RESPOND = 530, + CHOICE_BOX_RESPOND = 531, + SERVER_SET_USER_CTRL_COMP_PAUSE = 532, + SET_CHARACTER_AUTO_RUN = 533, + FOLLOW_WAYPOINTS = 534, + SWAP_DECAL_AND_COLOR = 535, + CONTINUE_WAYPOINTS = 536, + SMASH = 537, + UN_SMASH = 538, + GET_IS_SMASHED = 539, + GET_UP_VECTOR = 540, + SET_GRAVITY_SCALE = 541, + SET_GRAVITY_SCALE_FOR_RIGID_BODY = 542, + STOP_MOVING = 543, + SET_PATHING_SPEED = 544, + SET_SHIELDED = 545, + SET_SHOOTING_GALLERY_RETICULE_EFFECT = 546, + PLACE_MODEL_RESPONSE = 547, + SET_DODGE_INFO = 548, + GET_DODGE_INFO = 549, + SET_SKILL_ATTACK_SPEED = 550, + GET_SKILL_COOLDOWN_GROUP = 551, + GET_INITIAL_SKILL_COOLDOWN = 552, + GET_SKILL_COOLDOWN_REMAINING = 553, + GET_GLOBAL_COOLDOWN = 554, + SET_GLOBAL_COOLDOWN = 555, + RESET_GLOBAL_COOLDOWN = 556, + FINDINVENTORY_ITEM = 558, + PATH_STUCK = 559, + SET_CURRENT_PATH = 560, + SET_JET_PACK_MODE = 561, + SET_JET_PACK_TIME = 562, + PET_FOLLOW_OWNER = 563, + PLAYER_DIED = 564, + REGISTER_PET_ID = 565, + REGISTER_PET_DBID = 566, + GET_PET_ID = 567, + SHOW_ACTIVITY_COUNTDOWN = 568, + DISPLAY_TOOLTIP = 569, + SET_PHANTOM_BASE = 570, + GET_MOTION_STATE = 571, + GET_MOTION_CONFIG = 572, + SET_ACTIVE_PROJECTILE_SKILL = 573, + INITIALIZE_MISSION_VISUALS = 574, + GET_MISSIONS = 575, + START_ACTIVITY_TIME = 576, + ADD_ACTIVITY_TIME = 577, + GUILD_GET_SIZE = 578, + GUILD_CAN_WE_INVITE = 579, + GUILD_CAN_WE_KICK = 580, + SET_CHAR_GUILD_INFO = 581, + GET_CHAR_GUILD_INFO = 582, + GET_CHAR_IS_IN_GUILD = 583, + RE_RENDER_NAME_BILLBOARD = 584, + IS_IN_LOCAL_CHAR_PROXIMITY = 585, + GUILD_SET_STATUS = 586, + GUILD_ADD_PLAYER = 587, + GUILD_REMOVE_PLAYER = 588, + GUILD_UPDATE_PLAYER_NAME = 589, + GUILD_SET_PLAYER_RANK = 590, + GUILD_SET_ONLINE_STATUS = 591, + GUILD_INVITE = 592, + REQUEST_GUILD_DATA = 593, + POPULATE_GUILD_DATA = 594, + GET_CACHED_GUILD_DATA = 595, + GUILD_RENDER_NAME = 596, + GET_IS_SUPPORTED = 600, + CHARACTER_SUPPORT_CHANGED = 601, + ACTIVITY_PAUSE = 602, + USE_NON_EQUIPMENT_ITEM = 603, + REQUEST_USE_ITEM_ON = 604, + REQUEST_USE_ITEM_ON_TARGET = 605, + USE_ITEM_ON = 606, + USE_ITEM_RESULT = 607, + GET_PARENT_OBJ = 608, + SET_PARENT_OBJ = 609, + GET_UPDATES_WITH_PARENT_POSITION = 610, + PARENT_REMOVED = 611, + PARENT_LEFT_SCOPE = 612, + PARENT_ENTERED_SCOPE = 613, + CHILD_LOADED = 614, + CHILD_REMOVED = 615, + CHILD_DETACHED = 616, + CHILD_ENTERED_SCOPE = 617, + CHILD_LEFT_SCOPE = 618, + GET_CHILD_OBJECTS = 619, + ZONE_TRANSFER_FINISHED = 621, + CHAT_CONNECTION_UPDATE = 622, + PLATFORM_AT_LAST_WAYPOINT = 623, + LOOT_TAKE_ALL = 624, + GET_EQUIPPED_ITEM_INFO = 625, + DISPLAY_GUILD_CREATE_BOX = 626, + GET_EDITOR_LEVEL = 627, + GET_ACCOUNT_ID = 628, + GET_LAST_LOGOUT = 629, + GET_LAST_PROP_MOD_DISPLAY_TIME = 630, + SET_LAST_PROP_MOD_DISPLAY_TIME = 631, + SHOW_ACTIVITY_SUMMARY = 632, + CAN_RECEIVE_ALL_REWARDS = 633, + GET_ACTIVITY_REWARD = 634, + LOOT_CLOSE_WINDOW = 635, + GET_BLUEPRINT_ID = 636, + NOTIFY_BLUEPRINT_UPDATE = 637, + FETCH_MODEL_METADATA_REQUEST = 638, + FETCH_MODEL_METADATA_RESPONSE = 639, + COMMAND_PET = 640, + PET_RESPONSE = 641, + GET_ICON_ABOVE_HEAD_STATE = 642, + GET_ICON_ABOVE_HEAD = 643, + ICON_FINISHED_LOADING = 644, + ADD_PET_STATE = 645, + REMOVE_PET_STATE = 646, + SET_PET_STATE = 647, + REQUEST_ACTIVITY_SUMMARY_LEADERBOARD_DATA = 648, + SEND_ACTIVITY_SUMMARY_LEADERBOARD_DATA = 649, + SET_ON_TEAM = 650, + GET_PET_HAS_STATE = 651, + FIND_PROPERTY = 652, + SET_PET_MOVEMENT_STATE = 653, + GET_ITEM_TYPE = 654, + GET_ITEM_INFO_KEY = 655, + NOTIFY_OBJECT = 656, + IS_PET_WILD = 657, + CLIENT_NOTIFY_PET = 659, + NOTIFY_PET = 660, + NOTIFY_PET_TAMING_MINIGAME = 661, + START_SERVER_PET_MINIGAME_TIMER = 662, + CLIENT_EXIT_TAMING_MINIGAME = 663, + GET_BUILDMODE_ACTIVE = 664, + GET_PET_TAMING_MINIGAME_ACTIVE = 665, + PET_TAMING_OBJECT_PICKED = 666, + PET_TAMING_MINIGAME_RESULT = 667, + PET_TAMING_TRY_BUILD_RESULT = 668, + SET_PET_TAMING_MODEL = 669, + GET_PET_TAMING_MODEL = 670, + PET_ON_SWITCH = 671, + PET_OFF_SWITCH = 672, + NOTIFY_TAMING_BUILD_SUCCESS = 673, + NOTIFY_TAMING_MODEL_LOADED_ON_SERVER = 674, + NOTIFY_TAMING_PUZZLE_SELECTED = 675, + GET_INSTRUCTION_COUNT = 676, + GET_IS_NPC = 677, + ACTIVATE_BUBBLE_BUFF = 678, + DECTIVATE_BUBBLE_BUFF = 679, // This is spelled wrong in the client, so we misspell it here. + EXHIBIT_VOTE = 680, + ADD_PET_TO_PLAYER = 681, + REMOVE_PET_FROM_PLAYER = 682, + REQUEST_SET_PET_NAME = 683, + SET_PET_NAME = 684, + PET_NAME_CHANGED = 686, + GET_PET_AT_INDEX = 687, + GET_LOT_FOR_PET_BY_DBID = 688, + GET_NAME_FOR_PET_BY_DBID = 689, + GET_ACTIVE_PET_OBJ_ID = 690, + GET_ACTIVE_PET_INVENTORY_OBJ_ID = 691, + SHOW_PET_ACTION_BUTTON = 692, + SET_EMOTE_LOCK_STATE = 693, + GET_EMOTE_LOCK_STATE = 694, + LEAVE_TEAM_MSG = 695, + TEAM_KICK_PLAYER_MSG = 697, + TEAM_SET_LEADER_SEND_MSG = 698, + USE_ITEM_ON_CLIENT = 699, + DOES_FORWARD_TARGET_CLICKING = 700, + CHECK_USE_REQUIREMENTS = 701, + USE_REQUIREMENTS_RESPONSE = 702, + USE_ITEM_REQUIREMENTS_RESPONSE = 703, + PET_ADDED_TO_WORLD = 704, + BOUNCER_TRIGGERED = 705, + EXHIBIT_QUERY_CURRENT_MODEL = 706, + EXHIBIT_QUERY_CURRENT_MODEL_RESPONSE = 707, + EXHIBIT_ATTEMPT_VOTE = 708, + EXHIBIT_VOTE_RESPONSE = 709, + EHIBIT_REQUERYMODELS = 710, + IS_SKILL_ACTIVE = 711, + TOGGLE_ACTIVE_SKILL = 712, + PLAY_EMBEDDED_EFFECT_ON_ALL_CLIENTS_NEAR_OBJECT = 713, + EXHIBIT_GET_INFO = 714, + GET_PROPERTY_DATA = 715, + DOWNLOAD_PROPERTY_DATA = 716, + QUERY_PROPERTY_DATA = 717, + MODEL_MODERATION_ACTION = 719, + NOTIFY_SERVER_UGC_REVIEW_READY = 720, + NOTIFY_CLIENT_UGC_REVIEW_READY = 721, + OLD_USE_ITEM_ON = 722, + FIND_PROPERTY_FOR_SALE_RESPONSE = 723, + PROPERTY_EDITOR_BEGIN = 724, + PROPERTY_EDITOR_END = 725, + PROPERTY_EDITOR_SET_MODE = 726, + TOGGLE_TRIGGER = 727, + FIRE_EVENT = 728, + IS_MINIFIG_IN_A_BUBBLE = 729, + GET_ITEM_INFO = 730, + MISSION_NEEDS_LOT = 731, + STOP_PATHING = 732, + START_PATHING = 733, + ACTIVATE_BUBBLE_BUFF_FROM_SERVER = 734, + DEACTIVATE_BUBBLE_BUFF_FROM_SERVER = 735, + HAS_SKILL = 736, + NOTIFY_CLIENT_ZONE_OBJECT = 737, + MOVE_OBJECT = 738, + ROTATE_OBJECT = 739, + GET_SPAWNER_CONFIG_DATA = 740, + UPDATE_SPAWNER_CONFIG_DATA = 741, + TURN_AROUND = 743, + GO_FORWARD = 744, + GO_BACKWARD = 745, + UPDATE_REPUTATION = 746, + GET_REPUTATION = 747, + ADD_REPUTATION = 748, + UPDATE_PROPERTY_DATA = 749, + PROPERTY_RENTAL_RESPONSE = 750, + EXHIBIT_PLACEMENT_RESPONSE = 751, + SQUIRT_WITH_WATERGUN = 752, + GET_VOTES_LEFT = 753, + ADJUST_VOTES_LEFT = 754, + EVADE_TARGET = 755, + STOPPED_EVADING = 756, + GET_PET_HAS_ABILITY = 757, + REQUEST_PLATFORM_RESYNC = 760, + PLATFORM_RESYNC = 761, + PLAY_CINEMATIC = 762, + END_CINEMATIC = 763, + CINEMATIC_UPDATE = 764, + ATTACH_CAMERA_TO_RAIL = 765, + DETACH_CAMERA_FROM_RAIL = 766, + TOGGLE_GHOST_REFERENCE_OVERRIDE = 767, + SET_GHOST_REFERENCE_POSITION = 768, + GET_GHOST_REFERENCE_POSITION = 769, + FIRE_EVENT_SERVER_SIDE = 770, + GET_PET_ABILITY_OBJECT = 771, + TEAM_INVITE_MSG_FROM_UI = 772, + ADD_CAMERA_EFFECT = 773, + REMOVE_CAMERA_EFFECT = 774, + REMOVE_ALL_CAMERA_EFFECTS = 775, + GET_MY_PROPERTIES_IN_THIS_ZONE = 776, + IS_MODEL_WITHIN_PROPERTY_BOUNDS = 777, + PROPERTY_DATA_RESULTS = 778, + ON_UN_SERIALIZE = 779, + SCRIPT_NETWORK_VAR_UPDATE = 781, + ADD_OBJECT_TO_GROUP = 783, + REMOVE_OBJECT_FROM_GROUP = 784, + IS_OBJECT_STATIC = 785, + GET_HAS_MISSION = 786, + GET_MISSION_TARGET_LOT = 787, + GET_MISSION_OFFERER_LOT = 788, + USE_UNIQUE_ITEM = 789, + GET_IS_PET = 790, + DELETE_PROPERTY = 791, + CREATEMODEL_FROM_CLIENT = 792, + UPDATE_MODEL_FROM_CLIENT = 793, + DELETE_MODEL_FROM_CLIENT = 794, + SHOW_PROPERTY_BOUNDS = 795, + SET_PROPERTY_I_DS = 796, + PLAY_FACE_DECAL_ANIMATION = 797, + ADD_ACTIVITY_USER = 798, + REMOVE_ACTIVITY_USER = 799, + GET_NUM_ACTIVITY_USERS = 800, + ACTIVITY_USER_EXISTS = 801, + DO_COMPLETE_ACTIVITY_EVENTS = 805, + SET_ACTIVITY_PARAMS = 806, + SET_ACTIVITY_USER_DATA = 807, + GET_ACTIVITY_USER_DATA = 808, + DO_CALCULATE_ACTIVITY_RATING = 809, + ND_AUDIO_POST_SETUP = 812, + ND_AUDIO_PRE_SHUTDOWN = 813, + SET_ND_AUDION_LISTENER_STANCE = 814, + SET_UP_ND_AUDIO_EMIITTER = 815, + SHUT_DOWN_ND_AUDIO_EMITTER = 816, + METAIFY_ND_AUDIO_EMITTER = 817, + UN_METAIFY_ND_AUDIO_EMITTER = 818, + METAIFY_ND_AUDIO_EMITTERS = 819, + UN_METAIFY_ND_AUDIO_EMITTERS = 820, + PLAY_ND_AUDIO_EMITTER = 821, + STOP_ND_AUDIO_EMITTER = 822, + STOP_ND_AUDIO_EMITTER_ALL = 823, + SET_ND_AUDIO_EMITTER_PARAMETER = 824, + SET_ND_AUDIO_EMITTERS_PARAMETER = 825, + ND_AUDIO_CALLBACK = 826, + ACTIVATE_ND_AUDIO_MUSIC_CUE = 827, + DEACTIVATE_ND_AUDIO_MUSIC_CUE = 828, + FLASH_ND_AUDIO_MUSIC_CUE = 829, + SET_ND_AUDIO_MUSIC_PARAMETER = 830, + PLAY2_D_AMBIENT_SOUND = 831, + STOP2_D_AMBIENT_SOUND = 832, + PLAY3_D_AMBIENT_SOUND = 834, + STOP3_D_AMBIENT_SOUND = 835, + ACTIVATE_ND_AUDIO_MIXER_PROGRAM = 836, + DEACTIVATE_ND_AUDIO_MIXER_PROGRAM = 837, + UPDATE_ACTIVITY_LEADERBOARD = 838, + ACTIVITY_LEADERBOARD_UPDATED = 839, + ENTER_PROPERTY1 = 840, + ENTER_PROPERTY2 = 841, + PROPERTY_ENTRANCE_SYNC = 842, + SEND_PROPERTY_POPULATION_TO_CLIENT = 843, + SEN_PROPERTY_PLAQUE_VIS_UPDATE = 844, + PROPERTY_SELECT_QUERY = 845, + CREATE_POSITION_STRING = 848, + GET_PARALLEL_POSITION = 849, + PARSE_CHAT_MESSAGE = 850, + SET_MISSION_TYPE_STATE = 851, + GET_LOCATIONS_VISITED = 852, + GET_MISSION_TYPE_STATES = 853, + GET_TIME_PLAYED = 854, + SET_MISSION_VIEWED = 855, + SLASH_COMMAND_TEXT_FEEDBACK = 856, + HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857, + BROADCAST_TEXT_TO_CHATBOX = 858, + OPEN_PROPERTY_MANAGEMENT = 860, + OPEN_PROPERTY_VENDOR = 861, + VOTE_ON_PROPERTY = 862, + UPDATE_PROPERTY_OR_MODEL_FOR_FILTER_CHECK = 863, + NOTIFY_PLAYER_OF_PROPERTY_SUBMISSION = 865, + NOTIFY_PLAYER_OF_MODEL_SUBMISSION = 866, + PHYSICS_SYSTEM_LOADED = 867, + CLIENT_TRADE_REQUEST = 868, SERVER_TRADE_REQUEST = 869, - SERVER_TRADE_INVITE = 870, - CLIENT_TRADE_REPLY = 871, + SERVER_TRADE_INVITE = 870, + CLIENT_TRADE_REPLY = 871, SERVER_TRADE_REPLY = 872, - SERVER_TRADE_INITIAL_REPLY = 873, - SERVER_TRADE_FINAL_REPLY = 874, - CLIENT_TRADE_UPDATE = 875, + SERVER_TRADE_INITIAL_REPLY = 873, + SERVER_TRADE_FINAL_REPLY = 874, + CLIENT_TRADE_UPDATE = 875, SERVER_SIDE_TRADE_UPDATE = 876, - SERVER_TRADE_UPDATE = 877, - CLIENT_TRADE_CANCEL = 878, - CLIENT_SIDE_TRADE_CANCEL = 879, - CLIENT_TRADE_ACCEPT = 880, + SERVER_TRADE_UPDATE = 877, + CLIENT_TRADE_CANCEL = 878, + CLIENT_SIDE_TRADE_CANCEL = 879, + CLIENT_TRADE_ACCEPT = 880, SERVER_SIDE_TRADE_ACCEPT = 881, SERVER_SIDE_TRADE_CANCEL = 882, - SERVER_TRADE_CANCEL = 883, - SERVER_TRADE_ACCEPT = 884, - GET_TRADE_INFO = 885, - KF_LOADED = 886, - BRICKS_LOADED = 887, - READY_FOR_UPDATES = 888, - SEND_READY_FOR_UPDATES = 889, - SET_LAST_CUSTOM_BUILD = 890, - GET_LAST_CUSTOM_BUILD = 891, - GET_STATUS_EFFECT_BY_ID = 892, - GET_ALL_STATUS_EFFECTS = 893, - CHILD_RENDER_COMPONENT_READY = 894, - NOTIFY_APPEARANCE_CHANGED_MSG = 895, - SET_PHYSICS_MOTION_STATE = 896, - GET_PHYSICS_MOTION_STATE = 897, - ATTACH_GRAYSCALE_EFFECT = 898, - ATTACH_FADE_EFFECT = 899, - ATTACH_CHANGE_RENDER_ENVIRONMENT_EFFECT = 900, - FORCE_MOVEMENT = 901, - CANCEL_FORCE_MOVEMENT = 902, - SET_IGNORE_PROJECTILE_COLLISION = 903, - GET_IGNORE_PROJECTILE_COLLISION = 904, - ORIENT_TO_OBJECT = 905, - ORIENT_TO_POSITION = 906, - ORIENT_TO_ANGLE = 907, - NOTIFY_CLIENT_UGC_MODEL_READY = 909, - NOTIFY_CLIENT_UGC_ICON_READY = 911, - PROPERTY_BUILD_MODE_CHANGED = 912, - PROPERTY_BUILD_MODE_UPDATE = 913, - PROPERTY_DELETION_ACTION = 914, - PROPERTY_MODERATION_STATUS_ACTION = 915, - PROPERTY_MODERATION_STATUS_ACTION_RESPONSE = 916, - PROPERTY_MODERATION_STATUS_UPDATE = 917, - PROPERTY_NEEDS_GM_ATTENTION = 918, - PROPERTY_MODERATION_CHANGED = 919, - INVENTORY_REFRESH_ITEM_DETAILS = 922, - INVENTORY_LOAD_CUSTOM_ICON = 923, - GET_STATUS_EFFECT_BY_TYPE = 924, - RELEASE_CHARGED_SKILL = 925, - PROPERTY_RELOAD_DB = 926, - SET_PLAYER_TARGET = 927, - GET_PLAYER_TARGET = 928, - LOCK_CAMERA_NETWORKED = 929, - MOVE_CAMERA_NETWORKED = 930, - REBUILD_ACTIVATED = 931, - BOUNCE_NOTIFICATION = 932, - REQUEST_CLIENT_BOUNCE = 934, - GET_RECENT_BOUNCED = 935, - SET_RECENT_BOUNCED = 936, - SET_ACTIVE_STATE = 937, - GET_ACTIVE_STATE = 938, - HAS_COMPONENT_TYPE = 939, - GET_COMPONENT_LIST = 940, - RESPONDS_TO_FACTION = 941, - BOUNCER_ACTIVE_STATUS = 942, - HF_ATTRIBUTES_PUSH = 943, - HF_ATTRIBUTES_PULL = 944, - HF_ATTRIBUTES_PATH_DISPLAY = 945, - HF_CONTROLS_PULL = 946, - HF_OBJECT_SELECTED = 947, - HF_PLACEHOLDER_UPDATE = 948, - HF_PLACEHOLDER_TOGGLE = 949, - HF_GET_ASSOCIATED_PATHS = 950, - HF_GETS_WANT_PATH = 951, - GET_RECENT_MOVEMENT_KEYS = 952, - TRACK_RECENT_MOVEMENT_KEYS = 953, - PHYSICS_MOVEMENT_NOTIFICATION_REQUEST = 954, - PHYSICS_MOVEMENT_NOTIFICATION = 955, - MOVE_INVENTORY_SINGLE = 956, - MOVE_INVENTORY_BATCH = 957, - MINI_GAME_SET_PARAMETERS = 958, - MINI_GAME_GET_TEAM_SKILLS = 961, - MINI_GAME_GET_TEAM_SCORE = 963, - MINI_GAME_GET_PLAYER_SCORE = 967, - MINI_GAME_GET_TEAM_COLOR = 972, - MINI_GAME_GET_TEAM_PLAYERS = 975, - MINI_GAME_UPDATE_CLIENT = 976, - MINI_GAME_GET_TEAM = 977, - MINI_GAME_GET_PARAMETERS = 978, - OBJECT_ACTIVATED_CLIENT = 980, - IS_RESURRECTING = 983, - GET_ITEM_OWNER = 984, - GET_STORED_CONFIG_DATA = 985, - SET_STORED_CONFIG_DATA = 986, - ON_PLAYER_RESSURECTED = 988, - PLAYER_RESURRECTION_FINISHED = 989, - TRANSFORM_CHANGELING_BUILD = 990, - RETURN_CHANGELING_BUILD_ID = 991, - SPEND_BRICK_INVENTORY_FOR_LXFML = 992, - BRICK_INVENTORY_FOR_LXFML_SPENT = 993, - REBUILD_BBB_AUTOSAVE_MSG = 995, - SET_BBB_AUTOSAVE = 996, - USE_BBB_INVENTORY = 998, - UN_USE_BBB_MODEL = 999, - BBB_LOAD_ITEM_REQUEST = 1000, - BBB_SAVE_REQUEST = 1001, - BBBLUP_SAVE_REQUEST = 1002, - BBB_GET_METADATA_SOURCE_ITEM = 1003, - BBB_RESET_METADATA_SOURCE_ITEM = 1004, - BBB_SAVE_RESPONSE = 1005, - PLAYER_EXIT = 1006, - SET_PVP_STATUS = 1008, - GET_PVP_STATUS = 1009, - IS_VALID_PVP_TARGET = 1010, - PVP_RENDER_NAME = 1011, - ATTACH_OBJECT = 1012, - DETACH_OBJECT = 1013, - BOUNCE_SUCCEEDED = 1014, - GET_GAME_OBJECT_POINTER = 1015, - PHANTOM_HKX_LOADED = 1016, - DELAY_CREATE_EFFECT = 1017, - CHOICE_BUILD_SELECTION_CONFIRMED = 1018, - NOTIFY_FADE_UP_VIS_COMPLETE = 1019, - ITEM_HAS_NEW_INFO = 1020, - RESET_SECONDARY_ANIMATION = 1021, - GET_PICK_TYPE = 1022, - SET_PICK_TYPE = 1023, - GET_PRIORITY_PICK_LIST_TYPE = 1024, - REQUEST_PICK_TYPE_UPDATE = 1025, - GET_OVERRIDE_PICK_TYPE = 1026, - REQUEST_DISPLAY_OBJECT_INFO = 1027, - REQUEST_SERVER_OBJECT_INFO = 1028, - REQUEST_OBJECT_INFO_AS_XML = 1029, - GET_OBJECT_REPORT_INFO = 1030, - GET_OBJECT_REPORT_WINDOW_CLOSE = 1031, - GET_OBJECT_REPORT_STATUS = 1032, - GET_MISSION_DATA_FOR_OBJECT_REPORT = 1033, - GET_OBJECT_ROLLOVER_INFO = 1034, - PERFORM_ZONE_ANALYSIS = 1035, - UPDATE_HK_VISUAL_IZATION = 1036, - CLEAR_ITEMS_OWNER = 1037, - APPLY_LINEAR_IMPULSE = 1038, - APPLY_ANGULAR_IMPULSE = 1039, - GET_CONTACT_NORMALS = 1040, - IS_WATCHING_FOR_EMOTE = 1041, - NOTIFY_CLIENT_OBJECT = 1042, - DISPLAY_ZONE_SUMMARY = 1043, - ZONE_SUMMARY_DISMISSED = 1044, - GET_PLAYER_ZONE_STATISTIC = 1045, - MODIFY_PLAYER_ZONE_STATISTIC = 1046, - APPLY_EXTERNAL_FORCE = 1049, - GET_APPLIED_EXTERNAL_FORCE = 1050, - ITEM_EQUIPPED = 1052, - ACTIVITY_STATE_CHANGE_REQUEST = 1053, - OVERRIDE_FRICTION = 1054, - ARRANGE_WITH_ITEM = 1055, - CHECK_CAN_BUILD_WITH_ITEM = 1056, - START_BUILDING_WITH_ITEM = 1057, - START_BUILD_SESSION = 1058, - FINISH_BUILD_SESSION = 1059, - DONE_BUILD_SESSION = 1060, - START_ARRANGING_WITH_ITEM = 1061, - FINISH_ARRANGING_WITH_ITEM = 1062, - DONE_ARRANGING_WITH_ITEM = 1063, - START_ARRANGE_MODE = 1064, - ARRANGE_MODE_WITH_ITEM = 1065, - FINISH_ARRANGE_MODE = 1066, - DONE_ARRANGE_MODE = 1067, - SET_BUILD_MODE = 1068, - BUILD_MODE_SET = 1069, - CONFIRM_BUILD_MODE = 1070, - BUILD_MODE_CONFIRMATION = 1071, - BUILD_EXIT_CONFIRMATION = 1072, - SET_BUILD_MODE_CONFIRMED = 1073, - BUILD_MODE_NOTIFICATION = 1074, - BUILD_MODE_NOTIFICATION_REPORT = 1075, - CLIENT_USE_MODULE_ON = 1076, - SET_MODEL_TO_BUILD = 1077, - SPAWN_MODEL_BRICKS = 1078, - CHECK_PRECONDITION = 1079, - CHECK_ALL_PRECONDITIONS = 1080, - NOTIFY_CLIENT_FAILED_PRECONDITION = 1081, - GET_IS_ITEM_EQUIPPED_BY_LOT = 1082, - GET_IS_ITEM_EQUIPPED_BY_ID = 1083, - GET_OBJECT_DIRECTION_VECTORS = 1084, - GET_CASTABLE_SKILLS = 1085, - CHOICEBUILD_COMPLETE = 1086, - GET_MISSION_CHAT = 1087, - GET_MISSION_AUDIO = 1088, - MODULE_EQUIPPED = 1089, - MODULE_DROPPED = 1090, - MODULE_PICKED_UP = 1091, - MODULE_INFO = 1092, - MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1093, - MODULAR_BUILD_BEGIN = 1094, - MODULAR_BUILD_END = 1095, - MODULAR_BUILD_MOVE_AND_EQUIP = 1096, - MODULAR_BUILD_FINISH = 1097, - SET_REGISTRATION_FOR_UI_UPDATE = 1114, - GO_TO_WAYPOINT = 1115, - ARRIVED_AT_DESIRED_WAYPOINT = 1116, - CHECK_WITHIN_BOUNDS = 1117, - ATTACH_TO_BUILD_ASSEMBLY = 1118, - SET_BUILD_ASSEMBLY = 1119, - RESET_BUILD_ASSEMBLY = 1120, - GET_INVENTORY_ITEM_INFO = 1125, - GET_ITEM_DETAILS = 1126, - GET_BUILD_ACTIVATOR = 1127, - GET_MISSION_ANIMATION = 1128, - MISSION_DIALOGUE_CANCELLED = 1129, - MODULE_ASSEMBLY_DB_DATA = 1130, - MODULE_ASSEMBLY_DB_DATA_FOR_CLIENT = 1131, - MODULE_ASSEMBLY_QUERY_DATA = 1132, - MODULE_ASSEMBLY_HKX_LOADED = 1133, - MODULE_ASSEMBLY_NIF_LOADED = 1134, - MODULE_ASSEMBLY_MAIN_NIF_LOADED = 1135, - MODULE_ASSEMBLY_KFM_LOADED = 1136, - GET_PRECONDITION_INFO = 1137, - GET_MODEL_LOT = 1138, - ANIMATION_FINISHED_PRELOADING = 1139, - CHILD_BUILD_ASSEMBLY_COMPLETE = 1140, - CHARACTER_UNSERIALIZED = 1141, - CHARACTER_NEEDS_TRANSITION = 1142, - SET_NEEDS_TRANSITION = 1143, - ECHO_SYNC_SKILL = 1144, - SYNC_SKILL = 1145, - GET_BEHAVIOR_HANDLE = 1146, - ADD_OUTSTANDING_BEHAVIOR = 1147, - REQUEST_SERVER_PROJECTILE_IMPACT = 1148, - OFF_WORLD_IMPACT_REQUEST = 1149, - SERVER_IMPACT_REQUEST = 1150, - DO_CLIENT_PROJECTILE_IMPACT = 1151, - MODULE_ASSEMBLY_PART_INFO = 1152, - GET_BUILD_TYPE = 1153, - CHECK_BUILD_TYPE = 1154, - MODULAR_BUILD_CONVERT_MODEL = 1155, - DO_NPC_SHOWCASE_MODEL_SUBMISSION = 1156, - GET_MISSION_I_DS_LIST = 1157, - SET_SHOWCASE_MISSION_NPC_VALS = 1158, - NOTIFY_SHOWCASE_MISSION_NP_COF_SUCCESS = 1159, - SEND_LUA_NOTIFICATION_REQUEST = 1160, - SEND_LUA_NOTIFICATION_CANCEL = 1161, - ACTIVATOR_TOGGLE = 1162, - MAKE_PHYSICS = 1163, - SET_RESPAWN_GROUP = 1164, - SET_PLAYER_ALLOWED_RESPAWN = 1165, - TOGGLE_SENDING_POSITION_UPDATES = 1166, - TOGGLE_RECEIVING_POSITION_UPDATES = 1167, - GET_ENEMY_PRECONDITIONS = 1168, - START_MODEL_VISUALIZATION = 1169, - PLACE_PROPERTY_MODEL = 1170, - PROPERTY_MODEL_PLACED = 1171, - OPEN_EXHIBIT_REPLACE_MODEL_UI = 1172, - REPLACE_SHOWCASEMODEL = 1173, - CLEAR_UI_HOOK_EXHIBIT_REPLACEMENT = 1174, - ATTACH_FLYTO_SCREEN_POS = 1175, - VEHICLE_GET_DEBUG_INFO = 1176, - VEHICLE_GET_MOVEMENT_INPUT_VALUES = 1177, - ACTIVITY_TIMER_SET = 1178, - ACTIVITY_TIMER_UPDATE = 1179, - ACTIVITY_TIMER_GET = 1180, - ACTIVITY_TIMER_STOP = 1181, - ACTIVITY_TIMER_DONE = 1182, - GET_ATTACK_PRIORITY = 1183, - UI_MESSAGE_SERVER_TO_SINGLE_CLIENT = 1184, - UI_MESSAGE_SERVER_TO_ALL_CLIENTS = 1185, - SET_LOSE_COINS_ON_DEATH = 1186, - LOAD_EFFECTS = 1187, - SET_CUSTOM_BUILD = 1188, - ACTIVITY_TIMER_RESET = 1189, - ACTIVITY_TIMER_STOP_ALL_TIMERS = 1190, - ACTIVITY_TIMER_MODIFY = 1191, - SET_KEYFRAM_TRANSFORM = 1192, - ADD_ACTIVITY_OWNER = 1193, - REMOVE_ACTIVITY_OWNER = 1194, - GET_CURRENT_ACTIVITY_OWNERS = 1195, - TOGGLE_SKILL_DEBUGGING = 1196, - PET_TAMING_TRY_BUILD = 1197, - REPORT_BUG = 1198, - REPORT_OFFENSIVE_MODEL = 1199, - REPORT_OFFENSIVE_PROPERTY = 1200, - GET_ACTIVITY_ID = 1201, - REQUEST_SMASH_PLAYER = 1202, - GET_TIMES_REQUESTED_SMASH = 1203, - RESPONSE_SMASH_PLAYER = 1204, - MODIFY_DAMAGE_ABSORPTION = 1205, - UNCAST_SKILL = 1206, - GET_SHOWCASE_MODEL_READY = 1207, - IS_SKILL_NEEDED = 1208, - GET_COMPONENT_DATA = 1209, - VEHICLE_SET_POWERSLIDE_METHOD = 1210, - SHOWS_NAMETAG = 1211, - FIRE_EVENT_CLIENT_SIDE = 1213, - GET_REQUIRES_NAME_RESUBMISSION = 1216, - SET_REQUIRES_NAME_RESUBMISSION = 1217, - TOGGLE_GM_INVIS = 1218, - GET_GM_INVIS = 1219, - KILLED_PLAYER = 1220, - GET_PICKUP_SKILLS = 1221, - GET_FACTION_SKILL = 1222, - CHANGE_OBJECT_WORLD_STATE = 1223, - GET_OBJECT_WORLD_STATE = 1224, - VISIBILITY_CHANGED = 1225, - MOTION_EFFECT_COMPLETE = 1226, - TOGGLE_FREEZE_MODE = 1227, - SHADER_RENDER_MSG_APPLIED = 1228, - PLAYER_RENAME_REQUEST = 1229, - VEHICLE_LOCK_INPUT = 1230, - VEHICLE_UNLOCK_INPUT = 1231, - SET_AIR_MOVEMENT = 1232, - MOVEMENT_STATE_CHANGED = 1233, - SKILL_MOVEMENT_CANCELLED = 1234, - AIR_MOVEMENT_COMPLETE = 1235, - CANCEL_AIR_MOVEMENT = 1236, - FORCE_MINIFIGURE_TEXTURE_UPDATE = 1237, - RESYNC_EQUIPMENT = 1238, - ADD_COMPONENT_TO_OBJECT = 1239, - VEHICLE_GET_MAX_GAME_SPEED = 1240, - VEHICLE_GET_MAX_GAME_SPEED_WITH_BOOST = 1241, - GET_SPEED_FACTOR = 1242, - FREEZE_INVENTORY = 1243, - ADD_STAT_TRIGGER = 1244, - ADD_STAT_TRIGGER_CHILD = 1245, - CHECK_TRIGGERS_AND_FIRE_IF_NEEDED = 1246, - STAT_EVENT_TRIGGERED = 1247, - GET_CURRENT_SPEED = 1248, - RACING_PLAYER_RANK_CHANGED = 1249, - RACING_PLAYER_WRONG_WAY_STATUS_CHANGED = 1250, - RACING_PLAYER_CROSSED_FINISH_LINE = 1251, - RACING_RESET_PLAYER_TO_LAST_RESET = 1252, + SERVER_TRADE_CANCEL = 883, + SERVER_TRADE_ACCEPT = 884, + GET_TRADE_INFO = 885, + KF_LOADED = 886, + BRICKS_LOADED = 887, + READY_FOR_UPDATES = 888, + SEND_READY_FOR_UPDATES = 889, + SET_LAST_CUSTOM_BUILD = 890, + GET_LAST_CUSTOM_BUILD = 891, + GET_STATUS_EFFECT_BY_ID = 892, + GET_ALL_STATUS_EFFECTS = 893, + CHILD_RENDER_COMPONENT_READY = 894, + NOTIFY_APPEARANCE_CHANGED_MSG = 895, + SET_PHYSICS_MOTION_STATE = 896, + GET_PHYSICS_MOTION_STATE = 897, + ATTACH_GRAYSCALE_EFFECT = 898, + ATTACH_FADE_EFFECT = 899, + ATTACH_CHANGE_RENDER_ENVIRONMENT_EFFECT = 900, + FORCE_MOVEMENT = 901, + CANCEL_FORCE_MOVEMENT = 902, + SET_IGNORE_PROJECTILE_COLLISION = 903, + GET_IGNORE_PROJECTILE_COLLISION = 904, + ORIENT_TO_OBJECT = 905, + ORIENT_TO_POSITION = 906, + ORIENT_TO_ANGLE = 907, + NOTIFY_CLIENT_UGC_MODEL_READY = 909, + NOTIFY_CLIENT_UGC_ICON_READY = 911, + PROPERTY_BUILD_MODE_CHANGED = 912, + PROPERTY_BUILD_MODE_UPDATE = 913, + PROPERTY_DELETION_ACTION = 914, + PROPERTY_MODERATION_STATUS_ACTION = 915, + PROPERTY_MODERATION_STATUS_ACTION_RESPONSE = 916, + PROPERTY_MODERATION_STATUS_UPDATE = 917, + PROPERTY_NEEDS_GM_ATTENTION = 918, + PROPERTY_MODERATION_CHANGED = 919, + INVENTORY_REFRESH_ITEM_DETAILS = 922, + INVENTORY_LOAD_CUSTOM_ICON = 923, + GET_STATUS_EFFECT_BY_TYPE = 924, + RELEASE_CHARGED_SKILL = 925, + PROPERTY_RELOAD_DB = 926, + SET_PLAYER_TARGET = 927, + GET_PLAYER_TARGET = 928, + LOCK_CAMERA_NETWORKED = 929, + MOVE_CAMERA_NETWORKED = 930, + REBUILD_ACTIVATED = 931, + BOUNCE_NOTIFICATION = 932, + REQUEST_CLIENT_BOUNCE = 934, + GET_RECENT_BOUNCED = 935, + SET_RECENT_BOUNCED = 936, + SET_ACTIVE_STATE = 937, + GET_ACTIVE_STATE = 938, + HAS_COMPONENT_TYPE = 939, + GET_COMPONENT_LIST = 940, + RESPONDS_TO_FACTION = 941, + BOUNCER_ACTIVE_STATUS = 942, + HF_ATTRIBUTES_PUSH = 943, + HF_ATTRIBUTES_PULL = 944, + HF_ATTRIBUTES_PATH_DISPLAY = 945, + HF_CONTROLS_PULL = 946, + HF_OBJECT_SELECTED = 947, + HF_PLACEHOLDER_UPDATE = 948, + HF_PLACEHOLDER_TOGGLE = 949, + HF_GET_ASSOCIATED_PATHS = 950, + HF_GETS_WANT_PATH = 951, + GET_RECENT_MOVEMENT_KEYS = 952, + TRACK_RECENT_MOVEMENT_KEYS = 953, + PHYSICS_MOVEMENT_NOTIFICATION_REQUEST = 954, + PHYSICS_MOVEMENT_NOTIFICATION = 955, + MOVE_INVENTORY_SINGLE = 956, + MOVE_INVENTORY_BATCH = 957, + MINI_GAME_SET_PARAMETERS = 958, + MINI_GAME_GET_TEAM_SKILLS = 961, + MINI_GAME_GET_TEAM_SCORE = 963, + MINI_GAME_GET_PLAYER_SCORE = 967, + MINI_GAME_GET_TEAM_COLOR = 972, + MINI_GAME_GET_TEAM_PLAYERS = 975, + MINI_GAME_UPDATE_CLIENT = 976, + MINI_GAME_GET_TEAM = 977, + MINI_GAME_GET_PARAMETERS = 978, + OBJECT_ACTIVATED_CLIENT = 980, + IS_RESURRECTING = 983, + GET_ITEM_OWNER = 984, + GET_STORED_CONFIG_DATA = 985, + SET_STORED_CONFIG_DATA = 986, + ON_PLAYER_RESSURECTED = 988, + PLAYER_RESURRECTION_FINISHED = 989, + TRANSFORM_CHANGELING_BUILD = 990, + RETURN_CHANGELING_BUILD_ID = 991, + SPEND_BRICK_INVENTORY_FOR_LXFML = 992, + BRICK_INVENTORY_FOR_LXFML_SPENT = 993, + REBUILD_BBB_AUTOSAVE_MSG = 995, + SET_BBB_AUTOSAVE = 996, + USE_BBB_INVENTORY = 998, + UN_USE_BBB_MODEL = 999, + BBB_LOAD_ITEM_REQUEST = 1000, + BBB_SAVE_REQUEST = 1001, + BBBLUP_SAVE_REQUEST = 1002, + BBB_GET_METADATA_SOURCE_ITEM = 1003, + BBB_RESET_METADATA_SOURCE_ITEM = 1004, + BBB_SAVE_RESPONSE = 1005, + PLAYER_EXIT = 1006, + SET_PVP_STATUS = 1008, + GET_PVP_STATUS = 1009, + IS_VALID_PVP_TARGET = 1010, + PVP_RENDER_NAME = 1011, + ATTACH_OBJECT = 1012, + DETACH_OBJECT = 1013, + BOUNCE_SUCCEEDED = 1014, + GET_GAME_OBJECT_POINTER = 1015, + PHANTOM_HKX_LOADED = 1016, + DELAY_CREATE_EFFECT = 1017, + CHOICE_BUILD_SELECTION_CONFIRMED = 1018, + NOTIFY_FADE_UP_VIS_COMPLETE = 1019, + ITEM_HAS_NEW_INFO = 1020, + RESET_SECONDARY_ANIMATION = 1021, + GET_PICK_TYPE = 1022, + SET_PICK_TYPE = 1023, + GET_PRIORITY_PICK_LIST_TYPE = 1024, + REQUEST_PICK_TYPE_UPDATE = 1025, + GET_OVERRIDE_PICK_TYPE = 1026, + REQUEST_DISPLAY_OBJECT_INFO = 1027, + REQUEST_SERVER_OBJECT_INFO = 1028, + REQUEST_OBJECT_INFO_AS_XML = 1029, + GET_OBJECT_REPORT_INFO = 1030, + GET_OBJECT_REPORT_WINDOW_CLOSE = 1031, + GET_OBJECT_REPORT_STATUS = 1032, + GET_MISSION_DATA_FOR_OBJECT_REPORT = 1033, + GET_OBJECT_ROLLOVER_INFO = 1034, + PERFORM_ZONE_ANALYSIS = 1035, + UPDATE_HK_VISUAL_IZATION = 1036, + CLEAR_ITEMS_OWNER = 1037, + APPLY_LINEAR_IMPULSE = 1038, + APPLY_ANGULAR_IMPULSE = 1039, + GET_CONTACT_NORMALS = 1040, + IS_WATCHING_FOR_EMOTE = 1041, + NOTIFY_CLIENT_OBJECT = 1042, + DISPLAY_ZONE_SUMMARY = 1043, + ZONE_SUMMARY_DISMISSED = 1044, + GET_PLAYER_ZONE_STATISTIC = 1045, + MODIFY_PLAYER_ZONE_STATISTIC = 1046, + APPLY_EXTERNAL_FORCE = 1049, + GET_APPLIED_EXTERNAL_FORCE = 1050, + ITEM_EQUIPPED = 1052, + ACTIVITY_STATE_CHANGE_REQUEST = 1053, + OVERRIDE_FRICTION = 1054, + ARRANGE_WITH_ITEM = 1055, + CHECK_CAN_BUILD_WITH_ITEM = 1056, + START_BUILDING_WITH_ITEM = 1057, + START_BUILD_SESSION = 1058, + FINISH_BUILD_SESSION = 1059, + DONE_BUILD_SESSION = 1060, + START_ARRANGING_WITH_ITEM = 1061, + FINISH_ARRANGING_WITH_ITEM = 1062, + DONE_ARRANGING_WITH_ITEM = 1063, + START_ARRANGE_MODE = 1064, + ARRANGE_MODE_WITH_ITEM = 1065, + FINISH_ARRANGE_MODE = 1066, + DONE_ARRANGE_MODE = 1067, + SET_BUILD_MODE = 1068, + BUILD_MODE_SET = 1069, + CONFIRM_BUILD_MODE = 1070, + BUILD_MODE_CONFIRMATION = 1071, + BUILD_EXIT_CONFIRMATION = 1072, + SET_BUILD_MODE_CONFIRMED = 1073, + BUILD_MODE_NOTIFICATION = 1074, + BUILD_MODE_NOTIFICATION_REPORT = 1075, + CLIENT_USE_MODULE_ON = 1076, + SET_MODEL_TO_BUILD = 1077, + SPAWN_MODEL_BRICKS = 1078, + CHECK_PRECONDITION = 1079, + CHECK_ALL_PRECONDITIONS = 1080, + NOTIFY_CLIENT_FAILED_PRECONDITION = 1081, + GET_IS_ITEM_EQUIPPED_BY_LOT = 1082, + GET_IS_ITEM_EQUIPPED_BY_ID = 1083, + GET_OBJECT_DIRECTION_VECTORS = 1084, + GET_CASTABLE_SKILLS = 1085, + CHOICEBUILD_COMPLETE = 1086, + GET_MISSION_CHAT = 1087, + GET_MISSION_AUDIO = 1088, + MODULE_EQUIPPED = 1089, + MODULE_DROPPED = 1090, + MODULE_PICKED_UP = 1091, + MODULE_INFO = 1092, + MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1093, + MODULAR_BUILD_BEGIN = 1094, + MODULAR_BUILD_END = 1095, + MODULAR_BUILD_MOVE_AND_EQUIP = 1096, + MODULAR_BUILD_FINISH = 1097, + SET_REGISTRATION_FOR_UI_UPDATE = 1114, + GO_TO_WAYPOINT = 1115, + ARRIVED_AT_DESIRED_WAYPOINT = 1116, + CHECK_WITHIN_BOUNDS = 1117, + ATTACH_TO_BUILD_ASSEMBLY = 1118, + SET_BUILD_ASSEMBLY = 1119, + RESET_BUILD_ASSEMBLY = 1120, + GET_INVENTORY_ITEM_INFO = 1125, + GET_ITEM_DETAILS = 1126, + GET_BUILD_ACTIVATOR = 1127, + GET_MISSION_ANIMATION = 1128, + MISSION_DIALOGUE_CANCELLED = 1129, + MODULE_ASSEMBLY_DB_DATA = 1130, + MODULE_ASSEMBLY_DB_DATA_FOR_CLIENT = 1131, + MODULE_ASSEMBLY_QUERY_DATA = 1132, + MODULE_ASSEMBLY_HKX_LOADED = 1133, + MODULE_ASSEMBLY_NIF_LOADED = 1134, + MODULE_ASSEMBLY_MAIN_NIF_LOADED = 1135, + MODULE_ASSEMBLY_KFM_LOADED = 1136, + GET_PRECONDITION_INFO = 1137, + GET_MODEL_LOT = 1138, + ANIMATION_FINISHED_PRELOADING = 1139, + CHILD_BUILD_ASSEMBLY_COMPLETE = 1140, + CHARACTER_UNSERIALIZED = 1141, + CHARACTER_NEEDS_TRANSITION = 1142, + SET_NEEDS_TRANSITION = 1143, + ECHO_SYNC_SKILL = 1144, + SYNC_SKILL = 1145, + GET_BEHAVIOR_HANDLE = 1146, + ADD_OUTSTANDING_BEHAVIOR = 1147, + REQUEST_SERVER_PROJECTILE_IMPACT = 1148, + OFF_WORLD_IMPACT_REQUEST = 1149, + SERVER_IMPACT_REQUEST = 1150, + DO_CLIENT_PROJECTILE_IMPACT = 1151, + MODULE_ASSEMBLY_PART_INFO = 1152, + GET_BUILD_TYPE = 1153, + CHECK_BUILD_TYPE = 1154, + MODULAR_BUILD_CONVERT_MODEL = 1155, + DO_NPC_SHOWCASE_MODEL_SUBMISSION = 1156, + GET_MISSION_I_DS_LIST = 1157, + SET_SHOWCASE_MISSION_NPC_VALS = 1158, + NOTIFY_SHOWCASE_MISSION_NP_COF_SUCCESS = 1159, + SEND_LUA_NOTIFICATION_REQUEST = 1160, + SEND_LUA_NOTIFICATION_CANCEL = 1161, + ACTIVATOR_TOGGLE = 1162, + MAKE_PHYSICS = 1163, + SET_RESPAWN_GROUP = 1164, + SET_PLAYER_ALLOWED_RESPAWN = 1165, + TOGGLE_SENDING_POSITION_UPDATES = 1166, + TOGGLE_RECEIVING_POSITION_UPDATES = 1167, + GET_ENEMY_PRECONDITIONS = 1168, + START_MODEL_VISUALIZATION = 1169, + PLACE_PROPERTY_MODEL = 1170, + PROPERTY_MODEL_PLACED = 1171, + OPEN_EXHIBIT_REPLACE_MODEL_UI = 1172, + REPLACE_SHOWCASEMODEL = 1173, + CLEAR_UI_HOOK_EXHIBIT_REPLACEMENT = 1174, + ATTACH_FLYTO_SCREEN_POS = 1175, + VEHICLE_GET_DEBUG_INFO = 1176, + VEHICLE_GET_MOVEMENT_INPUT_VALUES = 1177, + ACTIVITY_TIMER_SET = 1178, + ACTIVITY_TIMER_UPDATE = 1179, + ACTIVITY_TIMER_GET = 1180, + ACTIVITY_TIMER_STOP = 1181, + ACTIVITY_TIMER_DONE = 1182, + GET_ATTACK_PRIORITY = 1183, + UI_MESSAGE_SERVER_TO_SINGLE_CLIENT = 1184, + UI_MESSAGE_SERVER_TO_ALL_CLIENTS = 1185, + SET_LOSE_COINS_ON_DEATH = 1186, + LOAD_EFFECTS = 1187, + SET_CUSTOM_BUILD = 1188, + ACTIVITY_TIMER_RESET = 1189, + ACTIVITY_TIMER_STOP_ALL_TIMERS = 1190, + ACTIVITY_TIMER_MODIFY = 1191, + SET_KEYFRAM_TRANSFORM = 1192, + ADD_ACTIVITY_OWNER = 1193, + REMOVE_ACTIVITY_OWNER = 1194, + GET_CURRENT_ACTIVITY_OWNERS = 1195, + TOGGLE_SKILL_DEBUGGING = 1196, + PET_TAMING_TRY_BUILD = 1197, + REPORT_BUG = 1198, + REPORT_OFFENSIVE_MODEL = 1199, + REPORT_OFFENSIVE_PROPERTY = 1200, + GET_ACTIVITY_ID = 1201, + REQUEST_SMASH_PLAYER = 1202, + GET_TIMES_REQUESTED_SMASH = 1203, + RESPONSE_SMASH_PLAYER = 1204, + MODIFY_DAMAGE_ABSORPTION = 1205, + UNCAST_SKILL = 1206, + GET_SHOWCASE_MODEL_READY = 1207, + IS_SKILL_NEEDED = 1208, + GET_COMPONENT_DATA = 1209, + VEHICLE_SET_POWERSLIDE_METHOD = 1210, + SHOWS_NAMETAG = 1211, + FIRE_EVENT_CLIENT_SIDE = 1213, + GET_REQUIRES_NAME_RESUBMISSION = 1216, + SET_REQUIRES_NAME_RESUBMISSION = 1217, + TOGGLE_GM_INVIS = 1218, + GET_GM_INVIS = 1219, + KILLED_PLAYER = 1220, + GET_PICKUP_SKILLS = 1221, + GET_FACTION_SKILL = 1222, + CHANGE_OBJECT_WORLD_STATE = 1223, + GET_OBJECT_WORLD_STATE = 1224, + VISIBILITY_CHANGED = 1225, + MOTION_EFFECT_COMPLETE = 1226, + TOGGLE_FREEZE_MODE = 1227, + SHADER_RENDER_MSG_APPLIED = 1228, + PLAYER_RENAME_REQUEST = 1229, + VEHICLE_LOCK_INPUT = 1230, + VEHICLE_UNLOCK_INPUT = 1231, + SET_AIR_MOVEMENT = 1232, + MOVEMENT_STATE_CHANGED = 1233, + SKILL_MOVEMENT_CANCELLED = 1234, + AIR_MOVEMENT_COMPLETE = 1235, + CANCEL_AIR_MOVEMENT = 1236, + FORCE_MINIFIGURE_TEXTURE_UPDATE = 1237, + RESYNC_EQUIPMENT = 1238, + ADD_COMPONENT_TO_OBJECT = 1239, + VEHICLE_GET_MAX_GAME_SPEED = 1240, + VEHICLE_GET_MAX_GAME_SPEED_WITH_BOOST = 1241, + GET_SPEED_FACTOR = 1242, + FREEZE_INVENTORY = 1243, + ADD_STAT_TRIGGER = 1244, + ADD_STAT_TRIGGER_CHILD = 1245, + CHECK_TRIGGERS_AND_FIRE_IF_NEEDED = 1246, + STAT_EVENT_TRIGGERED = 1247, + GET_CURRENT_SPEED = 1248, + RACING_PLAYER_RANK_CHANGED = 1249, + RACING_PLAYER_WRONG_WAY_STATUS_CHANGED = 1250, + RACING_PLAYER_CROSSED_FINISH_LINE = 1251, + RACING_RESET_PLAYER_TO_LAST_RESET = 1252, RACING_SERVER_SET_PLAYER_LAP_AND_PLANE = 1253, - RACING_SET_PLAYER_RESET_INFO = 1254, - RACING_PLAYER_INFO_RESET_FINISHED = 1255, - RACING_PLAYER_OUT_OF_TRACK_BOUNDS = 1256, - RACING_SYNC_INFO = 1257, - RACING_PLAYER_KEEP_ALIVE = 1258, - RACING_SERVER_KEEP_ALIVE = 1259, - LOCK_NODE_ROTATION = 1260, - GET_PHYSICS_COLLIDABLE = 1261, - SET_PHYSICS_COLOR_FOR_DEBUG = 1262, - GET_PHYSICS_COLOR_FOR_DEBUG = 1263, - SET_PHYSICS_TEXT_AND_STATE_FOR_DEBUG = 1264, - REQUEST_INFO_FOR_PHYSICS_DEBUGGER = 1265, - GET_COLLIDABLE_AT_ADDRESS = 1266, - REQUEST_SERVER_GET_COLLIDABLE_REPORT = 1267, - COLLISION_POINT_ADDED = 1268, - COLLISION_POINT_REMOVED = 1269, - SET_ATTACHED = 1270, - SET_DESTROYABLE_MODEL_BRICKS = 1271, - VEHICLE_SET_POWERSLIDE_LOCK_WHEELS = 1273, - VEHICLE_SET_WHEEL_LOCK_STATE = 1273, - SHOW_HEALTH_BAR = 1274, - GET_SHOWS_HEALTH_BAR = 1275, - NOTIFY_VEHICLE_OF_RACING_OBJECT = 1276, - ENABLE_CLIENT_EQUIP_MODE = 1278, - CLIENT_EQUIP_MODE_WAS_CHANGED = 1279, - VEHICLE_GET_SPAWN_HEIGHT = 1281, - SET_NAME_BILLBOARD_STATE = 1284, - CHECK_TARGETING_REQUIREMENTS = 1285, - VEHICLE_CAN_WRECK = 1286, - ATTACH_RENDER_EFFECT = 1287, - DETACH_RENDER_EFFECT = 1288, - IS_PET_USING_ABILITY = 1289, - SET_BLOCKING = 1290, - GET_BLOCKING = 1291, - UPDATE_BLOCKING = 1292, - CHECK_DAMAGE_RESULTS = 1293, - GET_OBJECT_IS_IN_RENDER_PIPE = 1294, - ATTACH_MOTION_FX_ARC = 1295, - PLAYER_REACHED_RESPAWN_CHECKPOINT = 1296, - GET_LAST_RESPAWN_CHECKPOINT = 1297, - GET_VEHICLE_DEBUG_COLLISIONS = 1298, - VISITING_PROPERTY = 1299, - HANDLE_UGC_POST_DELETE_BASED_ON_EDIT_MODE = 1300, - HANDLE_UGC_POST_CREATE_BASED_ON_EDIT_MODE = 1301, - WORLD_CHECK_RESPONSE = 1302, - ADD_DAMAGE_REDUCTION = 1303, - REMOVE_DAMAGE_REDUCTION = 1304, - PROPERTY_CONTENTS_FROM_CLIENT = 1305, - GET_MODELS_ON_PROPERTY = 1306, - IS_SHOWCASE_DISPLAY_PEDESTAL = 1307, - MATCH_REQUEST = 1308, - MATCH_RESPONSE = 1309, - MATCH_UPDATE = 1310, - IS_DEFAULT_SKILL_ACTIVE = 1311, - PROPERTY_EDITOR_CARRY = 1312, - GET_LOOT_OWNER_ID = 1313, - GET_ENEMY_LOOT_TAG = 1314, - GET_NUM_SPAWNED_BRICKS = 1315, - SET_ITEM_EQUIP_TRANSFORM = 1316, - GET_ITEM_EQUIP_TRANSFORM = 1317, - GET_PROPERTY_BUDGET_INFO = 1318, - CHATBOX_IS_INIT = 1319, - GET_SPAWNED_I_DS = 1320, - GET_IMMUNITY = 1321, - GET_GM_IMMUNITY = 1322, - PROCESS_REMOTE_SLASH_COMMAND = 1323, - IS_FRIEND_MSG = 1324, - RACING_PLAYER_EVENT = 1325, - GET_PROPERTY_EDIT_VALID = 1326, - REFRESH_RENDER_ASSET = 1327, - VEHICLE_APPLY_STAT_CHANGE = 1328, - ZONE_LOADED_INFO = 1329, - B3_INTERFACE_ACTION = 1330, - RACING_STAT_MODIFIERS_FROM_CLIENT = 1332, - GET_RACING_STAT_MODIFIERS = 1333, - SET_RACING_STAT_MODIFIERS = 1334, - GET_RACING_LICENSE_LEVEL = 1335, - ADD_EQUIP_CAST = 1336, - SHOW_BILLBOARD_INTERACT_ICON = 1337, - CHANGE_IDLE_FLAGS = 1338, - GET_ANIMATION_FLAG = 1339, - VEHICLE_ADD_PASSIVE_BOOST_ACTION = 1340, - VEHICLE_REMOVE_PASSIVE_BOOST_ACTION = 1341, - NOTIFY_SERVER_VEHICLE_ADD_PASSIVE_BOOST_ACTION = 1342, - NOTIFY_SERVER_VEHICLE_REMOVE_PASSIVE_BOOST_ACTION = 1343, - VEHICLE_ADD_SLOWDOWN_ACTION = 1344, - VEHICLE_REMOVE_SLOWDOWN_ACTION = 1345, - NOTIFY_SERVER_VEHICLE_ADD_SLOWDOWN_ACTION = 1346, - NOTIFY_SERVER_VEHICLE_REMOVE_SLOWDOWN_ACTION = 1347, - FORCE_UPDATE_ANIMATIONS = 1348, - MATCH_GET_DATA_FOR_PLAYER = 1349, - BUYBACK_FROM_VENDOR = 1350, - SET_INVENTORY_FILTER = 1351, - GET_INVENTORY_FILTER = 1352, - GET_INVENTORY_GROUPS = 1353, - GET_INVENTORY_GROUP = 1354, - UPDATE_INVENTORY_GROUP = 1355, - UPDATE_INVENTORY_UI = 1356, - UPDATE_INVENTORY_GROUP_CONTENTS = 1357, - CAN_REMOVE_ITEM_FROM_INVENTORY = 1362, - DRIVE_THIS_CAR = 1363, - VEHICLE_CAN_ADD_ACTIVE_BOOST = 1364, - VEHICLE_ADD_ACTIVE_BOOST = 1365, - SET_PROPERTY_ACCESS = 1366, + RACING_SET_PLAYER_RESET_INFO = 1254, + RACING_PLAYER_INFO_RESET_FINISHED = 1255, + RACING_PLAYER_OUT_OF_TRACK_BOUNDS = 1256, + RACING_SYNC_INFO = 1257, + RACING_PLAYER_KEEP_ALIVE = 1258, + RACING_SERVER_KEEP_ALIVE = 1259, + LOCK_NODE_ROTATION = 1260, + GET_PHYSICS_COLLIDABLE = 1261, + SET_PHYSICS_COLOR_FOR_DEBUG = 1262, + GET_PHYSICS_COLOR_FOR_DEBUG = 1263, + SET_PHYSICS_TEXT_AND_STATE_FOR_DEBUG = 1264, + REQUEST_INFO_FOR_PHYSICS_DEBUGGER = 1265, + GET_COLLIDABLE_AT_ADDRESS = 1266, + REQUEST_SERVER_GET_COLLIDABLE_REPORT = 1267, + COLLISION_POINT_ADDED = 1268, + COLLISION_POINT_REMOVED = 1269, + SET_ATTACHED = 1270, + SET_DESTROYABLE_MODEL_BRICKS = 1271, + VEHICLE_SET_POWERSLIDE_LOCK_WHEELS = 1272, + VEHICLE_SET_WHEEL_LOCK_STATE = 1273, + SHOW_HEALTH_BAR = 1274, + GET_SHOWS_HEALTH_BAR = 1275, + NOTIFY_VEHICLE_OF_RACING_OBJECT = 1276, + ENABLE_CLIENT_EQUIP_MODE = 1278, + CLIENT_EQUIP_MODE_WAS_CHANGED = 1279, + VEHICLE_GET_SPAWN_HEIGHT = 1281, + SET_NAME_BILLBOARD_STATE = 1284, + CHECK_TARGETING_REQUIREMENTS = 1285, + VEHICLE_CAN_WRECK = 1286, + ATTACH_RENDER_EFFECT = 1287, + DETACH_RENDER_EFFECT = 1288, + IS_PET_USING_ABILITY = 1289, + SET_BLOCKING = 1290, + GET_BLOCKING = 1291, + UPDATE_BLOCKING = 1292, + CHECK_DAMAGE_RESULTS = 1293, + GET_OBJECT_IS_IN_RENDER_PIPE = 1294, + ATTACH_MOTION_FX_ARC = 1295, + PLAYER_REACHED_RESPAWN_CHECKPOINT = 1296, + GET_LAST_RESPAWN_CHECKPOINT = 1297, + GET_VEHICLE_DEBUG_COLLISIONS = 1298, + VISITING_PROPERTY = 1299, + HANDLE_UGC_POST_DELETE_BASED_ON_EDIT_MODE = 1300, + HANDLE_UGC_POST_CREATE_BASED_ON_EDIT_MODE = 1301, + WORLD_CHECK_RESPONSE = 1302, + ADD_DAMAGE_REDUCTION = 1303, + REMOVE_DAMAGE_REDUCTION = 1304, + PROPERTY_CONTENTS_FROM_CLIENT = 1305, + GET_MODELS_ON_PROPERTY = 1306, + IS_SHOWCASE_DISPLAY_PEDESTAL = 1307, + MATCH_REQUEST = 1308, + MATCH_RESPONSE = 1309, + MATCH_UPDATE = 1310, + IS_DEFAULT_SKILL_ACTIVE = 1311, + PROPERTY_EDITOR_CARRY = 1312, + GET_LOOT_OWNER_ID = 1313, + GET_ENEMY_LOOT_TAG = 1314, + GET_NUM_SPAWNED_BRICKS = 1315, + SET_ITEM_EQUIP_TRANSFORM = 1316, + GET_ITEM_EQUIP_TRANSFORM = 1317, + GET_PROPERTY_BUDGET_INFO = 1318, + CHATBOX_IS_INIT = 1319, + GET_SPAWNED_I_DS = 1320, + GET_IMMUNITY = 1321, + GET_GM_IMMUNITY = 1322, + PROCESS_REMOTE_SLASH_COMMAND = 1323, + IS_FRIEND_MSG = 1324, + RACING_PLAYER_EVENT = 1325, + GET_PROPERTY_EDIT_VALID = 1326, + REFRESH_RENDER_ASSET = 1327, + VEHICLE_APPLY_STAT_CHANGE = 1328, + ZONE_LOADED_INFO = 1329, + B3_INTERFACE_ACTION = 1330, + RACING_STAT_MODIFIERS_FROM_CLIENT = 1332, + GET_RACING_STAT_MODIFIERS = 1333, + SET_RACING_STAT_MODIFIERS = 1334, + GET_RACING_LICENSE_LEVEL = 1335, + ADD_EQUIP_CAST = 1336, + SHOW_BILLBOARD_INTERACT_ICON = 1337, + CHANGE_IDLE_FLAGS = 1338, + GET_ANIMATION_FLAG = 1339, + VEHICLE_ADD_PASSIVE_BOOST_ACTION = 1340, + VEHICLE_REMOVE_PASSIVE_BOOST_ACTION = 1341, + NOTIFY_SERVER_VEHICLE_ADD_PASSIVE_BOOST_ACTION = 1342, + NOTIFY_SERVER_VEHICLE_REMOVE_PASSIVE_BOOST_ACTION = 1343, + VEHICLE_ADD_SLOWDOWN_ACTION = 1344, + VEHICLE_REMOVE_SLOWDOWN_ACTION = 1345, + NOTIFY_SERVER_VEHICLE_ADD_SLOWDOWN_ACTION = 1346, + NOTIFY_SERVER_VEHICLE_REMOVE_SLOWDOWN_ACTION = 1347, + FORCE_UPDATE_ANIMATIONS = 1348, + MATCH_GET_DATA_FOR_PLAYER = 1349, + BUYBACK_FROM_VENDOR = 1350, + SET_INVENTORY_FILTER = 1351, + GET_INVENTORY_FILTER = 1352, + GET_INVENTORY_GROUPS = 1353, + GET_INVENTORY_GROUP = 1354, + UPDATE_INVENTORY_GROUP = 1355, + UPDATE_INVENTORY_UI = 1356, + UPDATE_INVENTORY_GROUP_CONTENTS = 1357, + CAN_REMOVE_ITEM_FROM_INVENTORY = 1362, + DRIVE_THIS_CAR = 1363, + VEHICLE_CAN_ADD_ACTIVE_BOOST = 1364, + VEHICLE_ADD_ACTIVE_BOOST = 1365, + SET_PROPERTY_ACCESS = 1366, ZONE_PROPERTY_MODEL_PLACED = 1369, - ZONE_PROPERTY_MODEL_ROTATED = 1370, - ZONE_PROPERTY_MODEL_REMOVED_WHILE_EQUIPPED = 1371, - ZONE_PROPERTY_MODEL_EQUIPPED = 1372, + ZONE_PROPERTY_MODEL_ROTATED = 1370, + ZONE_PROPERTY_MODEL_REMOVED_WHILE_EQUIPPED = 1371, + ZONE_PROPERTY_MODEL_EQUIPPED = 1372, ZONE_PROPERTY_MODEL_PICKED_UP = 1373, ZONE_PROPERTY_MODEL_REMOVED = 1374, - GET_VERSIONING_INFO = 1381, - OPEN_UG_BEHAVIOR_UI = 1382, - VEHICLE_NOTIFY_HIT_SMASHABLE = 1383, - GET_TETHER_RADIUS = 1384, - VEHICLE_NOTIFY_HIT_EXPLODER = 1385, - CHECK_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1386, - REQUEST_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1387, - CONFIGURE_RACING_CONTROL_CLIENT = 1389, - NOTIFY_RACING_CLIENT = 1390, - RACING_PLAYER_HACK_CAR = 1391, - RACING_PLAYER_LOADED = 1392, - RACING_CLIENT_READY = 1393, - POSSESSION_FINISHED_ATTACH = 1394, - UPDATE_CHAT_MODE = 1395, - VEHICLE_NOTIFY_FINISHED_RACE = 1396, - EQUIPPED_ITEM_STARTUP = 1397, - FACTION_TRIGGER_ITEM_EQUIPPED = 1400, - FACTION_TRIGGER_ITEM_UNEQUIPPED = 1401, - TOGGLE_PROPERTY_BEHAVIORS = 1402, - GET_UG_OBJECT_INFO = 1405, - RESET_PROPERTY_BEHAVIORS = 1406, - IS_PROPERTY_MODEL_RESET = 1407, - SET_UG_OBJECT_NAME_AND_DESCRIPTION = 1408, - SET_CONSUMABLE_ITEM = 1409, - VEHICLE_GET_CURRENT_LAP = 1410, - GET_UGID = 1411, - SET_UGID = 1412, - UGID_CHANGED = 1413, - RACING_GET_CURRENT_LAP_FOR_PLAYER = 1414, - SUB_ITEM_UN_EQUIPPED = 1415, - SET_CUSTOM_DROP_SHADOW_TEXTURE = 1416, - GET_PLAYER_KIT_FACTION = 1418, - USED_INFORMATION_PLAQUE = 1419, - RACING_ENABLE_WRONG_WAY_RESET = 1420, - RACING_TOGGLE_RUBBER_BANDING = 1421, - GET_RACING_CONTROL_DEBUG_INFO = 1422, - SET_PROPERTY_BOUNDS_VISIBILITY = 1423, - SET_PROPERTY_VENDOR_VISIBILITY = 1424, - SET_EQUIP_STATE = 1425, - NOTIFY_COMBAT_AI_STATE_CHANGE = 1426, - SET_PROPERTY_MODEL_INTERACTIVE = 1430, - SERVER_STATE_NOTIFY = 1431, - GET_SERVER_STATE = 1432, - GET_ICON_FOR_PROXIMITY = 1433, - GET_LEGO_CLUB_MEMBERSHIP_STATUS = 1434, - SET_STATUS_IMMUNITY = 1435, - GET_STATUS_IMMUNITY = 1436, - TEAM_IS_MEMBER = 1437, - ACTIVATE_BRICK_MODE = 1438, - GET_BUILD_OBJECT_ID = 1439, - SET_ANIMATION_ENABLED = 1444, - PAUSE_COOLDOWNS = 1446, - FORCE_UPDATE_RENDER_NODE = 1447, - SET_PET_NAME_MODERATED = 1448, - TOGGLE_STRAFE_MODE = 1449, - SET_SCHEME_SPEED_SCALE = 1450, - CANCEL_SKILL_CAST = 1451, - CHECK_PLAYER_ASSEMBLY_FOR_UNIQUE_MODULE_BY_LOT = 1454, - MODULE_ASSEMBLY_DB_DATA_TO_LUA = 1455, - IS_ALLY = 1458, - MODIFY_LEGO_SCORE = 1459, - GET_LEGO_SCORE = 1460, - GET_PLAYER_LEVEL = 1461, - NOTIFY_LEGO_SCORE_UPDATE = 1462, - SET_LEGO_SCORE = 1463, - UPDATE_BEHAVIOR_EXECUTION_DETAILS = 1466, - RESTORE_TO_POST_LOAD_STATS = 1468, - PICKUP_OBJECT_ERROR = 1469, - CHECK_AND_SHOW_INVENTORY_FULL_TIP = 1470, - SET_RAIL_MOVEMENT = 1471, - START_RAIL_MOVEMENT = 1472, - SET_UP_VECTOR = 1473, - CANCEL_RAIL_MOVEMENT = 1474, - GET_RAIL_INFO = 1475, - CLIENT_RAIL_MOVEMENT_READY = 1476, - PLAYER_RAIL_ARRIVED_NOTIFICATION = 1477, - NOTIFY_RAIL_ACTOVATOR_STATE_CHANGE = 1478, - REQUEST_RAIL_ACTIVATOR_STATE = 1479, - NOTIFY_REWARD_MAILED = 1480, - UPDATE_PLAYER_STATISTIC = 1481, - IS_IN_COMBAT = 1482, - IS_PRIMITIVE_MODEL_MSG = 1483, - SCALE_PRIMITICE_MODEL_MSG = 1484, - MODIFY_GHOSTING_DISTANCE = 1485, - PRIMITIVE_MODEL_CHANGED_MSG = 1487, - GET_PROPRTY_CLONE_ID = 1488, - REQUEST_LEAVE_PROPERTY = 1489, - REQUERY_PROPERTY_MODELS = 1491, - GET_BEHAVIOR_COUNT = 1492, - UPDATE_BEHAVIOR_CONTROLS = 1493, - MODULE_ASSEMBLY_LXFML_LOADED = 1494, - REQUEST_ASSEMBLED_LXFML = 1495, - ASSEMBLED_LXFML_LOADED = 1496, - GET_REORIENT_UP_VECTOR = 1497, - MODULAR_ASSEMBLY_NIF_COMPLETED = 1498, - CHARACTER_DISCONNECT_BEFORE_CREATE = 1499, - SEND_LAUNCH_TO_PREVIOUS_ZONE_TO_CLIENT = 1500, - ROCKETLAUNCH_REQUEST_DEFAULT_MAP_ID = 1501, - BEGIN_LAUNCH = 1502, - PROCESS_CLAIM_CODES = 1503, - GET_LAST_ZONE_ID = 1504, - ADD_RUN_SPEED_MODIFIER = 1505, - REMOVE_RUN_SPEED_MODIFIER = 1506, - SKILL_EVENT_FIRED = 1507, - SEND_HOT_PROPERTY_DATA = 1510, - GET_HOT_PROPERTY_DATA = 1511, - GET_EQUIPPED_ITEMS_IN_SET = 1512, - IS_ITEM_IN_SET = 1513, - GET_INVENTORY_TYPE_FOR_LOT = 1514, - GET_BANK_TYPE_FOR_LOT = 1515, - NOTIFY_NOT_ENOUGH_INV_SPACE = 1516, - IMPORT_MODEL_TO_BBB = 1517, - SEARCH_NEARBY_OBJECTS = 1518, - SEARCH_NEARBY_OBJECTS_REQUEST_BY_LOT = 1519, - REQUEST_OBJECT_POSITION_BY_ID = 1520, - SEARCH_NEARBY_OBJECTS_REQUEST_BY_COMPONENT = 1521, - SEARCH_NEARBY_OBJECTS_RESPONSE = 1522, - BROADCAST_NON_STANDARD_COLLISIONS = 1523, - GET_REGISTERED_NON_STANDARD_COLLISION_GROUP = 1524, - BROADCAST_CRUSHED_NOTIFICATIONS = 1525, - GET_REGISTERED_CRUSHED_COLLISION_GROUPS = 1526, - IS_BEING_CRUSHED = 1527, - GET_SUPPORTING_OBJECT = 1528, - TREAT_RIGID_BODY_COLLSIONS_AS_FIXED = 1529, - BROADCAST_TELEPORTED_WITHIN_NOTIFICATION = 1530, - GET_REGISTERED_TELEPORTED_WITHIN_OBJECT_GROUP = 1531, - GET_INTERPENTRATING_INFORMATION = 1532, - OBJECT_TELEPORTED_WITHIN = 1533, - SET_PHYSICS_SOLVER_PROPERTIES = 1534, - HAS_BEHAVIORS = 1535, - PLAY_BEHAVIOR_SOUND = 1536, - GET_PLAYER_BEHAVIOR_TIER = 1537, - GET_EMOTE_ANIMATION_TIME = 1538, - GET_CHARACTER_STAT_TRACKING = 1539, - PLAYER_INVENTORY_READY = 1540, - SET_PRECONDITIONS = 1541, - DETACH_SHADOW = 1542, - GET_LOOT_INFO = 1543, - GET_PLAYERS_ON_PROPERTY = 1544, - PROPERTY_SPAWN_BY_BEHAVIOR = 1545, - NOTIFY_PROPERTY_OF_EDIT_MODE = 1546, - UPDATE_PROPERTY_PERFORMANCE_COST = 1547, - GET_PROPERTY_PERFORMANCE_COST = 1548, - GET_INVENTORY_ITEM_WITH_SUBKEY = 1549, - DISPLAY_PROPERTY_SUMMARY_SCREEN = 1550, - VALIDATE_BBB_MODEL = 1551, - BBB_MODEL_VALIDATION = 1552, - PROPERTY_ENTRANCE_BEGIN = 1553, - CHECK_LIST_OF_PRECONDITIONS_FROM_LUA = 1554, - GET_PROPERTYIN_ZONE = 1555, - GET_ZONE_ID_FROM_MULTI_ZONE_ENTRANCE = 1556, - TEAM_SET_LEADER = 1557, - TEAM_INVITE_CONFIRM = 1558, - TEAM_GET_STATUS_RESPONSE = 1559, - MINI_GAME_ENABLE_LOCAL_TEAMS = 1560, - TEAM_INVITE_FINAL_RESPONSE = 1561, - TEAM_ADD_PLAYER = 1562, - TEAM_REMOVE_PLAYER = 1563, - TEAM_CREATE_LOCAL = 1564, - TEAM_GET_LOOT = 1565, - TEAM_SET_LOOT = 1566, - SET_ZERO_IMPULSE_AGAINST_COLLISION_GROUPS = 1567, - SET_CENTER_OF_MASS_TO_PHYSICAL_CENTER = 1568, - SET_INERTIA_INVERSE = 1569, - ADD_REMOVE_CLIMBING_LISTENER = 1570, - GET_INVENTORYITEM_DETAILS = 1571, - PERFORM_CLIENT_SIDE_DEATH = 1572, - LEGO_CLUB_ACCESS_RESULT = 1573, - VEHICLE_GET_IS_REVERSING = 1574, - CHECK_CLAIM_CODE = 1575, - GET_HOLIDAY_EVENT = 1576, - SET_EMOTES_ENABLED = 1577, - GET_EMOTES_ENABLED = 1578, - FREEZE_ANIMATION = 1579, - LOCALIZED_ANNOUNCEMENT_SERVER_TO_SINGLE_CLIENT = 1580, - ANCHOR_FX_NODE = 1581, - WS_GET_FRIEND_LIST_MESSAGE = 1582, - WS_ADD_FRIEND_RESPONSE = 1583, - WS_REMOVE_FRIEND_REPSONSE = 1584, - WS_UPDATE_FRIEND_STATUS = 1585, - WS_UPDATE_FRIEND_NAME = 1586, - IS_BEST_FRIEND = 1587, - TEAM_NOTIFY_UPDATE_MISSION_TASK = 1588, - VEHICLE_ADD_SLIPPERY_ACTION = 1589, - VEHICLE_REMOVE_SLIPPERY_ACTION = 1590, - SET_RESURRECT_RESTORE_VALUES = 1591, - GET_MASS = 1592, - SET_PROPERTY_MODERATION_STATUS = 1593, - UPDATE_PROPERTY_MODEL_DEFAULTS = 1594, - UPDATE_PROPERTYMODEL_COUNT = 1595, - GET_PROPERTY_MODEL_COUNT = 1596, - IS_PLAYER_LOADED = 1597, - ATTACH_RENDER_EFFECT_FROM_LUA = 1598, - DETACH_RENDER_EFFECT_FROM_LUA = 1599, - TEAM_IS_LOCAL = 1600, - CREATE_CAMERA_PARTICLES = 1602, - SET_SMASHABLE_GRAVITY_FACTOR = 1603, - VEHICLE_SET_SURFACE_TYPE_OVERRIDE = 1604, - VEHICLE_NOTIFY_HIT_IMAGINATION = 1605, - VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER = 1606, - GET_SPAWNED_OBJECT_SPAWNER_INFO = 1607, - SAVE_PROPERTY = 1608, - SET_PROPERTY_DIRTY = 1609, - GET_PROPERTY_DIRTY = 1610, - GET_MODEL_LIST_FROM_PLAQUE = 1611, - GET_ORIGINAL_POSITION_AND_ROTATION = 1612, - VEHICLE_SET_MASS_FOR_COLLISION = 1613, - GET_INVENTORY_GROUP_COUNT = 1614, - GET_LATEST_CHAT_CHANNEL_USED = 1615, - SET_SUGGEST_LIST_LANGUAGE = 1616, - VEHICLE_STOP_BOOST = 1617, - START_CELEBRATION_EFFECT = 1618, - LOCK_PLAYER = 1619, - VEHICLE_IS_INPUT_LOCKED = 1620, - GET_MULTI_NODE = 1621, - RENEW_PROPERTY = 1622, - RENEW_PROPERTY_RESULT = 1623, - CHARGE_ACTIVITY_COST = 1624, - CAN_RECEIVE_LOOT = 1625, - JOIN_PLAYER_FACTION = 1626, - SET_PROXIMITY_UPDATE_RATE = 1627, - BBB_MODELS_TO_SAVE = 1628, - BELONGS_TO_FACTION = 1629, - MODIFY_FACTION = 1630, - FACTION_UPDATE = 1631, - CELEBRATION_COMPLETED = 1632, - PLAY_PRIMARY_MODULE_SOUNDS = 1633, - STOP_PRIMARY_MODULE_SOUNDS = 1634, - REQUEST_TEAM_PLAYER_UI_UPDATE = 1635, - SET_LOCAL_TEAM = 1636, - TEAM_GET_WORLD_MEMBERS_IN_RADIUS = 1637, - GET_PARENTAL_LEVEL = 1638, - GET_OBJECTS_MESSAGE_HANDLERS = 1639, - PROPERTY_FEATURED = 1640, - PROPERTY_NOTIFY_MODEL_SPAWNED = 1641, - SERVER_DONE_LOADING_ALL_OBJECTS = 1642, - GET_DONATION_TOTAL = 1643, - UPDATE_DONATION_VALUES = 1644, - DELAYED_DELETE_DROP_EFFECT_BRICK = 1645, - SET_CAMERA_UNLOCK_ROTATION_STATE = 1646, - ADD_BUFF = 1647, - REMOVE_BUFF = 1648, - CHECK_FOR_BUFF = 1649, - TEAM_MEMBERS_DISOWNS_LOOT = 1650, - GET_WHEEL_TEMPLATE = 1651, - ADD_SKILL_IN_PROGRESS = 1652, - REMOVE_SKILL_IN_PROGRESS = 1653, - SET_OVERHEAD_ICON_OFFSET = 1654, - SET_BILLBOARD_OFFSET = 1655, - SET_CHAT_BUBBLE_OFFSET = 1656, - SET_NO_TEAM_INVITES = 1657, - RESET_MODEL_TO_DEFAULTS = 1658, - IS_PROPERTY_IN_EDIT_MODE = 1659, - GET_OBJECTS_IN_PHYSICS_BOUNDS = 1660, - ENABLE_LU_REMOTE = 1661, - SET_IS_USING_FREE_TRIAL = 1662, - GET_IS_USING_FREE_TRIAL = 1663, - GET_ACCOUNT_FREE_TRIAL_MODE = 1664, - TOGGLE_INVENTORY_ITEM_LOCK = 1665, - REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666, - RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667, - REMOVE_SUB_COMPONENT = 1668, - TEAM_GET_LOOT_MEMBERS = 1669, - GET_FACTION_TOKEN_TYPE = 1670, - GET_SUBSCRIPTION_PRICING = 1671, - INFORM_AFK = 1672, - OVERHEAD_INDICATOR_CREATED = 1673, - SET_OVERHEAD_INDICATOR_GRID_LOCATION = 1674, - PLAYSTREAM_LOAD_PENDING = 1675, - PLAYER_SET_CAMERA_CYCLING_MODE = 1676, - PLAYER_GET_CAMERA_CYCLING_MODE = 1677, - FORCE_CAMERA_TARGET_CYCLE = 1678, - GET_OBJECT_CONFIG_DATA = 1679, - GET_OBJECT_CONFIG_DATA_NON_CONST = 1680, - SCOPE_CHANGED = 1681, - SET_ALLOW_JUMP_WITHOUT_SUPPORT = 1682, - GET_ALLOW_JUMP_WITHOUT_SUPPORT = 1683, - SET_JUMP_HEIGHT_SCALE = 1684, - GET_JUMP_HEIGHT_SCALE = 1685, - SET_VELOCITY_RESISTANCE = 1686, - GET_VELOCITY_RESISTANCE = 1687, - GATE_RUSH_VEHICLE_HIT_GATE = 1688, - GATE_RUSH_PLAYER_COLLECTED_GATE = 1689, - GATE_RUSH_ADD_GATE = 1690, - GATE_RUSH_REMOVE_GATE = 1691, - NOTIFY_VEHICLE_UPDATED = 1692, - VEHICLE_NOTIFY_HIT_WEAPON_POWERUP = 1693, - VEHICLE_NOTIFY_HIT_WEAPON_POWERUP_SERVER = 1694, - LOCAL_PLAYER_TARGETED = 1696, - SKILL_COUNT_CHANGED = 1697, - DO_YAW_ROTATION = 1698, - DO_PITCH_ROTATION = 1699, - DO_ROLL_ROTATION = 1700, - GET_CURRENT_LOOT_MATRIX = 1701, - SEND_MULTI_MISSION_OFFER_UPDATE_I_DS = 1702, - SET_AIR_SPEED_VALUES = 1703, - USE_LAUNCHER = 1704, - START_LAUNCHER = 1705, - STOP_LAUNCHER = 1706, - CAN_USE_JET_PACK = 1707, - JET_PACK_STATE_CHANGED = 1708, - TURN_OFF_JET_PACK = 1709, - ADD_PLAYER_JET_PACK_PAD = 1710, - SET_JET_PACK_WARNING = 1711, - JET_PACK_DISABLED = 1712, - JET_PACK_PAD_ENTERED = 1713, - UPDATE_RENDER_POSSESSION_FLAG = 1714, - POSSESSABLE_GET_ATTACH_OFFSET = 1715, - ATTEMPT_TO_CRAFT_ITEM = 1718, - CRAFT_ATTEMPT_RESPONSE = 1719, - SET_C_SCORE = 1720, - FILL_IN_RENDERER = 1721, - TOGGLE_CRAFTING_WINDOW = 1722, - REMOVE_TEAM_BUFFS = 1724, - REQUEST_FREE_TRIAL_STATUS_REFRESH = 1725, - REMOVE_BUFFS_APPLIED_BY_OBJECT = 1726, - SET_MOUNT_INVENTORY_ID = 1727, - GET_MOUNT_INVENTORY_ID = 1728, - GET_BUILD_CINEMATIC_TIME_REMAINING = 1730, - JET_PACK_FLYING = 1731, - NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734, - NOTIFY_LEVEL_REWARDS = 1735, - CHARACTER_VERSION_CHANGED = 1736, - SET_FREE_TRIAL_RENAME_AVAILABLE = 1737, - SET_PROJECTILE_LAUNCHER_PARAMS = 1738, - RACE_PRECOUNTDOWN_DONE = 1739, - CHECK_INVITE_SPAMMING = 1740, - GET_RESPAWN_VOLUME_INFO = 1741, - INVITE_ACCEPTED = 1742, - TELEPORT_TO_NEAREST_RESPAWN = 1743, - SET_SKILL_CANCEL_ON_MOVE = 1744, - CANCEL_MOVE_SKILL = 1745, - SERVER_CANCEL_MOVE_SKILL = 1746, - CLIENT_CANCEL_MOVE_SKILL = 1747, - END_LAUNCH_SEQUENCE = 1748, - CANCEL_QUEUE = 1749, - UPDATE_PROJECTILE_LAUNCHER_ROTATION = 1750, - GET_CHARACTER_VERSION_INFO = 1751, - GET_CON_INFO = 1753, - GET_SKILLS_FOR_LOT = 1755, - DISMOUNT_COMPLETE = 1756, - MOUNT_FAILURE_RESPONSE = 1757, - CLEAR_BILLBOARD_OFFSET = 1758, - GET_INVENTORY_ITEM_ANIMATION_FLAG = 1759, - SET_JET_PACK_ALLOWED = 1760, - GET_BUILD_TIME_DETAILS = 1761, - USE_SKILL_SET = 1762, - SET_SKILL_SET_POSSESSOR = 1763, - POPULATE_ACTION_BAR = 1764, - GET_COMPONENT_TEMPLATE_ID = 1765, - GET_POSSESSABLE_SKILL_SET = 1766, - MARK_INVENTORY_ITEM_AS_ACTIVE = 1767, - UPDATE_FORGED_ITEM = 1768, - CAN_ITEMS_BE_REFORGED = 1769, - NOTIFY_CLIENT_RAIL_START_FAILED = 1771, - GET_IS_ON_RAIL = 1772 + GET_VERSIONING_INFO = 1381, + OPEN_UG_BEHAVIOR_UI = 1382, + VEHICLE_NOTIFY_HIT_SMASHABLE = 1383, + GET_TETHER_RADIUS = 1384, + VEHICLE_NOTIFY_HIT_EXPLODER = 1385, + CHECK_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1386, + REQUEST_NEAREST_ROCKET_LAUNCH_PRE_CONDITIONS = 1387, + CONFIGURE_RACING_CONTROL_CLIENT = 1389, + NOTIFY_RACING_CLIENT = 1390, + RACING_PLAYER_HACK_CAR = 1391, + RACING_PLAYER_LOADED = 1392, + RACING_CLIENT_READY = 1393, + POSSESSION_FINISHED_ATTACH = 1394, + UPDATE_CHAT_MODE = 1395, + VEHICLE_NOTIFY_FINISHED_RACE = 1396, + EQUIPPED_ITEM_STARTUP = 1397, + FACTION_TRIGGER_ITEM_EQUIPPED = 1400, + FACTION_TRIGGER_ITEM_UNEQUIPPED = 1401, + TOGGLE_PROPERTY_BEHAVIORS = 1402, + GET_UG_OBJECT_INFO = 1405, + RESET_PROPERTY_BEHAVIORS = 1406, + IS_PROPERTY_MODEL_RESET = 1407, + SET_UG_OBJECT_NAME_AND_DESCRIPTION = 1408, + SET_CONSUMABLE_ITEM = 1409, + VEHICLE_GET_CURRENT_LAP = 1410, + GET_UGID = 1411, + SET_UGID = 1412, + UGID_CHANGED = 1413, + RACING_GET_CURRENT_LAP_FOR_PLAYER = 1414, + SUB_ITEM_UN_EQUIPPED = 1415, + SET_CUSTOM_DROP_SHADOW_TEXTURE = 1416, + GET_PLAYER_KIT_FACTION = 1418, + USED_INFORMATION_PLAQUE = 1419, + RACING_ENABLE_WRONG_WAY_RESET = 1420, + RACING_TOGGLE_RUBBER_BANDING = 1421, + GET_RACING_CONTROL_DEBUG_INFO = 1422, + SET_PROPERTY_BOUNDS_VISIBILITY = 1423, + SET_PROPERTY_VENDOR_VISIBILITY = 1424, + SET_EQUIP_STATE = 1425, + NOTIFY_COMBAT_AI_STATE_CHANGE = 1426, + SET_PROPERTY_MODEL_INTERACTIVE = 1430, + SERVER_STATE_NOTIFY = 1431, + GET_SERVER_STATE = 1432, + GET_ICON_FOR_PROXIMITY = 1433, + GET_LEGO_CLUB_MEMBERSHIP_STATUS = 1434, + SET_STATUS_IMMUNITY = 1435, + GET_STATUS_IMMUNITY = 1436, + TEAM_IS_MEMBER = 1437, + ACTIVATE_BRICK_MODE = 1438, + GET_BUILD_OBJECT_ID = 1439, + SET_ANIMATION_ENABLED = 1444, + PAUSE_COOLDOWNS = 1446, + FORCE_UPDATE_RENDER_NODE = 1447, + SET_PET_NAME_MODERATED = 1448, + TOGGLE_STRAFE_MODE = 1449, + SET_SCHEME_SPEED_SCALE = 1450, + CANCEL_SKILL_CAST = 1451, + CHECK_PLAYER_ASSEMBLY_FOR_UNIQUE_MODULE_BY_LOT = 1454, + MODULE_ASSEMBLY_DB_DATA_TO_LUA = 1455, + IS_ALLY = 1458, + MODIFY_LEGO_SCORE = 1459, + GET_LEGO_SCORE = 1460, + GET_PLAYER_LEVEL = 1461, + NOTIFY_LEGO_SCORE_UPDATE = 1462, + SET_LEGO_SCORE = 1463, + UPDATE_BEHAVIOR_EXECUTION_DETAILS = 1466, + RESTORE_TO_POST_LOAD_STATS = 1468, + PICKUP_OBJECT_ERROR = 1469, + CHECK_AND_SHOW_INVENTORY_FULL_TIP = 1470, + SET_RAIL_MOVEMENT = 1471, + START_RAIL_MOVEMENT = 1472, + SET_UP_VECTOR = 1473, + CANCEL_RAIL_MOVEMENT = 1474, + GET_RAIL_INFO = 1475, + CLIENT_RAIL_MOVEMENT_READY = 1476, + PLAYER_RAIL_ARRIVED_NOTIFICATION = 1477, + NOTIFY_RAIL_ACTOVATOR_STATE_CHANGE = 1478, + REQUEST_RAIL_ACTIVATOR_STATE = 1479, + NOTIFY_REWARD_MAILED = 1480, + UPDATE_PLAYER_STATISTIC = 1481, + IS_IN_COMBAT = 1482, + IS_PRIMITIVE_MODEL_MSG = 1483, + SCALE_PRIMITICE_MODEL_MSG = 1484, + MODIFY_GHOSTING_DISTANCE = 1485, + PRIMITIVE_MODEL_CHANGED_MSG = 1487, + GET_PROPRTY_CLONE_ID = 1488, + REQUEST_LEAVE_PROPERTY = 1489, + REQUERY_PROPERTY_MODELS = 1491, + GET_BEHAVIOR_COUNT = 1492, + UPDATE_BEHAVIOR_CONTROLS = 1493, + MODULE_ASSEMBLY_LXFML_LOADED = 1494, + REQUEST_ASSEMBLED_LXFML = 1495, + ASSEMBLED_LXFML_LOADED = 1496, + GET_REORIENT_UP_VECTOR = 1497, + MODULAR_ASSEMBLY_NIF_COMPLETED = 1498, + CHARACTER_DISCONNECT_BEFORE_CREATE = 1499, + SEND_LAUNCH_TO_PREVIOUS_ZONE_TO_CLIENT = 1500, + ROCKETLAUNCH_REQUEST_DEFAULT_MAP_ID = 1501, + BEGIN_LAUNCH = 1502, + PROCESS_CLAIM_CODES = 1503, + GET_LAST_ZONE_ID = 1504, + ADD_RUN_SPEED_MODIFIER = 1505, + REMOVE_RUN_SPEED_MODIFIER = 1506, + SKILL_EVENT_FIRED = 1507, + SEND_HOT_PROPERTY_DATA = 1510, + GET_HOT_PROPERTY_DATA = 1511, + GET_EQUIPPED_ITEMS_IN_SET = 1512, + IS_ITEM_IN_SET = 1513, + GET_INVENTORY_TYPE_FOR_LOT = 1514, + GET_BANK_TYPE_FOR_LOT = 1515, + NOTIFY_NOT_ENOUGH_INV_SPACE = 1516, + IMPORT_MODEL_TO_BBB = 1517, + SEARCH_NEARBY_OBJECTS = 1518, + SEARCH_NEARBY_OBJECTS_REQUEST_BY_LOT = 1519, + REQUEST_OBJECT_POSITION_BY_ID = 1520, + SEARCH_NEARBY_OBJECTS_REQUEST_BY_COMPONENT = 1521, + SEARCH_NEARBY_OBJECTS_RESPONSE = 1522, + BROADCAST_NON_STANDARD_COLLISIONS = 1523, + GET_REGISTERED_NON_STANDARD_COLLISION_GROUP = 1524, + BROADCAST_CRUSHED_NOTIFICATIONS = 1525, + GET_REGISTERED_CRUSHED_COLLISION_GROUPS = 1526, + IS_BEING_CRUSHED = 1527, + GET_SUPPORTING_OBJECT = 1528, + TREAT_RIGID_BODY_COLLSIONS_AS_FIXED = 1529, + BROADCAST_TELEPORTED_WITHIN_NOTIFICATION = 1530, + GET_REGISTERED_TELEPORTED_WITHIN_OBJECT_GROUP = 1531, + GET_INTERPENTRATING_INFORMATION = 1532, + OBJECT_TELEPORTED_WITHIN = 1533, + SET_PHYSICS_SOLVER_PROPERTIES = 1534, + HAS_BEHAVIORS = 1535, + PLAY_BEHAVIOR_SOUND = 1536, + GET_PLAYER_BEHAVIOR_TIER = 1537, + GET_EMOTE_ANIMATION_TIME = 1538, + GET_CHARACTER_STAT_TRACKING = 1539, + PLAYER_INVENTORY_READY = 1540, + SET_PRECONDITIONS = 1541, + DETACH_SHADOW = 1542, + GET_LOOT_INFO = 1543, + GET_PLAYERS_ON_PROPERTY = 1544, + PROPERTY_SPAWN_BY_BEHAVIOR = 1545, + NOTIFY_PROPERTY_OF_EDIT_MODE = 1546, + UPDATE_PROPERTY_PERFORMANCE_COST = 1547, + GET_PROPERTY_PERFORMANCE_COST = 1548, + GET_INVENTORY_ITEM_WITH_SUBKEY = 1549, + DISPLAY_PROPERTY_SUMMARY_SCREEN = 1550, + VALIDATE_BBB_MODEL = 1551, + BBB_MODEL_VALIDATION = 1552, + PROPERTY_ENTRANCE_BEGIN = 1553, + CHECK_LIST_OF_PRECONDITIONS_FROM_LUA = 1554, + GET_PROPERTYIN_ZONE = 1555, + GET_ZONE_ID_FROM_MULTI_ZONE_ENTRANCE = 1556, + TEAM_SET_LEADER = 1557, + TEAM_INVITE_CONFIRM = 1558, + TEAM_GET_STATUS_RESPONSE = 1559, + MINI_GAME_ENABLE_LOCAL_TEAMS = 1560, + TEAM_INVITE_FINAL_RESPONSE = 1561, + TEAM_ADD_PLAYER = 1562, + TEAM_REMOVE_PLAYER = 1563, + TEAM_CREATE_LOCAL = 1564, + TEAM_GET_LOOT = 1565, + TEAM_SET_LOOT = 1566, + SET_ZERO_IMPULSE_AGAINST_COLLISION_GROUPS = 1567, + SET_CENTER_OF_MASS_TO_PHYSICAL_CENTER = 1568, + SET_INERTIA_INVERSE = 1569, + ADD_REMOVE_CLIMBING_LISTENER = 1570, + GET_INVENTORYITEM_DETAILS = 1571, + PERFORM_CLIENT_SIDE_DEATH = 1572, + LEGO_CLUB_ACCESS_RESULT = 1573, + VEHICLE_GET_IS_REVERSING = 1574, + CHECK_CLAIM_CODE = 1575, + GET_HOLIDAY_EVENT = 1576, + SET_EMOTES_ENABLED = 1577, + GET_EMOTES_ENABLED = 1578, + FREEZE_ANIMATION = 1579, + LOCALIZED_ANNOUNCEMENT_SERVER_TO_SINGLE_CLIENT = 1580, + ANCHOR_FX_NODE = 1581, + WS_GET_FRIEND_LIST_MESSAGE = 1582, + WS_ADD_FRIEND_RESPONSE = 1583, + WS_REMOVE_FRIEND_REPSONSE = 1584, + WS_UPDATE_FRIEND_STATUS = 1585, + WS_UPDATE_FRIEND_NAME = 1586, + IS_BEST_FRIEND = 1587, + TEAM_NOTIFY_UPDATE_MISSION_TASK = 1588, + VEHICLE_ADD_SLIPPERY_ACTION = 1589, + VEHICLE_REMOVE_SLIPPERY_ACTION = 1590, + SET_RESURRECT_RESTORE_VALUES = 1591, + GET_MASS = 1592, + SET_PROPERTY_MODERATION_STATUS = 1593, + UPDATE_PROPERTY_MODEL_DEFAULTS = 1594, + UPDATE_PROPERTYMODEL_COUNT = 1595, + GET_PROPERTY_MODEL_COUNT = 1596, + IS_PLAYER_LOADED = 1597, + ATTACH_RENDER_EFFECT_FROM_LUA = 1598, + DETACH_RENDER_EFFECT_FROM_LUA = 1599, + TEAM_IS_LOCAL = 1600, + CREATE_CAMERA_PARTICLES = 1602, + SET_SMASHABLE_GRAVITY_FACTOR = 1603, + VEHICLE_SET_SURFACE_TYPE_OVERRIDE = 1604, + VEHICLE_NOTIFY_HIT_IMAGINATION = 1605, + VEHICLE_NOTIFY_HIT_IMAGINATION_SERVER = 1606, + GET_SPAWNED_OBJECT_SPAWNER_INFO = 1607, + SAVE_PROPERTY = 1608, + SET_PROPERTY_DIRTY = 1609, + GET_PROPERTY_DIRTY = 1610, + GET_MODEL_LIST_FROM_PLAQUE = 1611, + GET_ORIGINAL_POSITION_AND_ROTATION = 1612, + VEHICLE_SET_MASS_FOR_COLLISION = 1613, + GET_INVENTORY_GROUP_COUNT = 1614, + GET_LATEST_CHAT_CHANNEL_USED = 1615, + SET_SUGGEST_LIST_LANGUAGE = 1616, + VEHICLE_STOP_BOOST = 1617, + START_CELEBRATION_EFFECT = 1618, + LOCK_PLAYER = 1619, + VEHICLE_IS_INPUT_LOCKED = 1620, + GET_MULTI_NODE = 1621, + RENEW_PROPERTY = 1622, + RENEW_PROPERTY_RESULT = 1623, + CHARGE_ACTIVITY_COST = 1624, + CAN_RECEIVE_LOOT = 1625, + JOIN_PLAYER_FACTION = 1626, + SET_PROXIMITY_UPDATE_RATE = 1627, + BBB_MODELS_TO_SAVE = 1628, + BELONGS_TO_FACTION = 1629, + MODIFY_FACTION = 1630, + FACTION_UPDATE = 1631, + CELEBRATION_COMPLETED = 1632, + PLAY_PRIMARY_MODULE_SOUNDS = 1633, + STOP_PRIMARY_MODULE_SOUNDS = 1634, + REQUEST_TEAM_PLAYER_UI_UPDATE = 1635, + SET_LOCAL_TEAM = 1636, + TEAM_GET_WORLD_MEMBERS_IN_RADIUS = 1637, + GET_PARENTAL_LEVEL = 1638, + GET_OBJECTS_MESSAGE_HANDLERS = 1639, + PROPERTY_FEATURED = 1640, + PROPERTY_NOTIFY_MODEL_SPAWNED = 1641, + SERVER_DONE_LOADING_ALL_OBJECTS = 1642, + GET_DONATION_TOTAL = 1643, + UPDATE_DONATION_VALUES = 1644, + DELAYED_DELETE_DROP_EFFECT_BRICK = 1645, + SET_CAMERA_UNLOCK_ROTATION_STATE = 1646, + ADD_BUFF = 1647, + REMOVE_BUFF = 1648, + CHECK_FOR_BUFF = 1649, + TEAM_MEMBERS_DISOWNS_LOOT = 1650, + GET_WHEEL_TEMPLATE = 1651, + ADD_SKILL_IN_PROGRESS = 1652, + REMOVE_SKILL_IN_PROGRESS = 1653, + SET_OVERHEAD_ICON_OFFSET = 1654, + SET_BILLBOARD_OFFSET = 1655, + SET_CHAT_BUBBLE_OFFSET = 1656, + SET_NO_TEAM_INVITES = 1657, + RESET_MODEL_TO_DEFAULTS = 1658, + IS_PROPERTY_IN_EDIT_MODE = 1659, + GET_OBJECTS_IN_PHYSICS_BOUNDS = 1660, + ENABLE_LU_REMOTE = 1661, + SET_IS_USING_FREE_TRIAL = 1662, + GET_IS_USING_FREE_TRIAL = 1663, + GET_ACCOUNT_FREE_TRIAL_MODE = 1664, + TOGGLE_INVENTORY_ITEM_LOCK = 1665, + REQUEST_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1666, + RESPONSE_MOVE_ITEM_BETWEEN_INVENTORY_TYPES = 1667, + REMOVE_SUB_COMPONENT = 1668, + TEAM_GET_LOOT_MEMBERS = 1669, + GET_FACTION_TOKEN_TYPE = 1670, + GET_SUBSCRIPTION_PRICING = 1671, + INFORM_AFK = 1672, + OVERHEAD_INDICATOR_CREATED = 1673, + SET_OVERHEAD_INDICATOR_GRID_LOCATION = 1674, + PLAYSTREAM_LOAD_PENDING = 1675, + PLAYER_SET_CAMERA_CYCLING_MODE = 1676, + PLAYER_GET_CAMERA_CYCLING_MODE = 1677, + FORCE_CAMERA_TARGET_CYCLE = 1678, + GET_OBJECT_CONFIG_DATA = 1679, + GET_OBJECT_CONFIG_DATA_NON_CONST = 1680, + SCOPE_CHANGED = 1681, + SET_ALLOW_JUMP_WITHOUT_SUPPORT = 1682, + GET_ALLOW_JUMP_WITHOUT_SUPPORT = 1683, + SET_JUMP_HEIGHT_SCALE = 1684, + GET_JUMP_HEIGHT_SCALE = 1685, + SET_VELOCITY_RESISTANCE = 1686, + GET_VELOCITY_RESISTANCE = 1687, + GATE_RUSH_VEHICLE_HIT_GATE = 1688, + GATE_RUSH_PLAYER_COLLECTED_GATE = 1689, + GATE_RUSH_ADD_GATE = 1690, + GATE_RUSH_REMOVE_GATE = 1691, + NOTIFY_VEHICLE_UPDATED = 1692, + VEHICLE_NOTIFY_HIT_WEAPON_POWERUP = 1693, + VEHICLE_NOTIFY_HIT_WEAPON_POWERUP_SERVER = 1694, + LOCAL_PLAYER_TARGETED = 1696, + SKILL_COUNT_CHANGED = 1697, + DO_YAW_ROTATION = 1698, + DO_PITCH_ROTATION = 1699, + DO_ROLL_ROTATION = 1700, + GET_CURRENT_LOOT_MATRIX = 1701, + SEND_MULTI_MISSION_OFFER_UPDATE_I_DS = 1702, + SET_AIR_SPEED_VALUES = 1703, + USE_LAUNCHER = 1704, + START_LAUNCHER = 1705, + STOP_LAUNCHER = 1706, + CAN_USE_JET_PACK = 1707, + JET_PACK_STATE_CHANGED = 1708, + TURN_OFF_JET_PACK = 1709, + ADD_PLAYER_JET_PACK_PAD = 1710, + SET_JET_PACK_WARNING = 1711, + JET_PACK_DISABLED = 1712, + JET_PACK_PAD_ENTERED = 1713, + UPDATE_RENDER_POSSESSION_FLAG = 1714, + POSSESSABLE_GET_ATTACH_OFFSET = 1715, + ATTEMPT_TO_CRAFT_ITEM = 1718, + CRAFT_ATTEMPT_RESPONSE = 1719, + SET_C_SCORE = 1720, + FILL_IN_RENDERER = 1721, + TOGGLE_CRAFTING_WINDOW = 1722, + REMOVE_TEAM_BUFFS = 1724, + REQUEST_FREE_TRIAL_STATUS_REFRESH = 1725, + REMOVE_BUFFS_APPLIED_BY_OBJECT = 1726, + SET_MOUNT_INVENTORY_ID = 1727, + GET_MOUNT_INVENTORY_ID = 1728, + GET_BUILD_CINEMATIC_TIME_REMAINING = 1730, + JET_PACK_FLYING = 1731, + NOTIFY_SERVER_LEVEL_PROCESSING_COMPLETE = 1734, + NOTIFY_LEVEL_REWARDS = 1735, + CHARACTER_VERSION_CHANGED = 1736, + SET_FREE_TRIAL_RENAME_AVAILABLE = 1737, + SET_PROJECTILE_LAUNCHER_PARAMS = 1738, + RACE_PRECOUNTDOWN_DONE = 1739, + CHECK_INVITE_SPAMMING = 1740, + GET_RESPAWN_VOLUME_INFO = 1741, + INVITE_ACCEPTED = 1742, + TELEPORT_TO_NEAREST_RESPAWN = 1743, + SET_SKILL_CANCEL_ON_MOVE = 1744, + CANCEL_MOVE_SKILL = 1745, + SERVER_CANCEL_MOVE_SKILL = 1746, + CLIENT_CANCEL_MOVE_SKILL = 1747, + END_LAUNCH_SEQUENCE = 1748, + CANCEL_QUEUE = 1749, + UPDATE_PROJECTILE_LAUNCHER_ROTATION = 1750, + GET_CHARACTER_VERSION_INFO = 1751, + GET_CON_INFO = 1753, + GET_SKILLS_FOR_LOT = 1755, + DISMOUNT_COMPLETE = 1756, + MOUNT_FAILURE_RESPONSE = 1757, + CLEAR_BILLBOARD_OFFSET = 1758, + GET_INVENTORY_ITEM_ANIMATION_FLAG = 1759, + SET_JET_PACK_ALLOWED = 1760, + GET_BUILD_TIME_DETAILS = 1761, + USE_SKILL_SET = 1762, + SET_SKILL_SET_POSSESSOR = 1763, + POPULATE_ACTION_BAR = 1764, + GET_COMPONENT_TEMPLATE_ID = 1765, + GET_POSSESSABLE_SKILL_SET = 1766, + MARK_INVENTORY_ITEM_AS_ACTIVE = 1767, + UPDATE_FORGED_ITEM = 1768, + CAN_ITEMS_BE_REFORGED = 1769, + NOTIFY_CLIENT_RAIL_START_FAILED = 1771, + GET_IS_ON_RAIL = 1772 +}; + +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 0; + static constexpr int max = 1772; }; #endif //!__EGAMEMESSAGETYPE__H__ diff --git a/dCommon/dEnums/ePermissionMap.h b/dCommon/dEnums/ePermissionMap.h index d15c9fd3..d3cb12e5 100644 --- a/dCommon/dEnums/ePermissionMap.h +++ b/dCommon/dEnums/ePermissionMap.h @@ -27,20 +27,6 @@ enum class ePermissionMap : uint64_t { * The character has restricted chat access, bit 6. */ RestrictedChatAccess = 0x1 << 6, - - // - // Combined permissions - // - - /** - * The character is marked as 'old', restricted from trade and mail. - */ - Old = RestrictedTradeAccess | RestrictedMailAccess, - - /** - * The character is soft banned, restricted from trade, mail, and chat. - */ - SoftBanned = RestrictedTradeAccess | RestrictedMailAccess | RestrictedChatAccess, }; #endif //!__EPERMISSIONMAP__H__ diff --git a/dCommon/dEnums/ePetAbilityType.h b/dCommon/dEnums/ePetAbilityType.h new file mode 100644 index 00000000..0cc6d6bd --- /dev/null +++ b/dCommon/dEnums/ePetAbilityType.h @@ -0,0 +1,13 @@ +#ifndef __EPETABILITYTYPE__H__ +#define __EPETABILITYTYPE__H__ + +#include + +enum class ePetAbilityType : uint32_t { + Invalid, + GoToObject, + JumpOnObject, + DigAtPosition +}; + +#endif //!__EPETABILITYTYPE__H__ diff --git a/dCommon/dEnums/ePlayerFlag.h b/dCommon/dEnums/ePlayerFlag.h index fefdfb67..01adcb0e 100644 --- a/dCommon/dEnums/ePlayerFlag.h +++ b/dCommon/dEnums/ePlayerFlag.h @@ -166,7 +166,8 @@ enum ePlayerFlag : int32_t { NJ_LIGHTNING_SPINJITZU = 2031, NJ_ICE_SPINJITZU = 2032, NJ_FIRE_SPINJITZU = 2033, - NJ_WU_SHOW_DAILY_CHEST = 2099 + NJ_WU_SHOW_DAILY_CHEST = 2099, + DLU_SKIP_CINEMATICS = 1'000'000, }; #endif //!__EPLAYERFLAG__H__ diff --git a/dCommon/dEnums/eQuickBuildState.h b/dCommon/dEnums/eQuickBuildState.h new file mode 100644 index 00000000..15acbfb5 --- /dev/null +++ b/dCommon/dEnums/eQuickBuildState.h @@ -0,0 +1,15 @@ +#ifndef __EQUICKBUILDSTATE__H__ +#define __EQUICKBUILDSTATE__H__ + +#include + +enum class eQuickBuildState : uint32_t { + OPEN, + COMPLETED = 2, + RESETTING = 4, + BUILDING, + INCOMPLETE +}; + + +#endif //!__EQUICKBUILDSTATE__H__ diff --git a/dCommon/dEnums/eRebuildState.h b/dCommon/dEnums/eRebuildState.h deleted file mode 100644 index 497bcb77..00000000 --- a/dCommon/dEnums/eRebuildState.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __EREBUILDSTATE__H__ -#define __EREBUILDSTATE__H__ - -#include - -enum class eRebuildState : uint32_t { - OPEN, - COMPLETED = 2, - RESETTING = 4, - BUILDING, - INCOMPLETE -}; - - -#endif //!__EREBUILDSTATE__H__ diff --git a/dCommon/dEnums/eReplicaComponentType.h b/dCommon/dEnums/eReplicaComponentType.h index 2d24c19e..83acbf89 100644 --- a/dCommon/dEnums/eReplicaComponentType.h +++ b/dCommon/dEnums/eReplicaComponentType.h @@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t { PLATFORM_BOUNDARY, MODULE, ARCADE, - VEHICLE_PHYSICS, // Havok demo based + HAVOK_VEHICLE_PHYSICS, MOVEMENT_AI, EXHIBIT, OVERHEAD_ICON, @@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t { PROPERTY_ENTRANCE, FX, PROPERTY_MANAGEMENT, - VEHICLE_PHYSICS_NEW, // internal physics based on havok + VEHICLE_PHYSICS, PHYSICS_SYSTEM, QUICK_BUILD, SWITCH, - ZONE_CONTROL, // Minigame + MINI_GAME_CONTROL, CHANGLING, CHOICE_BUILD, PACKAGE, @@ -101,7 +101,7 @@ enum class eReplicaComponentType : uint32_t { TRADE, USER_CONTROL, IGNORE_LIST, - ROCKET_LAUNCH_LUP, + MULTI_ZONE_ENTRANCE, BUFF_REAL, // the real buff component, should just be name BUFF INTERACTION_MANAGER, DONATION_VENDOR, diff --git a/dCommon/dEnums/eWorldMessageType.h b/dCommon/dEnums/eWorldMessageType.h index 2a65fd98..bfaa110b 100644 --- a/dCommon/dEnums/eWorldMessageType.h +++ b/dCommon/dEnums/eWorldMessageType.h @@ -3,6 +3,8 @@ #include +#include "magic_enum.hpp" + enum class eWorldMessageType : uint32_t { VALIDATION = 1, // Session info CHARACTER_LIST_REQUEST, @@ -36,7 +38,14 @@ enum class eWorldMessageType : uint32_t { HANDLE_FUNNESS, FAKE_PRG_CSR_MESSAGE, REQUEST_FREE_TRIAL_REFRESH, - GM_SET_FREE_TRIAL_STATUS + GM_SET_FREE_TRIAL_STATUS, + UI_HELP_TOP_5 = 91 +}; + +template <> +struct magic_enum::customize::enum_range { + static constexpr int min = 0; + static constexpr int max = 91; }; #endif //!__EWORLDMESSAGETYPE__H__ diff --git a/dCommon/dLogger.cpp b/dCommon/dLogger.cpp deleted file mode 100644 index 7785a070..00000000 --- a/dCommon/dLogger.cpp +++ /dev/null @@ -1,110 +0,0 @@ -#include "dLogger.h" - -dLogger::dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements) { - m_logToConsole = logToConsole; - m_logDebugStatements = logDebugStatements; - m_outpath = outpath; - -#ifdef _WIN32 - mFile = std::ofstream(m_outpath); - if (!mFile) { printf("Couldn't open %s for writing!\n", outpath.c_str()); } -#else - fp = fopen(outpath.c_str(), "wt"); - if (fp == NULL) { printf("Couldn't open %s for writing!\n", outpath.c_str()); } -#endif -} - -dLogger::~dLogger() { -#ifdef _WIN32 - mFile.close(); -#else - if (fp != nullptr) { - fclose(fp); - fp = nullptr; - } -#endif -} - -void dLogger::vLog(const char* format, va_list args) { -#ifdef _WIN32 - time_t t = time(NULL); - struct tm time; - localtime_s(&time, &t); - char timeStr[70]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time); - char message[2048]; - vsnprintf(message, 2048, format, args); - - if (m_logToConsole) std::cout << "[" << timeStr << "] " << message; - mFile << "[" << timeStr << "] " << message; -#else - time_t t = time(NULL); - struct tm* time = localtime(&t); - char timeStr[70]; - strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time); - char message[2048]; - vsnprintf(message, 2048, format, args); - - if (m_logToConsole) { - fputs("[", stdout); - fputs(timeStr, stdout); - fputs("] ", stdout); - fputs(message, stdout); - } - - if (fp != nullptr) { - fputs("[", fp); - fputs(timeStr, fp); - fputs("] ", fp); - fputs(message, fp); - } else { - printf("Logger not initialized!\n"); - } -#endif -} - -void dLogger::LogBasic(const char* format, ...) { - va_list args; - va_start(args, format); - vLog(format, args); - va_end(args); -} - -void dLogger::LogBasic(const std::string& message) { - LogBasic(message.c_str()); -} - -void dLogger::Log(const char* className, const char* format, ...) { - va_list args; - std::string log = "[" + std::string(className) + "] " + std::string(format) + "\n"; - va_start(args, format); - vLog(log.c_str(), args); - va_end(args); -} - -void dLogger::Log(const std::string& className, const std::string& message) { - Log(className.c_str(), message.c_str()); -} - -void dLogger::LogDebug(const char* className, const char* format, ...) { - if (!m_logDebugStatements) return; - va_list args; - std::string log = "[" + std::string(className) + "] " + std::string(format) + "\n"; - va_start(args, format); - vLog(log.c_str(), args); - va_end(args); -} - -void dLogger::LogDebug(const std::string& className, const std::string& message) { - LogDebug(className.c_str(), message.c_str()); -} - -void dLogger::Flush() { -#ifdef _WIN32 - mFile.flush(); -#else - if (fp != nullptr) { - std::fflush(fp); - } -#endif -} diff --git a/dCommon/dLogger.h b/dCommon/dLogger.h deleted file mode 100644 index 6726c6f1..00000000 --- a/dCommon/dLogger.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include - -class dLogger { -public: - dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements); - ~dLogger(); - - void SetLogToConsole(bool logToConsole) { m_logToConsole = logToConsole; } - void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; } - void vLog(const char* format, va_list args); - - void LogBasic(const std::string& message); - void LogBasic(const char* format, ...); - void Log(const char* className, const char* format, ...); - void Log(const std::string& className, const std::string& message); - void LogDebug(const std::string& className, const std::string& message); - void LogDebug(const char* className, const char* format, ...); - - void Flush(); - - const bool GetIsLoggingToConsole() const { return m_logToConsole; } - -private: - bool m_logDebugStatements; - bool m_logToConsole; - std::string m_outpath; - std::ofstream mFile; - -#ifndef _WIN32 - //Glorious linux can run with SPEED: - FILE* fp = nullptr; -#endif -}; diff --git a/dDatabase/CDClientDatabase.cpp b/dDatabase/CDClientDatabase/CDClientDatabase.cpp similarity index 89% rename from dDatabase/CDClientDatabase.cpp rename to dDatabase/CDClientDatabase/CDClientDatabase.cpp index 4c2df1d2..886030a1 100644 --- a/dDatabase/CDClientDatabase.cpp +++ b/dDatabase/CDClientDatabase/CDClientDatabase.cpp @@ -4,9 +4,13 @@ // Static Variables static CppSQLite3DB* conn = new CppSQLite3DB(); +// Status Variables +bool CDClientDatabase::isConnected = false; + //! Opens a connection with the CDClient void CDClientDatabase::Connect(const std::string& filename) { conn->open(filename.c_str()); + isConnected = true; } //! Queries the CDClient diff --git a/dDatabase/CDClientDatabase.h b/dDatabase/CDClientDatabase/CDClientDatabase.h similarity index 78% rename from dDatabase/CDClientDatabase.h rename to dDatabase/CDClientDatabase/CDClientDatabase.h index 59f69b7d..fa58906c 100644 --- a/dDatabase/CDClientDatabase.h +++ b/dDatabase/CDClientDatabase/CDClientDatabase.h @@ -13,16 +13,12 @@ #include #include - // Enable this to cache all entries in each table for fast access, comes with more memory cost - //#define CDCLIENT_CACHE_ALL - -/*! - \file CDClientDatabase.hpp - \brief An interface between the CDClient.sqlite file and the server - */ - - //! The CDClient Database namespace +//! The CDClient Database namespace namespace CDClientDatabase { + /** + * Boolean defining the connection status of CDClient + */ + extern bool isConnected; //! Opens a connection with the CDClient /*! diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp new file mode 100644 index 00000000..0e05c0b8 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -0,0 +1,163 @@ +#include "CDClientManager.h" +#include "CDActivityRewardsTable.h" +#include "CDAnimationsTable.h" +#include "CDBehaviorParameterTable.h" +#include "CDBehaviorTemplateTable.h" +#include "CDClientDatabase.h" +#include "CDComponentsRegistryTable.h" +#include "CDCurrencyTableTable.h" +#include "CDDestructibleComponentTable.h" +#include "CDEmoteTable.h" +#include "CDInventoryComponentTable.h" +#include "CDItemComponentTable.h" +#include "CDItemSetsTable.h" +#include "CDItemSetSkillsTable.h" +#include "CDLevelProgressionLookupTable.h" +#include "CDLootMatrixTable.h" +#include "CDLootTableTable.h" +#include "CDMissionNPCComponentTable.h" +#include "CDMissionTasksTable.h" +#include "CDMissionsTable.h" +#include "CDObjectSkillsTable.h" +#include "CDObjectsTable.h" +#include "CDPhysicsComponentTable.h" +#include "CDRebuildComponentTable.h" +#include "CDScriptComponentTable.h" +#include "CDSkillBehaviorTable.h" +#include "CDZoneTableTable.h" +#include "CDVendorComponentTable.h" +#include "CDActivitiesTable.h" +#include "CDPackageComponentTable.h" +#include "CDProximityMonitorComponentTable.h" +#include "CDMovementAIComponentTable.h" +#include "CDBrickIDTableTable.h" +#include "CDRarityTableTable.h" +#include "CDMissionEmailTable.h" +#include "CDRewardsTable.h" +#include "CDPropertyEntranceComponentTable.h" +#include "CDPropertyTemplateTable.h" +#include "CDFeatureGatingTable.h" +#include "CDRailActivatorComponent.h" +#include "CDRewardCodesTable.h" +#include "CDPetComponentTable.h" + +#include + +#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. +// # define CDCLIENT_CACHE_ALL +#endif // CDCLIENT_CACHE_ALL + +#ifdef CDCLIENT_CACHE_ALL + #define CDCLIENT_DONT_CACHE_TABLE(x) x +#else + #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. + +#define SPECIALIZE_TABLE_STORAGE(table) \ + template<> typename table::StorageType& CDClientManager::GetEntriesMutable() { return table##Entries; }; + +#define DEFINE_TABLE_STORAGE(table) namespace { table::StorageType table##Entries; }; SPECIALIZE_TABLE_STORAGE(table) + +DEFINE_TABLE_STORAGE(CDActivityRewardsTable); +DEFINE_TABLE_STORAGE(CDActivitiesTable); +DEFINE_TABLE_STORAGE(CDAnimationsTable); +DEFINE_TABLE_STORAGE(CDBehaviorParameterTable); +DEFINE_TABLE_STORAGE(CDBehaviorTemplateTable); +DEFINE_TABLE_STORAGE(CDBrickIDTableTable); +DEFINE_TABLE_STORAGE(CDComponentsRegistryTable); +DEFINE_TABLE_STORAGE(CDCurrencyTableTable); +DEFINE_TABLE_STORAGE(CDDestructibleComponentTable); +DEFINE_TABLE_STORAGE(CDEmoteTableTable); +DEFINE_TABLE_STORAGE(CDFeatureGatingTable); +DEFINE_TABLE_STORAGE(CDInventoryComponentTable); +DEFINE_TABLE_STORAGE(CDItemComponentTable); +DEFINE_TABLE_STORAGE(CDItemSetSkillsTable); +DEFINE_TABLE_STORAGE(CDItemSetsTable); +DEFINE_TABLE_STORAGE(CDLevelProgressionLookupTable); +DEFINE_TABLE_STORAGE(CDLootMatrixTable); +DEFINE_TABLE_STORAGE(CDLootTableTable); +DEFINE_TABLE_STORAGE(CDMissionEmailTable); +DEFINE_TABLE_STORAGE(CDMissionNPCComponentTable); +DEFINE_TABLE_STORAGE(CDMissionTasksTable); +DEFINE_TABLE_STORAGE(CDMissionsTable); +DEFINE_TABLE_STORAGE(CDMovementAIComponentTable); +DEFINE_TABLE_STORAGE(CDObjectSkillsTable); +DEFINE_TABLE_STORAGE(CDObjectsTable); +DEFINE_TABLE_STORAGE(CDPhysicsComponentTable); +DEFINE_TABLE_STORAGE(CDPackageComponentTable); +DEFINE_TABLE_STORAGE(CDPetComponentTable); +DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable); +DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable); +DEFINE_TABLE_STORAGE(CDPropertyTemplateTable); +DEFINE_TABLE_STORAGE(CDRailActivatorComponentTable); +DEFINE_TABLE_STORAGE(CDRarityTableTable); +DEFINE_TABLE_STORAGE(CDRebuildComponentTable); +DEFINE_TABLE_STORAGE(CDRewardCodesTable); +DEFINE_TABLE_STORAGE(CDRewardsTable); +DEFINE_TABLE_STORAGE(CDScriptComponentTable); +DEFINE_TABLE_STORAGE(CDSkillBehaviorTable); +DEFINE_TABLE_STORAGE(CDVendorComponentTable); +DEFINE_TABLE_STORAGE(CDZoneTableTable); + +void CDClientManager::LoadValuesFromDatabase() { + if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); + + CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); + CDActivitiesTable::Instance().LoadValuesFromDatabase(); + CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase()); + CDBehaviorParameterTable::Instance().LoadValuesFromDatabase(); + CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase(); + CDBrickIDTableTable::Instance().LoadValuesFromDatabase(); + CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase()); + CDCurrencyTableTable::Instance().LoadValuesFromDatabase(); + CDDestructibleComponentTable::Instance().LoadValuesFromDatabase(); + CDEmoteTableTable::Instance().LoadValuesFromDatabase(); + CDFeatureGatingTable::Instance().LoadValuesFromDatabase(); + CDInventoryComponentTable::Instance().LoadValuesFromDatabase(); + CDCLIENT_DONT_CACHE_TABLE(CDItemComponentTable::Instance().LoadValuesFromDatabase()); + CDItemSetSkillsTable::Instance().LoadValuesFromDatabase(); + CDItemSetsTable::Instance().LoadValuesFromDatabase(); + CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase(); + CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase()); + CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase()); + CDMissionEmailTable::Instance().LoadValuesFromDatabase(); + CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase(); + CDMissionTasksTable::Instance().LoadValuesFromDatabase(); + CDMissionsTable::Instance().LoadValuesFromDatabase(); + CDMovementAIComponentTable::Instance().LoadValuesFromDatabase(); + CDObjectSkillsTable::Instance().LoadValuesFromDatabase(); + CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase()); + CDPhysicsComponentTable::Instance().LoadValuesFromDatabase(); + CDPackageComponentTable::Instance().LoadValuesFromDatabase(); + CDPetComponentTable::Instance().LoadValuesFromDatabase(); + CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase(); + CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase(); + CDPropertyTemplateTable::Instance().LoadValuesFromDatabase(); + CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase(); + CDRarityTableTable::Instance().LoadValuesFromDatabase(); + CDRebuildComponentTable::Instance().LoadValuesFromDatabase(); + CDRewardCodesTable::Instance().LoadValuesFromDatabase(); + CDRewardsTable::Instance().LoadValuesFromDatabase(); + CDScriptComponentTable::Instance().LoadValuesFromDatabase(); + CDSkillBehaviorTable::Instance().LoadValuesFromDatabase(); + CDVendorComponentTable::Instance().LoadValuesFromDatabase(); + CDZoneTableTable::Instance().LoadValuesFromDatabase(); +} + +void CDClientManager::LoadValuesFromDefaults() { + LOG("Loading default CDClient tables!"); + + CDPetComponentTable::Instance().LoadValuesFromDefaults(); +} diff --git a/dDatabase/CDClientDatabase/CDClientManager.h b/dDatabase/CDClientDatabase/CDClientManager.h new file mode 100644 index 00000000..c1c4443d --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientManager.h @@ -0,0 +1,44 @@ +#ifndef __CDCLIENTMANAGER__H__ +#define __CDCLIENTMANAGER__H__ + +#define UNUSED_TABLE(v) + +/** + * Initialize the CDClient tables so they are all loaded into memory. + */ +namespace CDClientManager { + void LoadValuesFromDatabase(); + void LoadValuesFromDefaults(); + + /** + * Fetch a table from CDClient + * + * @tparam Table type to fetch + * @return A pointer to the requested table. + */ + template + T* GetTable(); + + /** + * Fetch a table from CDClient + * Note: Calling this function without a template specialization in CDClientManager.cpp will cause a linker error. + * + * @tparam Table type to fetch + * @return A pointer to the requested table. + */ + template + typename T::StorageType& GetEntriesMutable(); +}; + + +// These are included after the CDClientManager namespace declaration as CDTable as of Jan 29 2024 relies on CDClientManager in Templated code. +#include "CDTable.h" + +#include "Singleton.h" + +template +T* CDClientManager::GetTable() { + return &T::Instance(); +}; + +#endif //!__CDCLIENTMANAGER__H__ diff --git a/dDatabase/Tables/CDActivitiesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp similarity index 87% rename from dDatabase/Tables/CDActivitiesTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp index e1660d66..19111490 100644 --- a/dDatabase/Tables/CDActivitiesTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp @@ -1,9 +1,9 @@ #include "CDActivitiesTable.h" -CDActivitiesTable::CDActivitiesTable(void) { +void CDActivitiesTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Activities"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -14,7 +14,8 @@ CDActivitiesTable::CDActivitiesTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Activities"); @@ -40,7 +41,7 @@ CDActivitiesTable::CDActivitiesTable(void) { entry.noTeamLootOnDeath = tableData.getIntField("noTeamLootOnDeath", -1); entry.optionalPercentage = tableData.getFloatField("optionalPercentage", -1.0f); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -49,14 +50,9 @@ CDActivitiesTable::CDActivitiesTable(void) { std::vector CDActivitiesTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDActivitiesTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h new file mode 100644 index 00000000..3e1d4c37 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h @@ -0,0 +1,34 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDActivities { + uint32_t ActivityID; + uint32_t locStatus; + uint32_t instanceMapID; + uint32_t minTeams; + uint32_t maxTeams; + uint32_t minTeamSize; + uint32_t maxTeamSize; + uint32_t waitTime; + uint32_t startDelay; + bool requiresUniqueData; + uint32_t leaderboardType; + bool localize; + int32_t optionalCostLOT; + int32_t optionalCostCount; + bool showUIRewards; + uint32_t CommunityActivityFlagID; + std::string gate_version; + bool noTeamLootOnDeath; + float optionalPercentage; +}; + +class CDActivitiesTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; diff --git a/dDatabase/Tables/CDActivityRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp similarity index 79% rename from dDatabase/Tables/CDActivityRewardsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp index 65ef1101..abe0c50c 100644 --- a/dDatabase/Tables/CDActivityRewardsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp @@ -1,9 +1,10 @@ #include "CDActivityRewardsTable.h" -CDActivityRewardsTable::CDActivityRewardsTable(void) { + +void CDActivityRewardsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ActivityRewards"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -14,7 +15,8 @@ CDActivityRewardsTable::CDActivityRewardsTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ActivityRewards"); @@ -28,7 +30,7 @@ CDActivityRewardsTable::CDActivityRewardsTable(void) { entry.ChallengeRating = tableData.getIntField("ChallengeRating", -1); entry.description = tableData.getStringField("description", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -37,14 +39,9 @@ CDActivityRewardsTable::CDActivityRewardsTable(void) { std::vector CDActivityRewardsTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDActivityRewardsTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h new file mode 100644 index 00000000..8d07a718 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h @@ -0,0 +1,21 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDActivityRewards { + uint32_t objectTemplate; //!< The object template (?) + uint32_t ActivityRewardIndex; //!< The activity reward index + int32_t activityRating; //!< The activity rating + uint32_t LootMatrixIndex; //!< The loot matrix index + uint32_t CurrencyIndex; //!< The currency index + uint32_t ChallengeRating; //!< The challenge rating + std::string description; //!< The description +}; + +class CDActivityRewardsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; diff --git a/dDatabase/Tables/CDAnimationsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp similarity index 54% rename from dDatabase/Tables/CDAnimationsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp index 76ce0e5a..cf461fc9 100644 --- a/dDatabase/Tables/CDAnimationsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp @@ -2,10 +2,41 @@ #include "GeneralUtils.h" #include "Game.h" + +void CDAnimationsTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Animations"); + auto& animations = GetEntriesMutable(); + while (!tableData.eof()) { + std::string animation_type = tableData.getStringField("animation_type", ""); + DluAssert(!animation_type.empty()); + AnimationGroupID animationGroupID = tableData.getIntField("animationGroupID", -1); + DluAssert(animationGroupID != -1); + + CDAnimation entry; + entry.animation_name = tableData.getStringField("animation_name", ""); + entry.chance_to_play = tableData.getFloatField("chance_to_play", 1.0f); + UNUSED_COLUMN(entry.min_loops = tableData.getIntField("min_loops", 0);) + UNUSED_COLUMN(entry.max_loops = tableData.getIntField("max_loops", 0);) + entry.animation_length = tableData.getFloatField("animation_length", 0.0f); + UNUSED_COLUMN(entry.hideEquip = tableData.getIntField("hideEquip", 0) == 1;) + UNUSED_COLUMN(entry.ignoreUpperBody = tableData.getIntField("ignoreUpperBody", 0) == 1;) + UNUSED_COLUMN(entry.restartable = tableData.getIntField("restartable", 0) == 1;) + UNUSED_COLUMN(entry.face_animation_name = tableData.getStringField("face_animation_name", "");) + UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);) + UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);) + + animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry); + tableData.nextRow(); + } + + tableData.finalize(); +} + bool CDAnimationsTable::CacheData(CppSQLite3Statement& queryToCache) { auto tableData = queryToCache.execQuery(); // If we received a bad lookup, cache it anyways so we do not run the query again. if (tableData.eof()) return false; + auto& animations = GetEntriesMutable(); do { std::string animation_type = tableData.getStringField("animation_type", ""); @@ -26,7 +57,7 @@ bool CDAnimationsTable::CacheData(CppSQLite3Statement& queryToCache) { UNUSED_COLUMN(entry.priority = tableData.getFloatField("priority", 0.0f);) UNUSED_COLUMN(entry.blendTime = tableData.getFloatField("blendTime", 0.0f);) - this->animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry); + animations[CDAnimationKey(animation_type, animationGroupID)].push_back(entry); tableData.nextRow(); } while (!tableData.eof()); @@ -39,15 +70,17 @@ void CDAnimationsTable::CacheAnimations(const CDAnimationKey animationKey) { auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Animations WHERE animationGroupID = ? and animation_type = ?"); query.bind(1, static_cast(animationKey.second)); query.bind(2, animationKey.first.c_str()); + auto& animations = GetEntriesMutable(); // If we received a bad lookup, cache it anyways so we do not run the query again. if (!CacheData(query)) { - this->animations[animationKey]; + animations[animationKey]; } } void CDAnimationsTable::CacheAnimationGroup(AnimationGroupID animationGroupID) { - auto animationEntryCached = this->animations.find(CDAnimationKey("", animationGroupID)); - if (animationEntryCached != this->animations.end()) { + auto& animations = GetEntriesMutable(); + auto animationEntryCached = animations.find(CDAnimationKey("", animationGroupID)); + if (animationEntryCached != animations.end()) { return; } @@ -56,28 +89,29 @@ void CDAnimationsTable::CacheAnimationGroup(AnimationGroupID animationGroupID) { // Cache the query so we don't run the query again. CacheData(query); - this->animations[CDAnimationKey("", animationGroupID)]; + animations[CDAnimationKey("", animationGroupID)]; } -CDAnimationLookupResult CDAnimationsTable::GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID) { +std::optional CDAnimationsTable::GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID) { + auto& animations = GetEntriesMutable(); CDAnimationKey animationKey(animationType, animationGroupID); - auto animationEntryCached = this->animations.find(animationKey); - if (animationEntryCached == this->animations.end()) { + auto animationEntryCached = animations.find(animationKey); + if (animationEntryCached == animations.end()) { this->CacheAnimations(animationKey); } - auto animationEntry = this->animations.find(animationKey); + auto animationEntry = animations.find(animationKey); // If we have only one animation, return it regardless of the chance to play. if (animationEntry->second.size() == 1) { - return CDAnimationLookupResult(animationEntry->second.front()); + return animationEntry->second.front(); } auto randomAnimation = GeneralUtils::GenerateRandomNumber(0, 1); for (auto& animationEntry : animationEntry->second) { randomAnimation -= animationEntry.chance_to_play; // This is how the client gets the random animation. - if (animationEntry.animation_name != previousAnimationName && randomAnimation <= 0.0f) return CDAnimationLookupResult(animationEntry); + if (animationEntry.animation_name != previousAnimationName && randomAnimation <= 0.0f) return animationEntry; } - return CDAnimationLookupResult(); + return std::nullopt; } diff --git a/dDatabase/Tables/CDAnimationsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h similarity index 65% rename from dDatabase/Tables/CDAnimationsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h index 65f54d98..643ef98f 100644 --- a/dDatabase/Tables/CDAnimationsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h @@ -2,15 +2,20 @@ #include "CDTable.h" #include +#include + +typedef int32_t AnimationGroupID; +typedef std::string AnimationID; +typedef std::pair CDAnimationKey; struct CDAnimation { - // unsigned int animationGroupID; + // uint32_t animationGroupID; // std::string animation_type; // The above two are a pair to represent a primary key in the map. std::string animation_name; //!< The animation name float chance_to_play; //!< The chance to play the animation - UNUSED_COLUMN(unsigned int min_loops;) //!< The minimum number of loops - UNUSED_COLUMN(unsigned int max_loops;) //!< The maximum number of loops + UNUSED_COLUMN(uint32_t min_loops;) //!< The minimum number of loops + UNUSED_COLUMN(uint32_t max_loops;) //!< The maximum number of loops float animation_length; //!< The animation length UNUSED_COLUMN(bool hideEquip;) //!< Whether or not to hide the equip UNUSED_COLUMN(bool ignoreUpperBody;) //!< Whether or not to ignore the upper body @@ -20,13 +25,9 @@ struct CDAnimation { UNUSED_COLUMN(float blendTime;) //!< The blend time }; -typedef LookupResult CDAnimationLookupResult; - -class CDAnimationsTable : public CDTable { - typedef int32_t AnimationGroupID; - typedef std::string AnimationID; - typedef std::pair CDAnimationKey; +class CDAnimationsTable : public CDTable>> { public: + void LoadValuesFromDatabase(); /** * Given an animationType and the previousAnimationName played, return the next animationType to play. * If there are more than 1 animationTypes that can be played, one is selected at random but also does not allow @@ -37,7 +38,7 @@ public: * @param animationGroupID The animationGroupID to lookup * @return CDAnimationLookupResult */ - [[nodiscard]] CDAnimationLookupResult GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID); + [[nodiscard]] std::optional GetAnimation(const AnimationID& animationType, const std::string& previousAnimationName, const AnimationGroupID animationGroupID); /** * Cache a full AnimationGroup by its ID. @@ -57,10 +58,4 @@ private: * @return false */ bool CacheData(CppSQLite3Statement& queryToCache); - - /** - * Each animation is key'd by its animationName and its animationGroupID. Each - * animation has a possible list of animations. This is because there can be animations have a percent chance to play so one is selected at random. - */ - std::map> animations; }; diff --git a/dDatabase/Tables/CDBehaviorParameterTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp similarity index 58% rename from dDatabase/Tables/CDBehaviorParameterTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp index 8181245b..57187c7c 100644 --- a/dDatabase/Tables/CDBehaviorParameterTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp @@ -1,16 +1,21 @@ #include "CDBehaviorParameterTable.h" #include "GeneralUtils.h" -uint64_t GetHash(const uint32_t behaviorID, const uint32_t parameterID) { - uint64_t hash = behaviorID; - hash <<= 31U; - hash |= parameterID; +namespace { + std::unordered_map m_ParametersList; +}; - return hash; +uint64_t GetKey(const uint32_t behaviorID, const uint32_t parameterID) { + uint64_t key = behaviorID; + key <<= 31U; + key |= parameterID; + + return key; } -CDBehaviorParameterTable::CDBehaviorParameterTable() { +void CDBehaviorParameterTable::LoadValuesFromDatabase() { auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { uint32_t behaviorID = tableData.getIntField("behaviorID", -1); auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", "")); @@ -21,10 +26,10 @@ CDBehaviorParameterTable::CDBehaviorParameterTable() { } else { parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second; } - uint64_t hash = GetHash(behaviorID, parameterId); + uint64_t hash = GetKey(behaviorID, parameterId); float value = tableData.getFloatField("value", -1.0f); - m_Entries.insert(std::make_pair(hash, value)); + entries.insert(std::make_pair(hash, value)); tableData.nextRow(); } @@ -32,22 +37,24 @@ CDBehaviorParameterTable::CDBehaviorParameterTable() { } float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) { - auto parameterID = this->m_ParametersList.find(name); - if (parameterID == this->m_ParametersList.end()) return defaultValue; - auto hash = GetHash(behaviorID, parameterID->second); + auto parameterID = m_ParametersList.find(name); + if (parameterID == m_ParametersList.end()) return defaultValue; + auto hash = GetKey(behaviorID, parameterID->second); // Search for specific parameter - auto it = m_Entries.find(hash); - return it != m_Entries.end() ? it->second : defaultValue; + auto& entries = GetEntriesMutable(); + auto it = entries.find(hash); + return it != entries.end() ? it->second : defaultValue; } std::map CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) { + auto& entries = GetEntriesMutable(); uint64_t hashBase = behaviorID; std::map returnInfo; for (auto& [parameterString, parameterId] : m_ParametersList) { - uint64_t hash = GetHash(hashBase, parameterId); - auto infoCandidate = m_Entries.find(hash); - if (infoCandidate != m_Entries.end()) { + uint64_t hash = GetKey(hashBase, parameterId); + auto infoCandidate = entries.find(hash); + if (infoCandidate != entries.end()) { returnInfo.insert(std::make_pair(parameterString, infoCandidate->second)); } } diff --git a/dDatabase/Tables/CDBehaviorParameterTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h similarity index 57% rename from dDatabase/Tables/CDBehaviorParameterTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h index 249caacf..ba6ad6c1 100644 --- a/dDatabase/Tables/CDBehaviorParameterTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h @@ -5,14 +5,12 @@ #include #include -class CDBehaviorParameterTable : public CDTable { -private: - typedef uint64_t BehaviorParameterHash; - typedef float BehaviorParameterValue; - std::unordered_map m_Entries; - std::unordered_map m_ParametersList; +typedef uint64_t BehaviorParameterHash; +typedef float BehaviorParameterValue; + +class CDBehaviorParameterTable : public CDTable> { public: - CDBehaviorParameterTable(); + void LoadValuesFromDatabase(); float GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue = 0); diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp similarity index 63% rename from dDatabase/Tables/CDBehaviorTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp index 08bc86d1..983156e3 100644 --- a/dDatabase/Tables/CDBehaviorTemplateTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp @@ -1,9 +1,13 @@ #include "CDBehaviorTemplateTable.h" -CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) { +namespace { + std::unordered_set m_EffectHandles; +}; + +void CDBehaviorTemplateTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM BehaviorTemplate"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -13,11 +17,9 @@ CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) { tableSize.finalize(); - // Reserve the size - this->entries.reserve(size); - // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorTemplate"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDBehaviorTemplate entry; entry.behaviorID = tableData.getIntField("behaviorID", -1); @@ -31,30 +33,17 @@ CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) { entry.effectHandle = m_EffectHandles.insert(candidateToAdd).first; } - this->entries.push_back(entry); - this->entriesMappedByBehaviorID.insert(std::make_pair(entry.behaviorID, entry)); + entries.insert(std::make_pair(entry.behaviorID, entry)); tableData.nextRow(); } tableData.finalize(); } -std::vector CDBehaviorTemplateTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) - >> cpplinq::where(predicate) - >> cpplinq::to_vector(); - - return data; -} - -std::vector CDBehaviorTemplateTable::GetEntries(void) const { - return this->entries; -} - const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behaviorID) { - auto entry = this->entriesMappedByBehaviorID.find(behaviorID); - if (entry == this->entriesMappedByBehaviorID.end()) { + auto& entries = GetEntriesMutable(); + auto entry = entries.find(behaviorID); + if (entry == entries.end()) { CDBehaviorTemplate entryToReturn; entryToReturn.behaviorID = 0; entryToReturn.effectHandle = m_EffectHandles.end(); @@ -64,4 +53,3 @@ const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behav return entry->second; } } - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h new file mode 100644 index 00000000..367f5f0a --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h @@ -0,0 +1,20 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" +#include +#include + +struct CDBehaviorTemplate { + uint32_t behaviorID; //!< The Behavior ID + uint32_t templateID; //!< The Template ID (LOT) + uint32_t effectID; //!< The Effect ID attached + std::unordered_set::iterator effectHandle; //!< The effect handle +}; + +class CDBehaviorTemplateTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + + const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID); +}; diff --git a/dDatabase/Tables/CDBrickIDTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp similarity index 72% rename from dDatabase/Tables/CDBrickIDTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp index 9ad24d39..c2714396 100644 --- a/dDatabase/Tables/CDBrickIDTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp @@ -1,9 +1,9 @@ #include "CDBrickIDTableTable.h" -CDBrickIDTableTable::CDBrickIDTableTable(void) { +void CDBrickIDTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM BrickIDTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -14,7 +14,8 @@ CDBrickIDTableTable::CDBrickIDTableTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BrickIDTable"); @@ -23,7 +24,7 @@ CDBrickIDTableTable::CDBrickIDTableTable(void) { entry.NDObjectID = tableData.getIntField("NDObjectID", -1); entry.LEGOBrickID = tableData.getIntField("LEGOBrickID", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -31,15 +32,9 @@ CDBrickIDTableTable::CDBrickIDTableTable(void) { } std::vector CDBrickIDTableTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDBrickIDTableTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDBrickIDTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h similarity index 60% rename from dDatabase/Tables/CDBrickIDTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h index e2084caf..9a2c2523 100644 --- a/dDatabase/Tables/CDBrickIDTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h @@ -10,20 +10,15 @@ //! BrickIDTable Entry Struct struct CDBrickIDTable { - unsigned int NDObjectID; - unsigned int LEGOBrickID; + uint32_t NDObjectID; + uint32_t LEGOBrickID; }; //! BrickIDTable table -class CDBrickIDTableTable : public CDTable { -private: - std::vector entries; - +class CDBrickIDTableTable : public CDTable> { public: - CDBrickIDTableTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp new file mode 100644 index 00000000..4944c13b --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp @@ -0,0 +1,53 @@ +#include "CDComponentsRegistryTable.h" +#include "eReplicaComponentType.h" + +void CDComponentsRegistryTable::LoadValuesFromDatabase() { + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ComponentsRegistry"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + CDComponentsRegistry entry; + entry.id = tableData.getIntField("id", -1); + entry.component_type = static_cast(tableData.getIntField("component_type", 0)); + entry.component_id = tableData.getIntField("component_id", -1); + + entries.insert_or_assign(static_cast(entry.component_type) << 32 | static_cast(entry.id), entry.component_id); + entries.insert_or_assign(entry.id, 0); + + tableData.nextRow(); + } + + tableData.finalize(); +} + +int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) { + auto& entries = GetEntriesMutable(); + auto exists = entries.find(id); + if (exists != entries.end()) { + auto iter = entries.find(static_cast(componentType) << 32 | static_cast(id)); + return iter == entries.end() ? defaultValue : iter->second; + } + + // Now get the data. Get all components of this entity so we dont do a query for each component + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;"); + query.bind(1, static_cast(id)); + + auto tableData = query.execQuery(); + + while (!tableData.eof()) { + CDComponentsRegistry entry; + entry.id = tableData.getIntField("id", -1); + entry.component_type = static_cast(tableData.getIntField("component_type", 0)); + entry.component_id = tableData.getIntField("component_id", -1); + + entries.insert_or_assign(static_cast(entry.component_type) << 32 | static_cast(entry.id), entry.component_id); + + tableData.nextRow(); + } + + entries.insert_or_assign(id, 0); + + auto iter = entries.find(static_cast(componentType) << 32 | static_cast(id)); + + return iter == entries.end() ? defaultValue : iter->second; +} diff --git a/dDatabase/Tables/CDComponentsRegistryTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h similarity index 53% rename from dDatabase/Tables/CDComponentsRegistryTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h index 990072c9..2165f907 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h @@ -3,19 +3,18 @@ // Custom Classes #include "CDTable.h" +#include + enum class eReplicaComponentType : uint32_t; struct CDComponentsRegistry { - unsigned int id; //!< The LOT is used as the ID + uint32_t id; //!< The LOT is used as the ID eReplicaComponentType component_type; //!< See ComponentTypes enum for values - unsigned int component_id; //!< The ID used within the component's table (0 may either mean it's non-networked, or that the ID is actually 0 + uint32_t component_id; //!< The ID used within the component's table (0 may either mean it's non-networked, or that the ID is actually 0 }; -class CDComponentsRegistryTable : public CDTable { -private: - std::map mappedEntries; //id, component_type, component_id - +class CDComponentsRegistryTable : public CDTable> { public: - CDComponentsRegistryTable(); + void LoadValuesFromDatabase(); int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0); }; diff --git a/dDatabase/Tables/CDCurrencyTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp similarity index 76% rename from dDatabase/Tables/CDCurrencyTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp index 78a716f9..19ac7da0 100644 --- a/dDatabase/Tables/CDCurrencyTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp @@ -1,10 +1,10 @@ #include "CDCurrencyTableTable.h" //! Constructor -CDCurrencyTableTable::CDCurrencyTableTable(void) { +void CDCurrencyTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM CurrencyTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +15,8 @@ CDCurrencyTableTable::CDCurrencyTableTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM CurrencyTable"); @@ -27,7 +28,7 @@ CDCurrencyTableTable::CDCurrencyTableTable(void) { entry.maxvalue = tableData.getIntField("maxvalue", -1); entry.id = tableData.getIntField("id", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -35,15 +36,9 @@ CDCurrencyTableTable::CDCurrencyTableTable(void) { } std::vector CDCurrencyTableTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDCurrencyTableTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h new file mode 100644 index 00000000..1cd6c142 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h @@ -0,0 +1,26 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +/*! + \file CDCurrencyTableTable.hpp + \brief Contains data for the CurrencyTable table + */ + + //! CurrencyTable Struct +struct CDCurrencyTable { + uint32_t currencyIndex; //!< The Currency Index + uint32_t npcminlevel; //!< The minimum level of the npc + uint32_t minvalue; //!< The minimum currency + uint32_t maxvalue; //!< The maximum currency + uint32_t id; //!< The ID of the currency index +}; + +//! CurrencyTable table +class CDCurrencyTableTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; diff --git a/dDatabase/Tables/CDDestructibleComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp similarity index 81% rename from dDatabase/Tables/CDDestructibleComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp index 4bbc8242..b1a6f699 100644 --- a/dDatabase/Tables/CDDestructibleComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp @@ -1,10 +1,8 @@ #include "CDDestructibleComponentTable.h" -//! Constructor -CDDestructibleComponentTable::CDDestructibleComponentTable(void) { - +void CDDestructibleComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM DestructibleComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +13,8 @@ CDDestructibleComponentTable::CDDestructibleComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM DestructibleComponent"); @@ -36,7 +35,7 @@ CDDestructibleComponentTable::CDDestructibleComponentTable(void) { entry.isSmashable = tableData.getIntField("isSmashable", -1) == 1 ? true : false; entry.difficultyLevel = tableData.getIntField("difficultyLevel", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -44,15 +43,9 @@ CDDestructibleComponentTable::CDDestructibleComponentTable(void) { } std::vector CDDestructibleComponentTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDDestructibleComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h new file mode 100644 index 00000000..3319907b --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h @@ -0,0 +1,28 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDDestructibleComponent { + uint32_t id; //!< The component ID from the ComponentsRegistry Table + int32_t faction; //!< The Faction ID of the object + std::string factionList; //!< A list of the faction IDs + int32_t life; //!< The amount of life of the object + uint32_t imagination; //!< The amount of imagination of the object + int32_t LootMatrixIndex; //!< The Loot Matrix Index + int32_t CurrencyIndex; //!< The Currency Index + uint32_t level; //!< ??? + float armor; //!< The amount of armor of the object + uint32_t death_behavior; //!< The behavior ID of the death behavior + bool isnpc; //!< Whether or not the object is an NPC + uint32_t attack_priority; //!< ??? + bool isSmashable; //!< Whether or not the object is smashable + int32_t difficultyLevel; //!< ??? +}; + +class CDDestructibleComponentTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp new file mode 100644 index 00000000..28052819 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp @@ -0,0 +1,28 @@ +#include "CDEmoteTable.h" + +void CDEmoteTableTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Emotes"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + CDEmoteTable entry; + entry.ID = tableData.getIntField("id", -1); + entry.animationName = tableData.getStringField("animationName", ""); + entry.iconFilename = tableData.getStringField("iconFilename", ""); + entry.channel = tableData.getIntField("channel", -1); + entry.locked = tableData.getIntField("locked", -1) != 0; + entry.localize = tableData.getIntField("localize", -1) != 0; + entry.locState = tableData.getIntField("locStatus", -1); + entry.gateVersion = tableData.getStringField("gate_version", ""); + + entries.insert(std::make_pair(entry.ID, entry)); + tableData.nextRow(); + } + + tableData.finalize(); +} + +CDEmoteTable* CDEmoteTableTable::GetEmote(int32_t id) { + auto& entries = GetEntriesMutable(); + auto itr = entries.find(id); + return itr != entries.end() ? &itr->second : nullptr; +} diff --git a/dDatabase/Tables/CDEmoteTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h similarity index 64% rename from dDatabase/Tables/CDEmoteTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h index be40c86f..ff0b28d1 100644 --- a/dDatabase/Tables/CDEmoteTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h @@ -16,23 +16,19 @@ struct CDEmoteTable { gateVersion = ""; } - int ID; + int32_t ID; std::string animationName; std::string iconFilename; - int locState; - int channel; + int32_t locState; + int32_t channel; bool locked; bool localize; std::string gateVersion; }; -class CDEmoteTableTable : public CDTable { -private: - std::map entries; - +class CDEmoteTableTable : public CDTable> { public: - CDEmoteTableTable(); - ~CDEmoteTableTable(); + void LoadValuesFromDatabase(); // Returns an emote by ID - CDEmoteTable* GetEmote(int id); + CDEmoteTable* GetEmote(int32_t id); }; diff --git a/dDatabase/Tables/CDFeatureGatingTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp similarity index 70% rename from dDatabase/Tables/CDFeatureGatingTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp index 05fe69bf..407efff7 100644 --- a/dDatabase/Tables/CDFeatureGatingTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp @@ -1,10 +1,9 @@ #include "CDFeatureGatingTable.h" -//! Constructor -CDFeatureGatingTable::CDFeatureGatingTable(void) { +void CDFeatureGatingTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM FeatureGating"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDFeatureGatingTable::CDFeatureGatingTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM FeatureGating"); @@ -27,7 +27,7 @@ CDFeatureGatingTable::CDFeatureGatingTable(void) { entry.minor = tableData.getIntField("minor", -1); entry.description = tableData.getStringField("description", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -36,24 +36,21 @@ CDFeatureGatingTable::CDFeatureGatingTable(void) { std::vector CDFeatureGatingTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + auto& entries = GetEntriesMutable(); + std::vector data = cpplinq::from(entries) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } -bool CDFeatureGatingTable::FeatureUnlocked(const std::string& feature) const { +bool CDFeatureGatingTable::FeatureUnlocked(const CDFeatureGating& feature) const { + auto& entries = GetEntriesMutable(); for (const auto& entry : entries) { - if (entry.featureName == feature) { + if (entry.featureName == feature.featureName && feature >= entry) { return true; } } return false; } - -std::vector CDFeatureGatingTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDFeatureGatingTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h similarity index 52% rename from dDatabase/Tables/CDFeatureGatingTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h index 7f536db5..65f33395 100644 --- a/dDatabase/Tables/CDFeatureGatingTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h @@ -9,18 +9,20 @@ struct CDFeatureGating { int32_t current; int32_t minor; std::string description; + + bool operator>=(const CDFeatureGating& b) const { + return (this->major > b.major) || + (this->major == b.major && this->current > b.current) || + (this->major == b.major && this->current == b.current && this->minor >= b.minor); + } }; -class CDFeatureGatingTable : public CDTable { -private: - std::vector entries; - +class CDFeatureGatingTable : public CDTable> { public: - CDFeatureGatingTable(); + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - bool FeatureUnlocked(const std::string& feature) const; - - std::vector GetEntries(void) const; + bool FeatureUnlocked(const CDFeatureGating& feature) const; }; diff --git a/dDatabase/Tables/CDInventoryComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp similarity index 73% rename from dDatabase/Tables/CDInventoryComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp index 2dc375ab..8cf3d8da 100644 --- a/dDatabase/Tables/CDInventoryComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDInventoryComponentTable.h" -//! Constructor -CDInventoryComponentTable::CDInventoryComponentTable(void) { +void CDInventoryComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM InventoryComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDInventoryComponentTable::CDInventoryComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM InventoryComponent"); @@ -26,7 +26,7 @@ CDInventoryComponentTable::CDInventoryComponentTable(void) { entry.count = tableData.getIntField("count", -1); entry.equip = tableData.getIntField("equip", -1) == 1 ? true : false; - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -34,15 +34,9 @@ CDInventoryComponentTable::CDInventoryComponentTable(void) { } std::vector CDInventoryComponentTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDInventoryComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDInventoryComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h similarity index 50% rename from dDatabase/Tables/CDInventoryComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h index cbc04d99..361e1a90 100644 --- a/dDatabase/Tables/CDInventoryComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h @@ -4,20 +4,15 @@ #include "CDTable.h" struct CDInventoryComponent { - unsigned int id; //!< The component ID for this object - unsigned int itemid; //!< The LOT of the object - unsigned int count; //!< The count of the items the object has + uint32_t id; //!< The component ID for this object + uint32_t itemid; //!< The LOT of the object + uint32_t count; //!< The count of the items the object has bool equip; //!< Whether or not to equip the item }; -class CDInventoryComponentTable : public CDTable { -private: - std::vector entries; - +class CDInventoryComponentTable : public CDTable> { public: - CDInventoryComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; }; diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp similarity index 88% rename from dDatabase/Tables/CDItemComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp index 54afc417..0d8b1ad9 100644 --- a/dDatabase/Tables/CDItemComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp @@ -3,13 +3,9 @@ CDItemComponent CDItemComponentTable::Default = {}; -//! Constructor -CDItemComponentTable::CDItemComponentTable(void) { - Default = CDItemComponent(); - -#ifdef CDCLIENT_CACHE_ALL +void CDItemComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -21,6 +17,7 @@ CDItemComponentTable::CDItemComponentTable(void) { // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ItemComponent"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDItemComponent entry; entry.id = tableData.getIntField("id", -1); @@ -55,37 +52,35 @@ CDItemComponentTable::CDItemComponentTable(void) { entry.currencyLOT = tableData.getIntField("currencyLOT", -1); entry.altCurrencyCost = tableData.getIntField("altCurrencyCost", -1); entry.subItems = tableData.getStringField("subItems", ""); - entry.audioEventUse = tableData.getStringField("audioEventUse", ""); + UNUSED_COLUMN(entry.audioEventUse = tableData.getStringField("audioEventUse", "")); entry.noEquipAnimation = tableData.getIntField("noEquipAnimation", -1) == 1 ? true : false; entry.commendationLOT = tableData.getIntField("commendationLOT", -1); entry.commendationCost = tableData.getIntField("commendationCost", -1); - entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", ""); + UNUSED_COLUMN(entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", "")); entry.currencyCosts = tableData.getStringField("currencyCosts", ""); - entry.ingredientInfo = tableData.getStringField("ingredientInfo", ""); + UNUSED_COLUMN(entry.ingredientInfo = tableData.getStringField("ingredientInfo", "")); entry.locStatus = tableData.getIntField("locStatus", -1); entry.forgeType = tableData.getIntField("forgeType", -1); entry.SellMultiplier = tableData.getFloatField("SellMultiplier", -1.0f); - this->entries.insert(std::make_pair(entry.id, entry)); + entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); } tableData.finalize(); -#endif } -const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) { - const auto& it = this->entries.find(skillID); - if (it != this->entries.end()) { +const CDItemComponent& CDItemComponentTable::GetItemComponentByID(uint32_t skillID) { + auto& entries = GetEntriesMutable(); + const auto& it = entries.find(skillID); + if (it != entries.end()) { return it->second; } -#ifndef CDCLIENT_CACHE_ALL - std::stringstream query; + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ItemComponent WHERE id = ?;"); + query.bind(1, static_cast(skillID)); - query << "SELECT * FROM ItemComponent WHERE id = " << std::to_string(skillID); - - auto tableData = CDClientDatabase::ExecuteQuery(query.str()); + auto tableData = query.execQuery(); if (tableData.eof()) { entries.insert(std::make_pair(skillID, Default)); return Default; @@ -136,15 +131,14 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int s entry.forgeType = tableData.getIntField("forgeType", -1); entry.SellMultiplier = tableData.getFloatField("SellMultiplier", -1.0f); - this->entries.insert(std::make_pair(entry.id, entry)); + entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); } - const auto& it2 = this->entries.find(skillID); - if (it2 != this->entries.end()) { + const auto& it2 = entries.find(skillID); + if (it2 != entries.end()) { return it2->second; } -#endif return Default; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h new file mode 100644 index 00000000..60a3e412 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h @@ -0,0 +1,61 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" +#include "dCommonVars.h" + +struct CDItemComponent { + uint32_t id; //!< The Component ID + std::string equipLocation; //!< The equip location + uint32_t baseValue; //!< The monetary base value of the item + bool isKitPiece; //!< Whether or not the item belongs to a kit + uint32_t rarity; //!< The rarity of the item + uint32_t itemType; //!< The item type + int64_t itemInfo; //!< The item info + bool inLootTable; //!< Whether or not the item is in a loot table + bool inVendor; //!< Whether or not the item is in a vendor inventory + bool isUnique; //!< ??? + bool isBOP; //!< ??? + bool isBOE; //!< ??? + uint32_t reqFlagID; //!< User must have completed this flag to get the item + uint32_t reqSpecialtyID; //!< ??? + uint32_t reqSpecRank; //!< ??? + uint32_t reqAchievementID; //!< The required achievement must be completed + uint32_t stackSize; //!< The stack size of the item + uint32_t color1; //!< Something to do with item color... + uint32_t decal; //!< The decal of the item + uint32_t offsetGroupID; //!< Something to do with group IDs + uint32_t buildTypes; //!< Something to do with building + std::string reqPrecondition; //!< The required precondition + uint32_t animationFlag; //!< The Animation Flag + uint32_t equipEffects; //!< The effect played when the item is equipped + bool readyForQA; //!< ??? + uint32_t itemRating; //!< ??? + bool isTwoHanded; //!< Whether or not the item is double handed + uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object? + uint32_t delResIndex; //!< ??? + uint32_t currencyLOT; //!< ??? + uint32_t altCurrencyCost; //!< ??? + std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set) + UNUSED(std::string audioEventUse); //!< ??? + bool noEquipAnimation; //!< Whether or not there is an equip animation + uint32_t commendationLOT; //!< The commendation LOT + uint32_t commendationCost; //!< The commendation cost + UNUSED(std::string audioEquipMetaEventSet); //!< ??? + std::string currencyCosts; //!< Used for crafting + UNUSED(std::string ingredientInfo); //!< Unused + uint32_t locStatus; //!< ??? + uint32_t forgeType; //!< Forge Type + float SellMultiplier; //!< Something to do with early vendors perhaps (but replaced) +}; + +class CDItemComponentTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + static std::map ParseCraftingCurrencies(const CDItemComponent& itemComponent); + + // Gets an entry by ID + const CDItemComponent& GetItemComponentByID(uint32_t skillID); + + static CDItemComponent Default; +}; diff --git a/dDatabase/Tables/CDItemSetSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp similarity index 74% rename from dDatabase/Tables/CDItemSetSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp index f6b412ff..79bd69ae 100644 --- a/dDatabase/Tables/CDItemSetSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp @@ -1,10 +1,9 @@ #include "CDItemSetSkillsTable.h" -//! Constructor -CDItemSetSkillsTable::CDItemSetSkillsTable(void) { +void CDItemSetSkillsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemSetSkills"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDItemSetSkillsTable::CDItemSetSkillsTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ItemSetSkills"); @@ -25,7 +25,7 @@ CDItemSetSkillsTable::CDItemSetSkillsTable(void) { entry.SkillID = tableData.getIntField("SkillID", -1); entry.SkillCastType = tableData.getIntField("SkillCastType", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -33,22 +33,17 @@ CDItemSetSkillsTable::CDItemSetSkillsTable(void) { } std::vector CDItemSetSkillsTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } -std::vector CDItemSetSkillsTable::GetEntries(void) const { - return this->entries; -} - -std::vector CDItemSetSkillsTable::GetBySkillID(unsigned int SkillSetID) { +std::vector CDItemSetSkillsTable::GetBySkillID(uint32_t SkillSetID) { std::vector toReturn; - for (CDItemSetSkills entry : this->entries) { + for (const auto& entry : GetEntries()) { if (entry.SkillSetID == SkillSetID) toReturn.push_back(entry); if (entry.SkillSetID > SkillSetID) return toReturn; //stop seeking in the db if it's not needed. } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h new file mode 100644 index 00000000..78e708ae --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h @@ -0,0 +1,19 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDItemSetSkills { + uint32_t SkillSetID; //!< The skill set ID + uint32_t SkillID; //!< The skill ID + uint32_t SkillCastType; //!< The skill cast type +}; + +class CDItemSetSkillsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); + + std::vector GetBySkillID(uint32_t SkillSetID); +}; diff --git a/dDatabase/Tables/CDItemSetsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp similarity index 84% rename from dDatabase/Tables/CDItemSetsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp index 0632ef13..77b3b1e7 100644 --- a/dDatabase/Tables/CDItemSetsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp @@ -1,10 +1,9 @@ #include "CDItemSetsTable.h" -//! Constructor -CDItemSetsTable::CDItemSetsTable(void) { +void CDItemSetsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemSets"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDItemSetsTable::CDItemSetsTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ItemSets"); @@ -37,7 +37,7 @@ CDItemSetsTable::CDItemSetsTable(void) { entry.kitID = tableData.getIntField("kitID", -1); entry.priority = tableData.getFloatField("priority", -1.0f); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -46,14 +46,9 @@ CDItemSetsTable::CDItemSetsTable(void) { std::vector CDItemSetsTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -std::vector CDItemSetsTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h new file mode 100644 index 00000000..45b91590 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h @@ -0,0 +1,30 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDItemSets { + uint32_t setID; //!< The item set ID + uint32_t locStatus; //!< The loc status + std::string itemIDs; //!< THe item IDs + uint32_t kitType; //!< The item kit type + uint32_t kitRank; //!< The item kit rank + uint32_t kitImage; //!< The item kit image + uint32_t skillSetWith2; //!< The skill set with 2 + uint32_t skillSetWith3; //!< The skill set with 3 + uint32_t skillSetWith4; //!< The skill set with 4 + uint32_t skillSetWith5; //!< The skill set with 5 + uint32_t skillSetWith6; //!< The skill set with 6 + bool localize; //!< Whether or localize + std::string gate_version; //!< The gate version + uint32_t kitID; //!< The kit ID + float priority; //!< The priority +}; + +class CDItemSetsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; + diff --git a/dDatabase/Tables/CDLevelProgressionLookupTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp similarity index 67% rename from dDatabase/Tables/CDLevelProgressionLookupTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp index 47a8fc0e..284cf484 100644 --- a/dDatabase/Tables/CDLevelProgressionLookupTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp @@ -1,10 +1,9 @@ #include "CDLevelProgressionLookupTable.h" -//! Constructor -CDLevelProgressionLookupTable::CDLevelProgressionLookupTable(void) { +void CDLevelProgressionLookupTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LevelProgressionLookup"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDLevelProgressionLookupTable::CDLevelProgressionLookupTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LevelProgressionLookup"); @@ -25,25 +25,18 @@ CDLevelProgressionLookupTable::CDLevelProgressionLookupTable(void) { entry.requiredUScore = tableData.getIntField("requiredUScore", -1); entry.BehaviorEffect = tableData.getStringField("BehaviorEffect", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } tableData.finalize(); } -//! Queries the table with a custom "where" clause std::vector CDLevelProgressionLookupTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDLevelProgressionLookupTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDLevelProgressionLookupTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h similarity index 54% rename from dDatabase/Tables/CDLevelProgressionLookupTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h index 070b2e0c..050d910e 100644 --- a/dDatabase/Tables/CDLevelProgressionLookupTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h @@ -4,20 +4,15 @@ #include "CDTable.h" struct CDLevelProgressionLookup { - unsigned int id; //!< The Level ID - unsigned int requiredUScore; //!< The required LEGO Score + uint32_t id; //!< The Level ID + uint32_t requiredUScore; //!< The required LEGO Score std::string BehaviorEffect; //!< The behavior effect attached to this }; -class CDLevelProgressionLookupTable : public CDTable { -private: - std::vector entries; - +class CDLevelProgressionLookupTable : public CDTable> { public: - CDLevelProgressionLookupTable(); + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - // Gets all the entries in the table - std::vector GetEntries(void) const; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp new file mode 100644 index 00000000..cd8ae5c4 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp @@ -0,0 +1,60 @@ +#include "CDLootMatrixTable.h" + +CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const { + CDLootMatrix entry{}; + if (tableData.eof()) return entry; + entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1); + entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1); + entry.percent = tableData.getFloatField("percent", -1.0f); + entry.minToDrop = tableData.getIntField("minToDrop", -1); + entry.maxToDrop = tableData.getIntField("maxToDrop", -1); + entry.flagID = tableData.getIntField("flagID", -1); + UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); + return entry; +} + +void CDLootMatrixTable::LoadValuesFromDatabase() { + + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootMatrix"); + 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 LootMatrix"); + while (!tableData.eof()) { + CDLootMatrix entry; + uint32_t lootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); + + entries[lootMatrixIndex].push_back(ReadRow(tableData)); + tableData.nextRow(); + } +} + +const LootMatrixEntries& CDLootMatrixTable::GetMatrix(uint32_t matrixId) { + auto& entries = GetEntriesMutable(); + auto itr = entries.find(matrixId); + if (itr != entries.end()) { + return itr->second; + } + + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootMatrix where LootMatrixIndex = ?;"); + query.bind(1, static_cast(matrixId)); + + auto tableData = query.execQuery(); + while (!tableData.eof()) { + entries[matrixId].push_back(ReadRow(tableData)); + tableData.nextRow(); + } + + return entries[matrixId]; +} + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h new file mode 100644 index 00000000..b0ce7e0f --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h @@ -0,0 +1,28 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDLootMatrix { + uint32_t LootTableIndex; //!< The Loot Table Index + uint32_t RarityTableIndex; //!< The Rarity Table Index + float percent; //!< The percent that this matrix is used? + uint32_t minToDrop; //!< The minimum amount of loot from this matrix to drop + uint32_t maxToDrop; //!< The maximum amount of loot from this matrix to drop + uint32_t flagID; //!< ??? + UNUSED(std::string gate_version); //!< The Gate Version +}; + +typedef uint32_t LootMatrixIndex; +typedef std::vector LootMatrixEntries; + +class CDLootMatrixTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + + // Gets a matrix by ID or inserts a blank one if none existed. + const LootMatrixEntries& GetMatrix(uint32_t matrixId); +private: + CDLootMatrix ReadRow(CppSQLite3Query& tableData) const; +}; + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp new file mode 100644 index 00000000..d5e9d4dc --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp @@ -0,0 +1,88 @@ +#include "CDLootTableTable.h" +#include "CDClientManager.h" +#include "CDComponentsRegistryTable.h" +#include "CDItemComponentTable.h" +#include "eReplicaComponentType.h" + +// Sort the tables by their rarity so the highest rarity items are first. +void SortTable(LootTableEntries& table) { + auto* componentsRegistryTable = CDClientManager::GetTable(); + auto* itemComponentTable = CDClientManager::GetTable(); + // We modify the table in place so the outer loop keeps track of what is sorted + // and the inner loop finds the highest rarity item and swaps it with the current position + // of the outer loop. + for (auto oldItrOuter = table.begin(); oldItrOuter != table.end(); oldItrOuter++) { + auto lootToInsert = oldItrOuter; + // Its fine if this starts at 0, even if this doesnt match lootToInsert as the actual highest will + // either be found and overwrite these values, or the original is somehow zero and is still the highest rarity. + uint32_t highestLootRarity = 0; + for (auto oldItrInner = oldItrOuter; oldItrInner != table.end(); oldItrInner++) { + uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(oldItrInner->itemid, eReplicaComponentType::ITEM); + uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity; + if (rarity > highestLootRarity) { + highestLootRarity = rarity; + lootToInsert = oldItrInner; + } + } + std::swap(*oldItrOuter, *lootToInsert); + } +} + +CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const { + CDLootTable entry{}; + if (tableData.eof()) return entry; + entry.itemid = tableData.getIntField("itemid", -1); + entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false; + entry.sortPriority = tableData.getIntField("sortPriority", -1); + return entry; +} + +void CDLootTableTable::LoadValuesFromDatabase() { + + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootTable"); + 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 LootTable"); + while (!tableData.eof()) { + CDLootTable entry; + uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1); + + entries[lootTableIndex].push_back(ReadRow(tableData)); + tableData.nextRow(); + } + for (auto& [id, table] : entries) { + SortTable(table); + } +} + +const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) { + auto& entries = GetEntriesMutable(); + auto itr = entries.find(tableId); + if (itr != entries.end()) { + return itr->second; + } + + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootTable WHERE LootTableIndex = ?;"); + query.bind(1, static_cast(tableId)); + auto tableData = query.execQuery(); + + while (!tableData.eof()) { + CDLootTable entry; + entries[tableId].push_back(ReadRow(tableData)); + tableData.nextRow(); + } + SortTable(entries[tableId]); + + return entries[tableId]; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h new file mode 100644 index 00000000..416bd87a --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h @@ -0,0 +1,25 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDLootTable { + uint32_t itemid; //!< The LOT of the item + uint32_t LootTableIndex; //!< The Loot Table Index + bool MissionDrop; //!< Whether or not this loot table is a mission drop + uint32_t sortPriority; //!< The sorting priority +}; + +typedef uint32_t LootTableIndex; +typedef std::vector LootTableEntries; + +class CDLootTableTable : public CDTable> { +private: + CDLootTable ReadRow(CppSQLite3Query& tableData) const; + +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + const LootTableEntries& GetTable(uint32_t tableId); +}; + diff --git a/dDatabase/Tables/CDMissionEmailTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp similarity index 73% rename from dDatabase/Tables/CDMissionEmailTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp index ed855d8a..1123bfec 100644 --- a/dDatabase/Tables/CDMissionEmailTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp @@ -1,10 +1,9 @@ #include "CDMissionEmailTable.h" -//! Constructor -CDMissionEmailTable::CDMissionEmailTable(void) { +void CDMissionEmailTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionEmail"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDMissionEmailTable::CDMissionEmailTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionEmail"); @@ -26,11 +26,11 @@ CDMissionEmailTable::CDMissionEmailTable(void) { entry.notificationGroup = tableData.getIntField("notificationGroup", -1); entry.missionID = tableData.getIntField("missionID", -1); entry.attachmentLOT = tableData.getIntField("attachmentLOT", 0); - entry.localize = (bool)tableData.getIntField("localize", -1); + entry.localize = static_cast(tableData.getIntField("localize", 1)); entry.locStatus = tableData.getIntField("locStatus", -1); entry.gate_version = tableData.getStringField("gate_version", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -39,16 +39,9 @@ CDMissionEmailTable::CDMissionEmailTable(void) { //! Queries the table with a custom "where" clause std::vector CDMissionEmailTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDMissionEmailTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h new file mode 100644 index 00000000..ac2dba81 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h @@ -0,0 +1,23 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDMissionEmail { + uint32_t ID; + uint32_t messageType; + uint32_t notificationGroup; + uint32_t missionID; + uint32_t attachmentLOT; + bool localize; + uint32_t locStatus; + std::string gate_version; +}; + + +class CDMissionEmailTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp similarity index 74% rename from dDatabase/Tables/CDMissionNPCComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp index 5672ed67..efe284d4 100644 --- a/dDatabase/Tables/CDMissionNPCComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDMissionNPCComponentTable.h" -//! Constructor -CDMissionNPCComponentTable::CDMissionNPCComponentTable(void) { +void CDMissionNPCComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionNPCComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDMissionNPCComponentTable::CDMissionNPCComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionNPCComponent"); @@ -27,7 +27,7 @@ CDMissionNPCComponentTable::CDMissionNPCComponentTable(void) { entry.acceptsMission = tableData.getIntField("acceptsMission", -1) == 1 ? true : false; entry.gate_version = tableData.getStringField("gate_version", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -37,15 +37,9 @@ CDMissionNPCComponentTable::CDMissionNPCComponentTable(void) { //! Queries the table with a custom "where" clause std::vector CDMissionNPCComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDMissionNPCComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h similarity index 62% rename from dDatabase/Tables/CDMissionNPCComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h index a7aeb145..1eba2fad 100644 --- a/dDatabase/Tables/CDMissionNPCComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h @@ -4,24 +4,17 @@ #include "CDTable.h" struct CDMissionNPCComponent { - unsigned int id; //!< The ID - unsigned int missionID; //!< The Mission ID + uint32_t id; //!< The ID + uint32_t missionID; //!< The Mission ID bool offersMission; //!< Whether or not this NPC offers a mission bool acceptsMission; //!< Whether or not this NPC accepts a mission std::string gate_version; //!< The gate version }; -class CDMissionNPCComponentTable : public CDTable { -private: - std::vector entries; - +class CDMissionNPCComponentTable : public CDTable> { public: - CDMissionNPCComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - // Gets all the entries in the table - std::vector GetEntries(void) const; - }; diff --git a/dDatabase/Tables/CDMissionTasksTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp similarity index 77% rename from dDatabase/Tables/CDMissionTasksTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp index f32dca1b..c5b6620f 100644 --- a/dDatabase/Tables/CDMissionTasksTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp @@ -1,10 +1,9 @@ #include "CDMissionTasksTable.h" -//! Constructor -CDMissionTasksTable::CDMissionTasksTable(void) { +void CDMissionTasksTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MissionTasks"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDMissionTasksTable::CDMissionTasksTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionTasks"); @@ -35,7 +35,7 @@ CDMissionTasksTable::CDMissionTasksTable(void) { UNUSED(entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false); UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -44,7 +44,7 @@ CDMissionTasksTable::CDMissionTasksTable(void) { std::vector CDMissionTasksTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); @@ -54,18 +54,16 @@ std::vector CDMissionTasksTable::Query(std::function CDMissionTasksTable::GetByMissionID(uint32_t missionID) { std::vector tasks; - for (auto& entry : this->entries) { + // TODO: this should not be linear(?) and also shouldnt need to be a pointer + for (auto& entry : GetEntriesMutable()) { if (entry.id == missionID) { - CDMissionTasks* task = const_cast(&entry); - - tasks.push_back(task); + tasks.push_back(&entry); } } return tasks; } -const std::vector& CDMissionTasksTable::GetEntries(void) const { - return this->entries; +const typename CDMissionTasksTable::StorageType& CDMissionTasksTable::GetEntries() const { + return CDTable::GetEntries(); } - diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h new file mode 100644 index 00000000..97553359 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h @@ -0,0 +1,33 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDMissionTasks { + uint32_t id; //!< The Mission ID that the task belongs to + UNUSED(uint32_t locStatus); //!< ??? + uint32_t taskType; //!< The task type + uint32_t target; //!< The mission target + std::string targetGroup; //!< The mission target group + int32_t targetValue; //!< The target value + std::string taskParam1; //!< The task param 1 + UNUSED(std::string largeTaskIcon); //!< ??? + UNUSED(uint32_t IconID); //!< ??? + uint32_t uid; //!< ??? + UNUSED(uint32_t largeTaskIconID); //!< ??? + UNUSED(bool localize); //!< Whether or not the task should be localized + UNUSED(std::string gate_version); //!< ??? +}; + +class CDMissionTasksTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); + + std::vector GetByMissionID(uint32_t missionID); + + // TODO: Remove this and replace it with a proper lookup function. + const CDTable::StorageType& GetEntries() const; +}; + diff --git a/dDatabase/Tables/CDMissionsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp similarity index 92% rename from dDatabase/Tables/CDMissionsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp index d4ee40ae..8862b1db 100644 --- a/dDatabase/Tables/CDMissionsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp @@ -2,11 +2,10 @@ CDMissions CDMissionsTable::Default = {}; -//! Constructor -CDMissionsTable::CDMissionsTable(void) { +void CDMissionsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Missions"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -17,7 +16,8 @@ CDMissionsTable::CDMissionsTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Missions"); @@ -76,7 +76,7 @@ CDMissionsTable::CDMissionsTable(void) { UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1)); entry.reward_bankinventory = tableData.getIntField("reward_bankinventory", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -87,19 +87,15 @@ CDMissionsTable::CDMissionsTable(void) { std::vector CDMissionsTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } -const std::vector& CDMissionsTable::GetEntries(void) const { - return this->entries; -} - const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const { - for (const auto& entry : entries) { + for (const auto& entry : GetEntries()) { if (entry.id == missionID) { return const_cast(&entry); } @@ -109,7 +105,7 @@ const CDMissions* CDMissionsTable::GetPtrByMissionID(uint32_t missionID) const { } const CDMissions& CDMissionsTable::GetByMissionID(uint32_t missionID, bool& found) const { - for (const auto& entry : entries) { + for (const auto& entry : GetEntries()) { if (entry.id == missionID) { found = true; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h new file mode 100644 index 00000000..6ba7b19e --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h @@ -0,0 +1,75 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" +#include +#include + +struct CDMissions { + int32_t id; //!< The Mission ID + std::string defined_type; //!< The type of mission + std::string defined_subtype; //!< The subtype of the mission + int32_t UISortOrder; //!< The UI Sort Order for the mission + int32_t offer_objectID; //!< The LOT of the mission giver + int32_t target_objectID; //!< The LOT of the mission's target + int64_t reward_currency; //!< The amount of currency to reward the player + int32_t LegoScore; //!< The amount of LEGO Score to reward the player + int64_t reward_reputation; //!< The reputation to award the player + bool isChoiceReward; //!< Whether or not the user has the option to choose their loot + int32_t reward_item1; //!< The first rewarded item + int32_t reward_item1_count; //!< The count of the first item to be rewarded + int32_t reward_item2; //!< The second rewarded item + int32_t reward_item2_count; //!< The count of the second item to be rewarded + int32_t reward_item3; //!< The third rewarded item + int32_t reward_item3_count; //!< The count of the third item to be rewarded + int32_t reward_item4; //!< The fourth rewarded item + int32_t reward_item4_count; //!< The count of the fourth item to be rewarded + int32_t reward_emote; //!< The first emote to be rewarded + int32_t reward_emote2; //!< The second emote to be rewarded + int32_t reward_emote3; //!< The third emote to be rewarded + int32_t reward_emote4; //!< The fourth emote to be rewarded + int32_t reward_maximagination; //!< The amount of max imagination to reward + int32_t reward_maxhealth; //!< The amount of max health to reward + int32_t reward_maxinventory; //!< The amount of max inventory to reward + int32_t reward_maxmodel; //!< ??? + int32_t reward_maxwidget; //!< ??? + int32_t reward_maxwallet; //!< ??? + bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission) + int64_t reward_currency_repeatable; //!< The repeatable reward + int32_t reward_item1_repeatable; //!< The first rewarded item + int32_t reward_item1_repeat_count; //!< The count of the first item to be rewarded + int32_t reward_item2_repeatable; //!< The second rewarded item + int32_t reward_item2_repeat_count; //!< The count of the second item to be rewarded + int32_t reward_item3_repeatable; //!< The third rewarded item + int32_t reward_item3_repeat_count; //!< The count of the third item to be rewarded + int32_t reward_item4_repeatable; //!< The fourth rewarded item + int32_t reward_item4_repeat_count; //!< The count of the fourth item to be rewarded + int32_t time_limit; //!< The time limit of the mission + bool isMission; //!< Maybe to differentiate between missions and achievements? + int32_t missionIconID; //!< The mission icon ID + std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions + bool localize; //!< Whether or not to localize the mission + bool inMOTD; //!< In Match of the Day(?) + int64_t cooldownTime; //!< The mission cooldown time + bool isRandom; //!< ??? + std::string randomPool; //!< ??? + int32_t UIPrereqID; //!< ??? + UNUSED(std::string gate_version); //!< The gate version + UNUSED(std::string HUDStates); //!< ??? + UNUSED(int32_t locStatus); //!< ??? + int32_t reward_bankinventory; //!< The amount of bank space this mission rewards +}; + +class CDMissionsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); + + const CDMissions* GetPtrByMissionID(uint32_t missionID) const; + + const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const; + + static CDMissions Default; +}; + diff --git a/dDatabase/Tables/CDMovementAIComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp similarity index 74% rename from dDatabase/Tables/CDMovementAIComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp index 3b9cc4f4..48964a59 100644 --- a/dDatabase/Tables/CDMovementAIComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDMovementAIComponentTable.h" -//! Constructor -CDMovementAIComponentTable::CDMovementAIComponentTable(void) { +void CDMovementAIComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM MovementAIComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDMovementAIComponentTable::CDMovementAIComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MovementAIComponent"); @@ -30,25 +30,18 @@ CDMovementAIComponentTable::CDMovementAIComponentTable(void) { entry.WanderRadius = tableData.getFloatField("WanderRadius", -1.0f); entry.attachedPath = tableData.getStringField("attachedPath", ""); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } tableData.finalize(); } -//! Queries the table with a custom "where" clause std::vector CDMovementAIComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDMovementAIComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDMovementAIComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h similarity index 68% rename from dDatabase/Tables/CDMovementAIComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h index 84896e2c..34d01e3d 100644 --- a/dDatabase/Tables/CDMovementAIComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h @@ -4,7 +4,7 @@ #include "CDTable.h" struct CDMovementAIComponent { - unsigned int id; + uint32_t id; std::string MovementType; float WanderChance; float WanderDelayMin; @@ -14,15 +14,9 @@ struct CDMovementAIComponent { std::string attachedPath; }; -class CDMovementAIComponentTable : public CDTable { -private: - std::vector entries; - +class CDMovementAIComponentTable : public CDTable> { public: - CDMovementAIComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - // Gets all the entries in the table - std::vector GetEntries(void) const; }; diff --git a/dDatabase/Tables/CDMovingPlatformComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMovingPlatformComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDMovingPlatformComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMovingPlatformComponentTable.cpp diff --git a/dDatabase/Tables/CDMovingPlatformComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMovingPlatformComponentTable.h similarity index 100% rename from dDatabase/Tables/CDMovingPlatformComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMovingPlatformComponentTable.h diff --git a/dDatabase/Tables/CDObjectSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp similarity index 69% rename from dDatabase/Tables/CDObjectSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp index 2e8b3fb2..9933fe7f 100644 --- a/dDatabase/Tables/CDObjectSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp @@ -1,10 +1,9 @@ #include "CDObjectSkillsTable.h" -//! Constructor -CDObjectSkillsTable::CDObjectSkillsTable(void) { +void CDObjectSkillsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ObjectSkills"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDObjectSkillsTable::CDObjectSkillsTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ObjectSkills"); @@ -26,24 +26,18 @@ CDObjectSkillsTable::CDObjectSkillsTable(void) { entry.castOnType = tableData.getIntField("castOnType", -1); entry.AICombatWeight = tableData.getIntField("AICombatWeight", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } tableData.finalize(); } -//! Queries the table with a custom "where" clause std::vector CDObjectSkillsTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDObjectSkillsTable::GetEntries(void) const { - return this->entries; -} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h new file mode 100644 index 00000000..a2a8d440 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h @@ -0,0 +1,19 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDObjectSkills { + uint32_t objectTemplate; //!< The LOT of the item + uint32_t skillID; //!< The Skill ID of the object + uint32_t castOnType; //!< ??? + uint32_t AICombatWeight; //!< ??? +}; + +class CDObjectSkillsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Queries the table with a custom "where" clause + std::vector Query(std::function predicate); +}; + diff --git a/dDatabase/Tables/CDObjectsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp similarity index 58% rename from dDatabase/Tables/CDObjectsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp index c68c3e6a..d1f6771e 100644 --- a/dDatabase/Tables/CDObjectsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp @@ -1,10 +1,12 @@ #include "CDObjectsTable.h" -//! Constructor -CDObjectsTable::CDObjectsTable(void) { -#ifdef CDCLIENT_CACHE_ALL +namespace { + CDObjects m_default; +}; + +void CDObjectsTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Objects"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -16,47 +18,46 @@ CDObjectsTable::CDObjectsTable(void) { // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Objects"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDObjects entry; entry.id = tableData.getIntField("id", -1); entry.name = tableData.getStringField("name", ""); - entry.placeable = tableData.getIntField("placeable", -1); + UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);) entry.type = tableData.getStringField("type", ""); - entry.description = tableData.getStringField("description", ""); - entry.localize = tableData.getIntField("localize", -1); - entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1); - entry.displayName = tableData.getStringField("displayName", ""); + UNUSED_COLUMN(entry.description = tableData.getStringField("description", "");) + UNUSED_COLUMN(entry.localize = tableData.getIntField("localize", -1);) + UNUSED_COLUMN(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1);) + UNUSED_COLUMN(entry.displayName = tableData.getStringField("displayName", "");) entry.interactionDistance = tableData.getFloatField("interactionDistance", -1.0f); - entry.nametag = tableData.getIntField("nametag", -1); - entry._internalNotes = tableData.getStringField("_internalNotes", ""); - entry.locStatus = tableData.getIntField("locStatus", -1); - entry.gate_version = tableData.getStringField("gate_version", ""); - entry.HQ_valid = tableData.getIntField("HQ_valid", -1); + UNUSED_COLUMN(entry.nametag = tableData.getIntField("nametag", -1);) + UNUSED_COLUMN(entry._internalNotes = tableData.getStringField("_internalNotes", "");) + UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1);) + UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");) + UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);) - this->entries.insert(std::make_pair(entry.id, entry)); + entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); } tableData.finalize(); -#endif m_default.id = 0; } -const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) { - const auto& it = this->entries.find(LOT); - if (it != this->entries.end()) { +const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) { + auto& entries = GetEntriesMutable(); + const auto& it = entries.find(LOT); + if (it != entries.end()) { return it->second; } -#ifndef CDCLIENT_CACHE_ALL - std::stringstream query; + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;"); + query.bind(1, static_cast(LOT)); - query << "SELECT * FROM Objects WHERE id = " << std::to_string(LOT); - - auto tableData = CDClientDatabase::ExecuteQuery(query.str()); + auto tableData = query.execQuery(); if (tableData.eof()) { - this->entries.insert(std::make_pair(LOT, m_default)); + entries.insert(std::make_pair(LOT, m_default)); return m_default; } @@ -78,7 +79,7 @@ const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) { UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); UNUSED(entry.HQ_valid = tableData.getIntField("HQ_valid", -1)); - this->entries.insert(std::make_pair(entry.id, entry)); + entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); } @@ -88,7 +89,6 @@ const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) { if (it2 != entries.end()) { return it2->second; } -#endif return m_default; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h new file mode 100644 index 00000000..add21c8f --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h @@ -0,0 +1,29 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDObjects { + uint32_t id; //!< The LOT of the object + std::string name; //!< The internal name of the object + UNUSED(uint32_t placeable); //!< Whether or not the object is placable + std::string type; //!< The object type + UNUSED(std::string description); //!< An internal description of the object + UNUSED(uint32_t localize); //!< Whether or not the object should localize + UNUSED(uint32_t npcTemplateID); //!< Something related to NPCs... + UNUSED(std::string displayName); //!< The display name of the object + float interactionDistance; //!< The interaction distance of the object + UNUSED(uint32_t nametag); //!< ??? + UNUSED(std::string _internalNotes); //!< Some internal notes (rarely used) + UNUSED(uint32_t locStatus); //!< ??? + UNUSED(std::string gate_version); //!< The gate version for the object + UNUSED(uint32_t HQ_valid); //!< Probably used for the Nexus HQ database on LEGOUniverse.com +}; + +class CDObjectsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + // Gets an entry by ID + const CDObjects& GetByID(uint32_t LOT); +}; + diff --git a/dDatabase/Tables/CDPackageComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp similarity index 69% rename from dDatabase/Tables/CDPackageComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp index efb85eeb..da100026 100644 --- a/dDatabase/Tables/CDPackageComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDPackageComponentTable.h" -//! Constructor -CDPackageComponentTable::CDPackageComponentTable(void) { +void CDPackageComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM PackageComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -14,8 +13,8 @@ CDPackageComponentTable::CDPackageComponentTable(void) { tableSize.finalize(); - // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PackageComponent"); @@ -25,7 +24,7 @@ CDPackageComponentTable::CDPackageComponentTable(void) { entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); entry.packageType = tableData.getIntField("packageType", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -35,15 +34,9 @@ CDPackageComponentTable::CDPackageComponentTable(void) { //! Queries the table with a custom "where" clause std::vector CDPackageComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDPackageComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDPackageComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h similarity index 57% rename from dDatabase/Tables/CDPackageComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h index 6c11ab39..ad7003df 100644 --- a/dDatabase/Tables/CDPackageComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h @@ -4,19 +4,14 @@ #include "CDTable.h" struct CDPackageComponent { - unsigned int id; - unsigned int LootMatrixIndex; - unsigned int packageType; + uint32_t id; + uint32_t LootMatrixIndex; + uint32_t packageType; }; -class CDPackageComponentTable : public CDTable { -private: - std::vector entries; - +class CDPackageComponentTable : public CDTable> { public: - CDPackageComponentTable(void); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp new file mode 100644 index 00000000..f3371ecb --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -0,0 +1,64 @@ +#include "CDPetComponentTable.h" + +namespace { + // Default entries for fallback + CDPetComponent defaultEntry{ + .id = 0, + UNUSED_ENTRY(.minTameUpdateTime = 60.0f,) + UNUSED_ENTRY(.maxTameUpdateTime = 300.0f,) + UNUSED_ENTRY(.percentTameChance = 101.0f,) + UNUSED_ENTRY(.tameability = 100.0f,) + UNUSED_ENTRY(.elementType = 1,) + .walkSpeed = 2.5f, + .runSpeed = 5.0f, + .sprintSpeed = 10.0f, + UNUSED_ENTRY(.idleTimeMin = 60.0f,) + UNUSED_ENTRY(.idleTimeMax = 300.0f,) + UNUSED_ENTRY(.petForm = 0,) + .imaginationDrainRate = 60.0f, + UNUSED_ENTRY(.AudioMetaEventSet = "",) + UNUSED_ENTRY(.buffIDs = "",) + }; +} + +void CDPetComponentTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PetComponent"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + const uint32_t componentID = tableData.getIntField("id", defaultEntry.id); + + auto& entry = entries[componentID]; + + entry.id = componentID; + UNUSED_COLUMN(entry.minTameUpdateTime = tableData.getFloatField("minTameUpdateTime", defaultEntry.minTameUpdateTime)); + UNUSED_COLUMN(entry.maxTameUpdateTime = tableData.getFloatField("maxTameUpdateTime", defaultEntry.maxTameUpdateTime)); + UNUSED_COLUMN(entry.percentTameChance = tableData.getFloatField("percentTameChance", defaultEntry.percentTameChance)); + UNUSED_COLUMN(entry.tameability = tableData.getFloatField("tamability", defaultEntry.tameability)); // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(entry.elementType = tableData.getIntField("elementType", defaultEntry.elementType)); + entry.walkSpeed = static_cast(tableData.getFloatField("walkSpeed", defaultEntry.walkSpeed)); + entry.runSpeed = static_cast(tableData.getFloatField("runSpeed", defaultEntry.runSpeed)); + entry.sprintSpeed = static_cast(tableData.getFloatField("sprintSpeed", defaultEntry.sprintSpeed)); + UNUSED_COLUMN(entry.idleTimeMin = tableData.getFloatField("idleTimeMin", defaultEntry.idleTimeMin)); + UNUSED_COLUMN(entry.idleTimeMax = tableData.getFloatField("idleTimeMax", defaultEntry.idleTimeMax)); + UNUSED_COLUMN(entry.petForm = tableData.getIntField("petForm", defaultEntry.petForm)); + entry.imaginationDrainRate = static_cast(tableData.getFloatField("imaginationDrainRate", defaultEntry.imaginationDrainRate)); + UNUSED_COLUMN(entry.AudioMetaEventSet = tableData.getStringField("AudioMetaEventSet", defaultEntry.AudioMetaEventSet)); + UNUSED_COLUMN(entry.buffIDs = tableData.getStringField("buffIDs", defaultEntry.buffIDs)); + + tableData.nextRow(); + } +} + +void CDPetComponentTable::LoadValuesFromDefaults() { + GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry)); +} + +CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { + auto& entries = GetEntriesMutable(); + auto itr = entries.find(componentID); + if (itr == entries.end()) { + LOG("Unable to load pet component (ID %i) values from database! Using default values instead.", componentID); + return defaultEntry; + } + return itr->second; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h new file mode 100644 index 00000000..42890253 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -0,0 +1,42 @@ +#pragma once +#include "CDTable.h" +#include +#include + +struct CDPetComponent { + uint32_t id; + UNUSED_COLUMN(float minTameUpdateTime;) + UNUSED_COLUMN(float maxTameUpdateTime;) + UNUSED_COLUMN(float percentTameChance;) + UNUSED_COLUMN(float tameability;) // Mispelled as "tamability" in CDClient + UNUSED_COLUMN(uint32_t elementType;) + float walkSpeed; + float runSpeed; + float sprintSpeed; + UNUSED_COLUMN(float idleTimeMin;) + UNUSED_COLUMN(float idleTimeMax;) + UNUSED_COLUMN(uint32_t petForm;) + float imaginationDrainRate; + UNUSED_COLUMN(std::string AudioMetaEventSet;) + UNUSED_COLUMN(std::string buffIDs;) +}; + +class CDPetComponentTable : public CDTable> { +public: + + /** + * Load values from the CD client database + */ + void LoadValuesFromDatabase(); + + /** + * Load the default values into memory instead of attempting to connect to the CD client database + */ + void LoadValuesFromDefaults(); + + /** + * Gets the pet component table corresponding to the pet component ID + * @returns A reference to the corresponding table, or the default if one could not be found + */ + CDPetComponent& GetByID(const uint32_t componentID); +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp new file mode 100644 index 00000000..34671f3c --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp @@ -0,0 +1,37 @@ +#include "CDPhysicsComponentTable.h" + +void CDPhysicsComponentTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + CDPhysicsComponent entry; + entry.id = tableData.getIntField("id", -1); + entry.bStatic = tableData.getIntField("static", -1) != 0; + entry.physicsAsset = tableData.getStringField("physics_asset", ""); + UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0); + UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0); + entry.speed = tableData.getFloatField("speed", -1); + UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1)); + entry.playerHeight = tableData.getFloatField("playerHeight"); + entry.playerRadius = tableData.getFloatField("playerRadius"); + entry.pcShapeType = tableData.getIntField("pcShapeType"); + entry.collisionGroup = tableData.getIntField("collisionGroup"); + UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed")); + UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset")); + UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed")); + UNUSED(entry->friction = tableData.getFloatField("friction")); + UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset")); + + entries.insert(std::make_pair(entry.id, entry)); + tableData.nextRow(); + } + + tableData.finalize(); +} + +CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) { + auto& entries = GetEntriesMutable(); + auto itr = entries.find(componentID); + return itr != entries.end() ? &itr->second : nullptr; +} + diff --git a/dDatabase/Tables/CDPhysicsComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h similarity index 70% rename from dDatabase/Tables/CDPhysicsComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h index e63d337d..f0a62139 100644 --- a/dDatabase/Tables/CDPhysicsComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h @@ -3,7 +3,7 @@ #include struct CDPhysicsComponent { - int id; + int32_t id; bool bStatic; std::string physicsAsset; UNUSED(bool jump); @@ -12,8 +12,8 @@ struct CDPhysicsComponent { UNUSED(float rotSpeed); float playerHeight; float playerRadius; - int pcShapeType; - int collisionGroup; + int32_t pcShapeType; + int32_t collisionGroup; UNUSED(float airSpeed); UNUSED(std::string boundaryAsset); UNUSED(float jumpAirSpeed); @@ -21,14 +21,10 @@ struct CDPhysicsComponent { UNUSED(std::string gravityVolumeAsset); }; -class CDPhysicsComponentTable : public CDTable { +class CDPhysicsComponentTable : public CDTable> { public: - CDPhysicsComponentTable(); - ~CDPhysicsComponentTable(); + void LoadValuesFromDatabase(); static const std::string GetTableName() { return "PhysicsComponent"; }; - CDPhysicsComponent* GetByID(unsigned int componentID); - -private: - std::map m_entries; + CDPhysicsComponent* GetByID(uint32_t componentID); }; diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp similarity index 79% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp index 1fead45e..dfb591d1 100644 --- a/dDatabase/Tables/CDPropertyEntranceComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp @@ -1,7 +1,10 @@ - #include "CDPropertyEntranceComponentTable.h" -CDPropertyEntranceComponentTable::CDPropertyEntranceComponentTable() { +namespace { + CDPropertyEntranceComponent defaultEntry{}; +}; + +void CDPropertyEntranceComponentTable::LoadValuesFromDatabase() { // First, get the size of the table size_t size = 0; @@ -13,7 +16,8 @@ CDPropertyEntranceComponentTable::CDPropertyEntranceComponentTable() { tableSize.finalize(); - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PropertyEntranceComponent;"); while (!tableData.eof()) { @@ -25,7 +29,7 @@ CDPropertyEntranceComponentTable::CDPropertyEntranceComponentTable() { tableData.getStringField("groupType", "") }; - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -33,11 +37,10 @@ CDPropertyEntranceComponentTable::CDPropertyEntranceComponentTable() { } CDPropertyEntranceComponent CDPropertyEntranceComponentTable::GetByID(uint32_t id) { - for (const auto& entry : entries) { + for (const auto& entry : GetEntries()) { if (entry.id == id) return entry; } return defaultEntry; } - diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h similarity index 53% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h index 925fd1be..f687b881 100644 --- a/dDatabase/Tables/CDPropertyEntranceComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h @@ -9,15 +9,9 @@ struct CDPropertyEntranceComponent { std::string groupType; }; -class CDPropertyEntranceComponentTable : public CDTable { +class CDPropertyEntranceComponentTable : public CDTable> { public: - CDPropertyEntranceComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause CDPropertyEntranceComponent GetByID(uint32_t id); - - // Gets all the entries in the table - [[nodiscard]] std::vector GetEntries() const { return entries; } -private: - std::vector entries{}; - CDPropertyEntranceComponent defaultEntry{}; }; diff --git a/dDatabase/Tables/CDPropertyTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp similarity index 78% rename from dDatabase/Tables/CDPropertyTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp index 4caa6dc5..4046b6fa 100644 --- a/dDatabase/Tables/CDPropertyTemplateTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp @@ -1,6 +1,10 @@ #include "CDPropertyTemplateTable.h" -CDPropertyTemplateTable::CDPropertyTemplateTable() { +namespace { + CDPropertyTemplate defaultEntry{}; +}; + +void CDPropertyTemplateTable::LoadValuesFromDatabase() { // First, get the size of the table size_t size = 0; @@ -12,7 +16,8 @@ CDPropertyTemplateTable::CDPropertyTemplateTable() { tableSize.finalize(); - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PropertyTemplate;"); while (!tableData.eof()) { @@ -23,7 +28,7 @@ CDPropertyTemplateTable::CDPropertyTemplateTable() { tableData.getStringField("spawnName", "") }; - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -31,7 +36,7 @@ CDPropertyTemplateTable::CDPropertyTemplateTable() { } CDPropertyTemplate CDPropertyTemplateTable::GetByMapID(uint32_t mapID) { - for (const auto& entry : entries) { + for (const auto& entry : GetEntries()) { if (entry.mapID == mapID) return entry; } diff --git a/dDatabase/Tables/CDPropertyTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h similarity index 57% rename from dDatabase/Tables/CDPropertyTemplateTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h index cb075dbf..e0c6c485 100644 --- a/dDatabase/Tables/CDPropertyTemplateTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h @@ -8,13 +8,9 @@ struct CDPropertyTemplate { std::string spawnName; }; -class CDPropertyTemplateTable : public CDTable { +class CDPropertyTemplateTable : public CDTable> { public: - CDPropertyTemplateTable(); + void LoadValuesFromDatabase(); - static const std::string GetTableName() { return "PropertyTemplate"; }; CDPropertyTemplate GetByMapID(uint32_t mapID); -private: - std::vector entries{}; - CDPropertyTemplate defaultEntry{}; }; diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp similarity index 68% rename from dDatabase/Tables/CDProximityMonitorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp index 688de056..279d6408 100644 --- a/dDatabase/Tables/CDProximityMonitorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDProximityMonitorComponentTable.h" -//! Constructor -CDProximityMonitorComponentTable::CDProximityMonitorComponentTable(void) { +void CDProximityMonitorComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ProximityMonitorComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDProximityMonitorComponentTable::CDProximityMonitorComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ProximityMonitorComponent"); @@ -26,25 +26,18 @@ CDProximityMonitorComponentTable::CDProximityMonitorComponentTable(void) { entry.LoadOnClient = tableData.getIntField("LoadOnClient", -1); entry.LoadOnServer = tableData.getIntField("LoadOnServer", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } tableData.finalize(); } -//! Queries the table with a custom "where" clause std::vector CDProximityMonitorComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDProximityMonitorComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h similarity index 64% rename from dDatabase/Tables/CDProximityMonitorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h index 38b7d43b..af2c385e 100644 --- a/dDatabase/Tables/CDProximityMonitorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h @@ -4,20 +4,15 @@ #include "CDTable.h" struct CDProximityMonitorComponent { - unsigned int id; + uint32_t id; std::string Proximities; bool LoadOnClient; bool LoadOnServer; }; -class CDProximityMonitorComponentTable : public CDTable { -private: - std::vector entries; - +class CDProximityMonitorComponentTable : public CDTable> { public: - CDProximityMonitorComponentTable(void); + void LoadValuesFromDatabase(); //! Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; }; diff --git a/dDatabase/Tables/CDRailActivatorComponent.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp similarity index 89% rename from dDatabase/Tables/CDRailActivatorComponent.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp index 2ff8990d..72a26beb 100644 --- a/dDatabase/Tables/CDRailActivatorComponent.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp @@ -1,8 +1,9 @@ #include "CDRailActivatorComponent.h" #include "GeneralUtils.h" -CDRailActivatorComponentTable::CDRailActivatorComponentTable() { +void CDRailActivatorComponentTable::LoadValuesFromDatabase() { auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RailActivatorComponent;"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDRailActivatorComponent entry; @@ -36,7 +37,7 @@ CDRailActivatorComponentTable::CDRailActivatorComponentTable() { entry.showNameBillboard = tableData.getIntField("ShowNameBillboard", 0); - m_Entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -44,7 +45,7 @@ CDRailActivatorComponentTable::CDRailActivatorComponentTable() { } CDRailActivatorComponent CDRailActivatorComponentTable::GetEntryByID(int32_t id) const { - for (const auto& entry : m_Entries) { + for (const auto& entry : GetEntries()) { if (entry.id == id) return entry; } @@ -52,10 +53,6 @@ CDRailActivatorComponent CDRailActivatorComponentTable::GetEntryByID(int32_t id) return {}; } -std::vector CDRailActivatorComponentTable::GetEntries() const { - return m_Entries; -} - std::pair CDRailActivatorComponentTable::EffectPairFromString(std::string& str) { const auto split = GeneralUtils::SplitString(str, ':'); if (split.size() == 2) { diff --git a/dDatabase/Tables/CDRailActivatorComponent.h b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h similarity index 75% rename from dDatabase/Tables/CDRailActivatorComponent.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h index 03dd0525..d9a94d37 100644 --- a/dDatabase/Tables/CDRailActivatorComponent.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h @@ -20,13 +20,10 @@ struct CDRailActivatorComponent { bool showNameBillboard; }; -class CDRailActivatorComponentTable : public CDTable { +class CDRailActivatorComponentTable : public CDTable> { public: - CDRailActivatorComponentTable(); - static const std::string GetTableName() { return "RailActivatorComponent"; }; + void LoadValuesFromDatabase(); [[nodiscard]] CDRailActivatorComponent GetEntryByID(int32_t id) const; - [[nodiscard]] std::vector GetEntries() const; private: static std::pair EffectPairFromString(std::string& str); - std::vector m_Entries{}; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp new file mode 100644 index 00000000..def27339 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp @@ -0,0 +1,35 @@ +#include "CDRarityTableTable.h" + +void CDRarityTableTable::LoadValuesFromDatabase() { + + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable"); + while (!tableSize.eof()) { + size = tableSize.getIntField(0, 0); + + tableSize.nextRow(); + } + + tableSize.finalize(); + + // Reserve the size + auto& entries = GetEntriesMutable(); + entries.reserve(size); + + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable order by randmax desc;"); + while (!tableData.eof()) { + uint32_t rarityTableIndex = tableData.getIntField("RarityTableIndex", -1); + + CDRarityTable entry; + entry.randmax = tableData.getFloatField("randmax", -1); + entry.rarity = tableData.getIntField("rarity", -1); + entries[rarityTableIndex].push_back(entry); + tableData.nextRow(); + } +} + +const std::vector& CDRarityTableTable::GetRarityTable(uint32_t id) { + return GetEntriesMutable()[id]; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h new file mode 100644 index 00000000..006ac986 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h @@ -0,0 +1,21 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDRarityTable { + float randmax; + uint32_t rarity; + + typedef uint32_t Index; +}; + +typedef std::vector RarityTable; + +class CDRarityTableTable : public CDTable>> { +public: + void LoadValuesFromDatabase(); + + const std::vector& GetRarityTable(uint32_t predicate); +}; + diff --git a/dDatabase/Tables/CDRebuildComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp similarity index 77% rename from dDatabase/Tables/CDRebuildComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp index d5c386d1..8a07db88 100644 --- a/dDatabase/Tables/CDRebuildComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDRebuildComponentTable.h" -//! Constructor -CDRebuildComponentTable::CDRebuildComponentTable(void) { +void CDRebuildComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RebuildComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDRebuildComponentTable::CDRebuildComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RebuildComponent"); @@ -32,25 +32,18 @@ CDRebuildComponentTable::CDRebuildComponentTable(void) { entry.post_imagination_cost = tableData.getIntField("post_imagination_cost", -1); entry.time_before_smash = tableData.getFloatField("time_before_smash", -1.0f); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } tableData.finalize(); } -//! Queries the table with a custom "where" clause std::vector CDRebuildComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDRebuildComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDRebuildComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h similarity index 62% rename from dDatabase/Tables/CDRebuildComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h index db70a47d..0eeb62ae 100644 --- a/dDatabase/Tables/CDRebuildComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h @@ -4,27 +4,22 @@ #include "CDTable.h" struct CDRebuildComponent { - unsigned int id; //!< The component Id + uint32_t id; //!< The component Id float reset_time; //!< The reset time float complete_time; //!< The complete time - unsigned int take_imagination; //!< The amount of imagination it costs + uint32_t take_imagination; //!< The amount of imagination it costs bool interruptible; //!< Whether or not the rebuild is interruptible bool self_activator; //!< Whether or not the rebuild is a rebuild activator itself std::string custom_modules; //!< The custom modules - unsigned int activityID; //!< The activity ID - unsigned int post_imagination_cost; //!< The post imagination cost + uint32_t activityID; //!< The activity ID + uint32_t post_imagination_cost; //!< The post imagination cost float time_before_smash; //!< The time before smash }; -class CDRebuildComponentTable : public CDTable { -private: - std::vector entries; - +class CDRebuildComponentTable : public CDTable> { public: - CDRebuildComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries() const; }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp new file mode 100644 index 00000000..8ade60a9 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp @@ -0,0 +1,48 @@ +#include "CDRewardCodesTable.h" + +void CDRewardCodesTable::LoadValuesFromDatabase() { + + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes"); + while (!tableSize.eof()) { + size = tableSize.getIntField(0, 0); + + tableSize.nextRow(); + } + + tableSize.finalize(); + + // Reserve the size + auto& entries = GetEntriesMutable(); + entries.reserve(size); + + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes"); + while (!tableData.eof()) { + CDRewardCode entry; + entry.id = tableData.getIntField("id", -1); + entry.code = tableData.getStringField("code", ""); + entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1); + UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1)); + UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "")); + + entries.push_back(entry); + tableData.nextRow(); + } +} + +LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const { + for (auto const &entry : GetEntries()){ + if (rewardCodeId == entry.id) return entry.attachmentLOT; + } + return LOT_NULL; +} + +uint32_t CDRewardCodesTable::GetCodeID(std::string code) const { + for (auto const &entry : GetEntries()){ + if (code == entry.code) return entry.id; + } + return -1; +} + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h new file mode 100644 index 00000000..aa64c3bb --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h @@ -0,0 +1,21 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + + +struct CDRewardCode { + uint32_t id; + std::string code; + LOT attachmentLOT; + UNUSED(uint32_t locStatus); + UNUSED(std::string gate_version); +}; + + +class CDRewardCodesTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + LOT GetAttachmentLOT(uint32_t rewardCodeId) const; + uint32_t GetCodeID(std::string code) const; +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp new file mode 100644 index 00000000..4539e417 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp @@ -0,0 +1,30 @@ +#include "CDRewardsTable.h" + +void CDRewardsTable::LoadValuesFromDatabase() { + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards"); + auto& entries = GetEntriesMutable(); + while (!tableData.eof()) { + CDRewards entry; + entry.id = tableData.getIntField("id", -1); + entry.levelID = tableData.getIntField("LevelID", -1); + entry.missionID = tableData.getIntField("MissionID", -1); + entry.rewardType = tableData.getIntField("RewardType", -1); + entry.value = tableData.getIntField("value", -1); + entry.count = tableData.getIntField("count", -1); + + entries.insert(std::make_pair(entry.id, entry)); + tableData.nextRow(); + } + + tableData.finalize(); +} + +std::vector CDRewardsTable::GetByLevelID(uint32_t levelID) { + std::vector result{}; + for (const auto& e : GetEntries()) { + if (e.second.levelID == levelID) result.push_back(e.second); + } + + return result; +} + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h new file mode 100644 index 00000000..cec787cf --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h @@ -0,0 +1,19 @@ +#pragma once +#include "CDTable.h" +#include + +struct CDRewards { + int32_t id; + int32_t levelID; + int32_t missionID; + int32_t rewardType; + int32_t value; + int32_t count; +}; + +class CDRewardsTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + + std::vector GetByLevelID(uint32_t levelID); +}; diff --git a/dDatabase/Tables/CDScriptComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp similarity index 66% rename from dDatabase/Tables/CDScriptComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp index 8050c139..02e3e29c 100644 --- a/dDatabase/Tables/CDScriptComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp @@ -1,10 +1,13 @@ #include "CDScriptComponentTable.h" -//! Constructor -CDScriptComponentTable::CDScriptComponentTable(void) { +namespace { + CDScriptComponent m_ToReturnWhenNoneFound; +}; + +void CDScriptComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ScriptComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -16,22 +19,24 @@ CDScriptComponentTable::CDScriptComponentTable(void) { // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ScriptComponent"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDScriptComponent entry; entry.id = tableData.getIntField("id", -1); entry.script_name = tableData.getStringField("script_name", ""); entry.client_script_name = tableData.getStringField("client_script_name", ""); - this->entries.insert(std::make_pair(entry.id, entry)); + entries.insert(std::make_pair(entry.id, entry)); tableData.nextRow(); } tableData.finalize(); } -const CDScriptComponent& CDScriptComponentTable::GetByID(unsigned int id) { - std::map::iterator it = this->entries.find(id); - if (it != this->entries.end()) { +const CDScriptComponent& CDScriptComponentTable::GetByID(uint32_t id) { + auto& entries = GetEntries(); + auto it = entries.find(id); + if (it != entries.end()) { return it->second; } diff --git a/dDatabase/Tables/CDScriptComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h similarity index 54% rename from dDatabase/Tables/CDScriptComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h index 77453939..96c1b5a5 100644 --- a/dDatabase/Tables/CDScriptComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h @@ -4,19 +4,15 @@ #include "CDTable.h" struct CDScriptComponent { - unsigned int id; //!< The component ID + uint32_t id; //!< The component ID std::string script_name; //!< The script name std::string client_script_name; //!< The client script name }; -class CDScriptComponentTable : public CDTable { -private: - std::map entries; - CDScriptComponent m_ToReturnWhenNoneFound; - +class CDScriptComponentTable : public CDTable> { public: - CDScriptComponentTable(); + void LoadValuesFromDatabase(); // Gets an entry by scriptID - const CDScriptComponent& GetByID(unsigned int id); + const CDScriptComponent& GetByID(uint32_t id); }; diff --git a/dDatabase/Tables/CDSkillBehaviorTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp similarity index 73% rename from dDatabase/Tables/CDSkillBehaviorTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp index c5df78ef..0df67884 100644 --- a/dDatabase/Tables/CDSkillBehaviorTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp @@ -1,12 +1,12 @@ #include "CDSkillBehaviorTable.h" -//#include "Logger.hpp" -//! Constructor -CDSkillBehaviorTable::CDSkillBehaviorTable(void) { - m_empty = CDSkillBehavior(); +namespace { + CDSkillBehavior m_empty = CDSkillBehavior(); +}; +void CDSkillBehaviorTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM SkillBehavior"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -16,8 +16,7 @@ CDSkillBehaviorTable::CDSkillBehaviorTable(void) { tableSize.finalize(); - // Reserve the size - //this->entries.reserve(size); + auto& entries = GetEntriesMutable(); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM SkillBehavior"); @@ -43,7 +42,7 @@ CDSkillBehaviorTable::CDSkillBehaviorTable(void) { UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); UNUSED(entry.cancelType = tableData.getIntField("cancelType", -1)); - this->entries.insert(std::make_pair(entry.skillID, entry)); + entries.insert(std::make_pair(entry.skillID, entry)); //this->entries.push_back(entry); tableData.nextRow(); } @@ -51,16 +50,10 @@ CDSkillBehaviorTable::CDSkillBehaviorTable(void) { tableData.finalize(); } -//! Queries the table with a custom "where" clause -std::vector CDSkillBehaviorTable::Query(std::function predicate) { - std::vector data; //So MSVC shuts up - return data; -} - -//! Gets an entry by ID -const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) { - std::map::iterator it = this->entries.find(skillID); - if (it != this->entries.end()) { +const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(uint32_t skillID) { + auto& entries = GetEntries(); + auto it = entries.find(skillID); + if (it != entries.end()) { return it->second; } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h new file mode 100644 index 00000000..19225d19 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h @@ -0,0 +1,35 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + +struct CDSkillBehavior { + uint32_t skillID; //!< The Skill ID of the skill + UNUSED(uint32_t locStatus); //!< ?? + uint32_t behaviorID; //!< The Behavior ID of the skill + uint32_t imaginationcost; //!< The imagination cost of the skill + uint32_t cooldowngroup; //!< The cooldown group ID of the skill + float cooldown; //!< The cooldown time of the skill + UNUSED(bool isNpcEditor); //!< ??? + UNUSED(uint32_t skillIcon); //!< The Skill Icon ID + UNUSED(std::string oomSkillID); //!< ??? + UNUSED(uint32_t oomBehaviorEffectID); //!< ??? + UNUSED(uint32_t castTypeDesc); //!< The cast type description(?) + UNUSED(uint32_t imBonusUI); //!< The imagination bonus of the skill + UNUSED(nint32_t lifeBonusUI); //!< The life bonus of the skill + UNUSED(uint32_t armorBonusUI); //!< The armor bonus of the skill + UNUSED(uint32_t damageUI); //!< ??? + UNUSED(bool hideIcon); //!< Whether or not to show the icon + UNUSED(bool localize); //!< ??? + UNUSED(std::string gate_version); //!< ??? + UNUSED(uint32_t cancelType); //!< The cancel type (?) +}; + +class CDSkillBehaviorTable : public CDTable> { +public: + void LoadValuesFromDatabase(); + + // Gets an entry by skillID + const CDSkillBehavior& GetSkillByID(uint32_t skillID); +}; + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h new file mode 100644 index 00000000..4e896dbb --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h @@ -0,0 +1,50 @@ +#pragma once + +#include "CDClientDatabase.h" +#include "CDClientManager.h" +#include "Singleton.h" +#include "DluAssert.h" + +#include +#include +#include +#include +#include + +// CPPLinq +#ifdef _WIN32 +#define NOMINMAX +// windows.h has min and max macros that breaks cpplinq +#endif +#include "cpplinq.hpp" + +// Used for legacy +#define UNUSED(x) + +// Enable this to skip some unused columns in some tables +#define UNUSED_COLUMN(v) + +// Use this to skip unused defaults for unused entries in some tables +#define UNUSED_ENTRY(v, x) + +#pragma warning (disable : 4244) //Disable double to float conversion warnings +// #pragma warning (disable : 4715) //Disable "not all control paths return a value" + +template +class CDTable : public Singleton
{ +public: + typedef Storage StorageType; + +protected: + virtual ~CDTable() = default; + + // If you need these for a specific table, override it such that there is a public variant. + [[nodiscard]] StorageType& GetEntriesMutable() const { + return CDClientManager::GetEntriesMutable
(); + } + + // If you need these for a specific table, override it such that there is a public variant. + [[nodiscard]] const StorageType& GetEntries() const { + return GetEntriesMutable(); + } +}; diff --git a/dDatabase/Tables/CDVendorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp similarity index 74% rename from dDatabase/Tables/CDVendorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp index 17989dfb..f639a7e3 100644 --- a/dDatabase/Tables/CDVendorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp @@ -1,10 +1,9 @@ #include "CDVendorComponentTable.h" -//! Constructor -CDVendorComponentTable::CDVendorComponentTable(void) { +void CDVendorComponentTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM VendorComponent"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -15,7 +14,8 @@ CDVendorComponentTable::CDVendorComponentTable(void) { tableSize.finalize(); // Reserve the size - this->entries.reserve(size); + auto& entries = GetEntriesMutable(); + entries.reserve(size); // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM VendorComponent"); @@ -27,7 +27,7 @@ CDVendorComponentTable::CDVendorComponentTable(void) { entry.refreshTimeSeconds = tableData.getFloatField("refreshTimeSeconds", -1.0f); entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); - this->entries.push_back(entry); + entries.push_back(entry); tableData.nextRow(); } @@ -37,15 +37,9 @@ CDVendorComponentTable::CDVendorComponentTable(void) { //! Queries the table with a custom "where" clause std::vector CDVendorComponentTable::Query(std::function predicate) { - std::vector data = cpplinq::from(this->entries) + std::vector data = cpplinq::from(GetEntries()) >> cpplinq::where(predicate) >> cpplinq::to_vector(); return data; } - -//! Gets all the entries in the table -std::vector CDVendorComponentTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDVendorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h similarity index 64% rename from dDatabase/Tables/CDVendorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h index f2666d7e..cbed2d13 100644 --- a/dDatabase/Tables/CDVendorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h @@ -4,22 +4,17 @@ #include "CDTable.h" struct CDVendorComponent { - unsigned int id; //!< The Component ID + uint32_t id; //!< The Component ID float buyScalar; //!< Buy Scalar (what does that mean?) float sellScalar; //!< Sell Scalar (what does that mean?) float refreshTimeSeconds; //!< The refresh time - unsigned int LootMatrixIndex; //!< LootMatrixIndex of the vendor's items + uint32_t LootMatrixIndex; //!< LootMatrixIndex of the vendor's items }; -class CDVendorComponentTable : public CDTable { -private: - std::vector entries; - +class CDVendorComponentTable : public CDTable> { public: - CDVendorComponentTable(); + void LoadValuesFromDatabase(); // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; }; diff --git a/dDatabase/Tables/CDZoneTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp similarity index 82% rename from dDatabase/Tables/CDZoneTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp index bafbf8fe..6aaeb854 100644 --- a/dDatabase/Tables/CDZoneTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp @@ -1,10 +1,9 @@ #include "CDZoneTableTable.h" -//! Constructor -CDZoneTableTable::CDZoneTableTable(void) { +void CDZoneTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); @@ -16,6 +15,7 @@ CDZoneTableTable::CDZoneTableTable(void) { // Now get the data auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable"); + auto& entries = GetEntriesMutable(); while (!tableData.eof()) { CDZoneTable entry; entry.zoneID = tableData.getIntField("zoneID", -1); @@ -32,7 +32,7 @@ CDZoneTableTable::CDZoneTableTable(void) { entry.smashableMaxDistance = tableData.getFloatField("smashableMaxDistance", -1.0f); UNUSED(entry.mixerProgram = tableData.getStringField("mixerProgram", "")); UNUSED(entry.clientPhysicsFramerate = tableData.getStringField("clientPhysicsFramerate", "")); - UNUSED(entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", "")); + entry.serverPhysicsFramerate = tableData.getStringField("serverPhysicsFramerate", ""); entry.zoneControlTemplate = tableData.getIntField("zoneControlTemplate", -1); entry.widthInChunks = tableData.getIntField("widthInChunks", -1); entry.heightInChunks = tableData.getIntField("heightInChunks", -1); @@ -41,12 +41,12 @@ CDZoneTableTable::CDZoneTableTable(void) { entry.fZoneWeight = tableData.getFloatField("fZoneWeight", -1.0f); UNUSED(entry.thumbnail = tableData.getStringField("thumbnail", "")); entry.PlayerLoseCoinsOnDeath = tableData.getIntField("PlayerLoseCoinsOnDeath", -1) == 1 ? true : false; - UNUSED(entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false); + entry.disableSaveLoc = tableData.getIntField("disableSaveLoc", -1) == 1 ? true : false; entry.teamRadius = tableData.getFloatField("teamRadius", -1.0f); UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); - UNUSED(entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false); + entry.mountsAllowed = tableData.getIntField("mountsAllowed", -1) == 1 ? true : false; - this->m_Entries.insert(std::make_pair(entry.zoneID, entry)); + entries.insert(std::make_pair(entry.zoneID, entry)); tableData.nextRow(); } @@ -54,7 +54,8 @@ CDZoneTableTable::CDZoneTableTable(void) { } //! Queries the table with a zoneID to find. -const CDZoneTable* CDZoneTableTable::Query(unsigned int zoneID) { +const CDZoneTable* CDZoneTableTable::Query(uint32_t zoneID) { + auto& m_Entries = GetEntries(); const auto& iter = m_Entries.find(zoneID); if (iter != m_Entries.end()) { diff --git a/dDatabase/Tables/CDZoneTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h similarity index 53% rename from dDatabase/Tables/CDZoneTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h index f844fd25..b1e8b1ba 100644 --- a/dDatabase/Tables/CDZoneTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h @@ -4,42 +4,39 @@ #include "CDTable.h" struct CDZoneTable { - unsigned int zoneID; //!< The Zone ID of the object - unsigned int locStatus; //!< The Locale Status(?) + uint32_t zoneID; //!< The Zone ID of the object + uint32_t locStatus; //!< The Locale Status(?) std::string zoneName; //!< The name of the zone - unsigned int scriptID; //!< The Script ID of the zone (ScriptsTable) + uint32_t scriptID; //!< The Script ID of the zone (ScriptsTable) float ghostdistance_min; //!< The minimum ghosting distance float ghostdistance; //!< The ghosting distance - unsigned int population_soft_cap; //!< The "soft cap" on the world population - unsigned int population_hard_cap; //!< The "hard cap" on the world population + uint32_t population_soft_cap; //!< The "soft cap" on the world population + uint32_t population_hard_cap; //!< The "hard cap" on the world population UNUSED(std::string DisplayDescription); //!< The display description of the world UNUSED(std::string mapFolder); //!< ??? float smashableMinDistance; //!< The minimum smashable distance? float smashableMaxDistance; //!< The maximum smashable distance? UNUSED(std::string mixerProgram); //!< ??? UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate - UNUSED(std::string serverPhysicsFramerate); //!< The server physics framerate - unsigned int zoneControlTemplate; //!< The Zone Control template - unsigned int widthInChunks; //!< The width of the world in chunks - unsigned int heightInChunks; //!< The height of the world in chunks + std::string serverPhysicsFramerate; //!< The server physics framerate + uint32_t zoneControlTemplate; //!< The Zone Control template + uint32_t widthInChunks; //!< The width of the world in chunks + uint32_t heightInChunks; //!< The height of the world in chunks bool petsAllowed; //!< Whether or not pets are allowed in the world bool localize; //!< Whether or not the world should be localized float fZoneWeight; //!< ??? UNUSED(std::string thumbnail); //!< The thumbnail of the world bool PlayerLoseCoinsOnDeath; //!< Whether or not the user loses coins on death - UNUSED(bool disableSaveLoc); //!< Disables the saving location? + bool disableSaveLoc; //!< Disables the saving location? float teamRadius; //!< ??? UNUSED(std::string gate_version); //!< The gate version - UNUSED(bool mountsAllowed); //!< Whether or not mounts are allowed + bool mountsAllowed; //!< Whether or not mounts are allowed }; -class CDZoneTableTable : public CDTable { -private: - std::map m_Entries; - +class CDZoneTableTable : public CDTable> { public: - CDZoneTableTable(); + void LoadValuesFromDatabase(); // Queries the table with a zoneID to find. - const CDZoneTable* Query(unsigned int zoneID); + const CDZoneTable* Query(uint32_t zoneID); }; diff --git a/dDatabase/Tables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt similarity index 89% rename from dDatabase/Tables/CMakeLists.txt rename to dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index fafe8d58..fd47b5db 100644 --- a/dDatabase/Tables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -1,4 +1,4 @@ -set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" +set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDActivityRewardsTable.cpp" "CDAnimationsTable.cpp" "CDBehaviorParameterTable.cpp" @@ -24,6 +24,7 @@ set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" "CDMovementAIComponentTable.cpp" "CDObjectSkillsTable.cpp" "CDObjectsTable.cpp" + "CDPetComponentTable.cpp" "CDPackageComponentTable.cpp" "CDPhysicsComponentTable.cpp" "CDPropertyEntranceComponentTable.cpp" @@ -32,6 +33,7 @@ set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" "CDRailActivatorComponent.cpp" "CDRarityTableTable.cpp" "CDRebuildComponentTable.cpp" + "CDRewardCodesTable.cpp" "CDRewardsTable.cpp" "CDScriptComponentTable.cpp" "CDSkillBehaviorTable.cpp" diff --git a/dDatabase/CDClientDatabase/CMakeLists.txt b/dDatabase/CDClientDatabase/CMakeLists.txt new file mode 100644 index 00000000..2645c215 --- /dev/null +++ b/dDatabase/CDClientDatabase/CMakeLists.txt @@ -0,0 +1,12 @@ +set(DDATABASE_CDCLIENTDATABASE_SOURCES + "CDClientDatabase.cpp" + "CDClientManager.cpp" +) + +add_subdirectory(CDClientTables) + +foreach(file ${DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES}) + set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} "CDClientTables/${file}") +endforeach() + +set(DDATABASE_CDCLIENTDATABASE_SOURCES ${DDATABASE_CDCLIENTDATABASE_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/CDClientManager.cpp b/dDatabase/CDClientManager.cpp deleted file mode 100644 index 9df6ff31..00000000 --- a/dDatabase/CDClientManager.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "CDClientManager.h" -#include "CDActivityRewardsTable.h" -#include "CDAnimationsTable.h" -#include "CDBehaviorParameterTable.h" -#include "CDBehaviorTemplateTable.h" -#include "CDComponentsRegistryTable.h" -#include "CDCurrencyTableTable.h" -#include "CDDestructibleComponentTable.h" -#include "CDEmoteTable.h" -#include "CDInventoryComponentTable.h" -#include "CDItemComponentTable.h" -#include "CDItemSetsTable.h" -#include "CDItemSetSkillsTable.h" -#include "CDLevelProgressionLookupTable.h" -#include "CDLootMatrixTable.h" -#include "CDLootTableTable.h" -#include "CDMissionNPCComponentTable.h" -#include "CDMissionTasksTable.h" -#include "CDMissionsTable.h" -#include "CDObjectSkillsTable.h" -#include "CDObjectsTable.h" -#include "CDPhysicsComponentTable.h" -#include "CDRebuildComponentTable.h" -#include "CDScriptComponentTable.h" -#include "CDSkillBehaviorTable.h" -#include "CDZoneTableTable.h" -#include "CDVendorComponentTable.h" -#include "CDActivitiesTable.h" -#include "CDPackageComponentTable.h" -#include "CDProximityMonitorComponentTable.h" -#include "CDMovementAIComponentTable.h" -#include "CDBrickIDTableTable.h" -#include "CDRarityTableTable.h" -#include "CDMissionEmailTable.h" -#include "CDRewardsTable.h" -#include "CDPropertyEntranceComponentTable.h" -#include "CDPropertyTemplateTable.h" -#include "CDFeatureGatingTable.h" -#include "CDRailActivatorComponent.h" - -CDClientManager::CDClientManager() { - CDActivityRewardsTable::Instance(); - CDAnimationsTable::Instance(); - CDBehaviorParameterTable::Instance(); - CDBehaviorTemplateTable::Instance(); - CDComponentsRegistryTable::Instance(); - CDCurrencyTableTable::Instance(); - CDDestructibleComponentTable::Instance(); - CDEmoteTableTable::Instance(); - CDInventoryComponentTable::Instance(); - CDItemComponentTable::Instance(); - CDItemSetsTable::Instance(); - CDItemSetSkillsTable::Instance(); - CDLevelProgressionLookupTable::Instance(); - CDLootMatrixTable::Instance(); - CDLootTableTable::Instance(); - CDMissionNPCComponentTable::Instance(); - CDMissionTasksTable::Instance(); - CDMissionsTable::Instance(); - CDObjectSkillsTable::Instance(); - CDObjectsTable::Instance(); - CDPhysicsComponentTable::Instance(); - CDRebuildComponentTable::Instance(); - CDScriptComponentTable::Instance(); - CDSkillBehaviorTable::Instance(); - CDZoneTableTable::Instance(); - CDVendorComponentTable::Instance(); - CDActivitiesTable::Instance(); - CDPackageComponentTable::Instance(); - CDProximityMonitorComponentTable::Instance(); - CDMovementAIComponentTable::Instance(); - CDBrickIDTableTable::Instance(); - CDRarityTableTable::Instance(); - CDMissionEmailTable::Instance(); - CDRewardsTable::Instance(); - CDPropertyEntranceComponentTable::Instance(); - CDPropertyTemplateTable::Instance(); - CDFeatureGatingTable::Instance(); - CDRailActivatorComponentTable::Instance(); -} diff --git a/dDatabase/CDClientManager.h b/dDatabase/CDClientManager.h deleted file mode 100644 index 74069ff4..00000000 --- a/dDatabase/CDClientManager.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "CDTable.h" - -#include "Singleton.h" - -#define UNUSED_TABLE(v) - -/** - * Initialize the CDClient tables so they are all loaded into memory. - */ -class CDClientManager : public Singleton { -public: - CDClientManager(); - - /** - * Fetch a table from CDClient - * - * @tparam Table type to fetch - * @return A pointer to the requested table. - */ - template - T* GetTable() { - return &T::Instance(); - } -}; diff --git a/dDatabase/CMakeLists.txt b/dDatabase/CMakeLists.txt index 9e55fbe2..f0fe54b4 100644 --- a/dDatabase/CMakeLists.txt +++ b/dDatabase/CMakeLists.txt @@ -1,13 +1,20 @@ -set(DDATABASE_SOURCES "CDClientDatabase.cpp" - "CDClientManager.cpp" - "Database.cpp" - "MigrationRunner.cpp") +set(DDATABASE_SOURCES) -add_subdirectory(Tables) +add_subdirectory(CDClientDatabase) -foreach(file ${DDATABASE_TABLES_SOURCES}) - set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "Tables/${file}") +foreach(file ${DDATABASE_CDCLIENTDATABASE_SOURCES}) + set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "CDClientDatabase/${file}") +endforeach() + +add_subdirectory(GameDatabase) + +foreach(file ${DDATABASE_GAMEDATABASE_SOURCES}) + set(DDATABASE_SOURCES ${DDATABASE_SOURCES} "GameDatabase/${file}") endforeach() add_library(dDatabase STATIC ${DDATABASE_SOURCES}) target_link_libraries(dDatabase sqlite3 mariadbConnCpp) + +if (${CDCLIENT_CACHE_ALL}) + add_compile_definitions(dDatabase CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL}) +endif() diff --git a/dDatabase/Database.cpp b/dDatabase/Database.cpp deleted file mode 100644 index 1ce6966f..00000000 --- a/dDatabase/Database.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Database.h" -#include "Game.h" -#include "dConfig.h" -#include "dLogger.h" -using namespace std; - -#pragma warning (disable:4251) //Disables SQL warnings - -sql::Driver* Database::driver; -sql::Connection* Database::con; -sql::Properties Database::props; -std::string Database::database; - -void Database::Connect(const string& host, const string& database, const string& username, const string& password) { - - //To bypass debug issues: - const char* szDatabase = database.c_str(); - const char* szUsername = username.c_str(); - const char* szPassword = password.c_str(); - - driver = sql::mariadb::get_driver_instance(); - - sql::Properties properties; - // The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where - // 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the - // presence of a / - // 2) even avoiding that, the connector still assumes you're connecting with a tcp socket - // So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost, - // which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate - // property manually (which the URL parsing fails to do) - const std::string UNIX_PROTO = "unix://"; - const std::string PIPE_PROTO = "pipe://"; - if (host.find(UNIX_PROTO) == 0) { - properties["hostName"] = "unix://localhost"; - properties["localSocket"] = host.substr(UNIX_PROTO.length()).c_str(); - } else if (host.find(PIPE_PROTO) == 0) { - properties["hostName"] = "pipe://localhost"; - properties["pipe"] = host.substr(PIPE_PROTO.length()).c_str(); - } else { - properties["hostName"] = host.c_str(); - } - properties["user"] = szUsername; - properties["password"] = szPassword; - properties["autoReconnect"] = "true"; - - Database::props = properties; - Database::database = database; - - Database::Connect(); -} - -void Database::Connect() { - // `connect(const Properties& props)` segfaults in windows debug, but - // `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly - if (Database::props.find("localSocket") != Database::props.end() || Database::props.find("pipe") != Database::props.end()) { - con = driver->connect(Database::props); - } else { - con = driver->connect(Database::props["hostName"].c_str(), Database::props["user"].c_str(), Database::props["password"].c_str()); - } - con->setSchema(Database::database.c_str()); -} - -void Database::Destroy(std::string source, bool log) { - if (!con) return; - - if (log) { - if (source != "") Game::logger->Log("Database", "Destroying MySQL connection from %s!", source.c_str()); - else Game::logger->Log("Database", "Destroying MySQL connection!"); - } - - con->close(); - delete con; -} //Destroy - -sql::Statement* Database::CreateStmt() { - sql::Statement* toReturn = con->createStatement(); - return toReturn; -} //CreateStmt - -sql::PreparedStatement* Database::CreatePreppedStmt(const std::string& query) { - const char* test = query.c_str(); - size_t size = query.length(); - sql::SQLString str(test, size); - - if (!con) { - Connect(); - Game::logger->Log("Database", "Trying to reconnect to MySQL"); - } - - if (!con->isValid() || con->isClosed()) { - delete con; - - con = nullptr; - - Connect(); - Game::logger->Log("Database", "Trying to reconnect to MySQL from invalid or closed connection"); - } - - auto* stmt = con->prepareStatement(str); - - return stmt; -} //CreatePreppedStmt - -void Database::Commit() { - Database::con->commit(); -} - -bool Database::GetAutoCommit() { - // TODO This should not just access a pointer. A future PR should update this - // to check for null and throw an error if the connection is not valid. - return con->getAutoCommit(); -} - -void Database::SetAutoCommit(bool value) { - // TODO This should not just access a pointer. A future PR should update this - // to check for null and throw an error if the connection is not valid. - Database::con->setAutoCommit(value); -} diff --git a/dDatabase/Database.h b/dDatabase/Database.h deleted file mode 100644 index f4d13da3..00000000 --- a/dDatabase/Database.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include - -class MySqlException : public std::runtime_error { -public: - MySqlException() : std::runtime_error("MySQL error!") {} - MySqlException(const std::string& msg) : std::runtime_error(msg.c_str()) {} -}; - -class Database { -private: - static sql::Driver* driver; - static sql::Connection* con; - static sql::Properties props; - static std::string database; -public: - static void Connect(const std::string& host, const std::string& database, const std::string& username, const std::string& password); - static void Connect(); - static void Destroy(std::string source = "", bool log = true); - - static sql::Statement* CreateStmt(); - static sql::PreparedStatement* CreatePreppedStmt(const std::string& query); - static void Commit(); - static bool GetAutoCommit(); - static void SetAutoCommit(bool value); - - static std::string GetDatabase() { return database; } - static sql::Properties GetProperties() { return props; } -}; diff --git a/dDatabase/GameDatabase/CMakeLists.txt b/dDatabase/GameDatabase/CMakeLists.txt new file mode 100644 index 00000000..c32007bb --- /dev/null +++ b/dDatabase/GameDatabase/CMakeLists.txt @@ -0,0 +1,12 @@ +set(DDATABASE_GAMEDATABASE_SOURCES + "Database.cpp" + "MigrationRunner.cpp" +) + +add_subdirectory(MySQL) + +foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES}) + set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}") +endforeach() + +set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/GameDatabase/Database.cpp b/dDatabase/GameDatabase/Database.cpp new file mode 100644 index 00000000..cb4f989a --- /dev/null +++ b/dDatabase/GameDatabase/Database.cpp @@ -0,0 +1,40 @@ +#include "Database.h" +#include "Game.h" +#include "dConfig.h" +#include "Logger.h" +#include "MySQLDatabase.h" +#include "DluAssert.h" + +#pragma warning (disable:4251) //Disables SQL warnings + +namespace { + GameDatabase* database = nullptr; +} + +void Database::Connect() { + if (database) { + LOG("Tried to connect to database when it's already connected!"); + return; + } + + database = new MySQLDatabase(); + database->Connect(); +} + +GameDatabase* Database::Get() { + if (!database) { + LOG("Tried to get database when it's not connected!"); + Connect(); + } + return database; +} + +void Database::Destroy(std::string source) { + if (database) { + database->Destroy(source); + delete database; + database = nullptr; + } else { + LOG("Trying to destroy database when it's not connected!"); + } +} diff --git a/dDatabase/GameDatabase/Database.h b/dDatabase/GameDatabase/Database.h new file mode 100644 index 00000000..3eb292d1 --- /dev/null +++ b/dDatabase/GameDatabase/Database.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include "GameDatabase.h" + +namespace Database { + void Connect(); + GameDatabase* Get(); + void Destroy(std::string source = ""); +}; diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h new file mode 100644 index 00000000..bcd8550b --- /dev/null +++ b/dDatabase/GameDatabase/GameDatabase.h @@ -0,0 +1,57 @@ +#ifndef __GAMEDATABASE__H__ +#define __GAMEDATABASE__H__ + +#include + +#include "ILeaderboard.h" +#include "IPlayerCheatDetections.h" +#include "ICommandLog.h" +#include "IMail.h" +#include "IObjectIdTracker.h" +#include "IPlayKeys.h" +#include "IServers.h" +#include "IBugReports.h" +#include "IPropertyContents.h" +#include "IProperty.h" +#include "IPetNames.h" +#include "ICharXml.h" +#include "IMigrationHistory.h" +#include "IUgc.h" +#include "IFriends.h" +#include "ICharInfo.h" +#include "IAccounts.h" +#include "IActivityLog.h" +#include "IIgnoreList.h" +#include "IAccountsRewardCodes.h" + +namespace sql { + class Statement; + class PreparedStatement; +}; + +#ifdef _DEBUG +# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (sql::SQLException& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0) +#else +# define DLU_SQL_TRY_CATCH_RETHROW(x) x +#endif // _DEBUG + +class GameDatabase : + public IPlayKeys, public ILeaderboard, public IObjectIdTracker, public IServers, + 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: + virtual ~GameDatabase() = default; + // TODO: These should be made private. + virtual void Connect() = 0; + virtual void Destroy(std::string source = "") = 0; + virtual void ExecuteCustomQuery(const std::string_view query) = 0; + virtual sql::PreparedStatement* CreatePreppedStmt(const std::string& query) = 0; + virtual void Commit() = 0; + virtual bool GetAutoCommit() = 0; + virtual void SetAutoCommit(bool value) = 0; + virtual void DeleteCharacter(const uint32_t characterId) = 0; +}; + +#endif //!__GAMEDATABASE__H__ diff --git a/dDatabase/GameDatabase/ITables/IAccounts.h b/dDatabase/GameDatabase/ITables/IAccounts.h new file mode 100644 index 00000000..1b1f85a7 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IAccounts.h @@ -0,0 +1,37 @@ +#ifndef __IACCOUNTS__H__ +#define __IACCOUNTS__H__ + +#include +#include +#include + +enum class eGameMasterLevel : uint8_t; + +class IAccounts { +public: + struct Info { + std::string bcryptPassword; + uint32_t id{}; + uint32_t playKeyId{}; + bool banned{}; + bool locked{}; + eGameMasterLevel maxGmLevel{}; + }; + + // Get the account info for the given username. + virtual std::optional GetAccountInfo(const std::string_view username) = 0; + + // Update the account's unmute time. + virtual void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) = 0; + + // Update the account's ban status. + virtual void UpdateAccountBan(const uint32_t accountId, const bool banned) = 0; + + // Update the account's password. + virtual void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) = 0; + + // Add a new account to the database. + virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0; +}; + +#endif //!__IACCOUNTS__H__ diff --git a/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h b/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h new file mode 100644 index 00000000..2fcb9d2a --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h @@ -0,0 +1,13 @@ +#ifndef __IACCOUNTSREWARDCODES__H__ +#define __IACCOUNTSREWARDCODES__H__ + +#include +#include + +class IAccountsRewardCodes { +public: + virtual void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) = 0; + virtual std::vector GetRewardCodesByAccountID(const uint32_t account_id) = 0; +}; + +#endif //!__IACCOUNTSREWARDCODES__H__ diff --git a/dDatabase/GameDatabase/ITables/IActivityLog.h b/dDatabase/GameDatabase/ITables/IActivityLog.h new file mode 100644 index 00000000..a67b61a4 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IActivityLog.h @@ -0,0 +1,19 @@ +#ifndef __IACTIVITYLOG__H__ +#define __IACTIVITYLOG__H__ + +#include + +#include "dCommonVars.h" + +enum class eActivityType : uint32_t { + PlayerLoggedIn, + PlayerLoggedOut, +}; + +class IActivityLog { +public: + // Update the activity log for the given account. + virtual void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) = 0; +}; + +#endif //!__IACTIVITYLOG__H__ diff --git a/dDatabase/GameDatabase/ITables/IBugReports.h b/dDatabase/GameDatabase/ITables/IBugReports.h new file mode 100644 index 00000000..29a6180f --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IBugReports.h @@ -0,0 +1,20 @@ +#ifndef __IBUGREPORTS__H__ +#define __IBUGREPORTS__H__ + +#include +#include + +class IBugReports { +public: + struct Info { + std::string body; + std::string clientVersion; + std::string otherPlayer; + std::string selection; + uint32_t characterId{}; + }; + + // Add a new bug report to the database. + virtual void InsertNewBugReport(const Info& info) = 0; +}; +#endif //!__IBUGREPORTS__H__ diff --git a/dDatabase/GameDatabase/ITables/ICharInfo.h b/dDatabase/GameDatabase/ITables/ICharInfo.h new file mode 100644 index 00000000..416dad2c --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICharInfo.h @@ -0,0 +1,49 @@ +#ifndef __ICHARINFO__H__ +#define __ICHARINFO__H__ + +#include +#include +#include +#include +#include + +#include "ePermissionMap.h" + +class ICharInfo { +public: + struct Info { + std::string name; + std::string pendingName; + uint32_t id{}; + uint32_t accountId{}; + bool needsRename{}; + LWOCLONEID cloneId{}; + ePermissionMap permissionMap{}; + }; + + // Get the approved names of all characters. + virtual std::vector GetApprovedCharacterNames() = 0; + + // Get the character info for the given character id. + virtual std::optional GetCharacterInfo(const uint32_t charId) = 0; + + // Get the character info for the given character name. + virtual std::optional GetCharacterInfo(const std::string_view name) = 0; + + // Get the character ids for the given account. + virtual std::vector GetAccountCharacterIds(const uint32_t accountId) = 0; + + // Insert a new character into the database. + virtual void InsertNewCharacter(const ICharInfo::Info info) = 0; + + // Set the name of the given character. + virtual void SetCharacterName(const uint32_t characterId, const std::string_view name) = 0; + + // Set the pending name of the given character. + virtual void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) = 0; + + // Updates the given character ids last login to be right now. + virtual void UpdateLastLoggedInCharacter(const uint32_t characterId) = 0; +}; + +#endif //!__ICHARINFO__H__ diff --git a/dDatabase/GameDatabase/ITables/ICharXml.h b/dDatabase/GameDatabase/ITables/ICharXml.h new file mode 100644 index 00000000..c7ada075 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICharXml.h @@ -0,0 +1,20 @@ +#ifndef __ICHARXML__H__ +#define __ICHARXML__H__ + +#include +#include +#include + +class ICharXml { +public: + // Get the character xml for the given character id. + virtual std::string GetCharacterXml(const uint32_t charId) = 0; + + // Update the character xml for the given character id. + virtual void UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) = 0; + + // Insert the character xml for the given character id. + virtual void InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) = 0; +}; + +#endif //!__ICHARXML__H__ diff --git a/dDatabase/GameDatabase/ITables/ICommandLog.h b/dDatabase/GameDatabase/ITables/ICommandLog.h new file mode 100644 index 00000000..63595360 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ICommandLog.h @@ -0,0 +1,14 @@ +#ifndef __ICOMMANDLOG__H__ +#define __ICOMMANDLOG__H__ + +#include +#include + +class ICommandLog { +public: + + // Insert a new slash command log entry. + virtual void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) = 0; +}; + +#endif //!__ICOMMANDLOG__H__ diff --git a/dDatabase/GameDatabase/ITables/IFriends.h b/dDatabase/GameDatabase/ITables/IFriends.h new file mode 100644 index 00000000..6f96f441 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IFriends.h @@ -0,0 +1,32 @@ +#ifndef __IFRIENDS__H__ +#define __IFRIENDS__H__ + +#include +#include +#include + +class IFriends { +public: + struct BestFriendStatus { + uint32_t playerCharacterId{}; + uint32_t friendCharacterId{}; + uint32_t bestFriendStatus{}; + }; + + // Get the friends list for the given character id. + virtual std::vector GetFriendsList(const uint32_t charId) = 0; + + // Get the best friend status for the given player and friend character ids. + virtual std::optional GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; + + // Set the best friend status for the given player and friend character ids. + virtual void SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) = 0; + + // Add a friend to the given character id. + virtual void AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; + + // Remove a friend from the given character id. + virtual void RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) = 0; +}; + +#endif //!__IFRIENDS__H__ diff --git a/dDatabase/GameDatabase/ITables/IIgnoreList.h b/dDatabase/GameDatabase/ITables/IIgnoreList.h new file mode 100644 index 00000000..cf831203 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IIgnoreList.h @@ -0,0 +1,20 @@ +#ifndef __IIGNORELIST__H__ +#define __IIGNORELIST__H__ + +#include +#include +#include + +class IIgnoreList { +public: + struct Info { + std::string name; + uint32_t id; + }; + + virtual std::vector GetIgnoreList(const uint32_t playerId) = 0; + virtual void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0; + virtual void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) = 0; +}; + +#endif //!__IIGNORELIST__H__ diff --git a/dDatabase/GameDatabase/ITables/ILeaderboard.h b/dDatabase/GameDatabase/ITables/ILeaderboard.h new file mode 100644 index 00000000..84d44eb2 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/ILeaderboard.h @@ -0,0 +1,14 @@ +#ifndef __ILEADERBOARD__H__ +#define __ILEADERBOARD__H__ + +#include +#include + +class ILeaderboard { +public: + + // Get the donation total for the given activity id. + virtual std::optional GetDonationTotal(const uint32_t activityId) = 0; +}; + +#endif //!__ILEADERBOARD__H__ diff --git a/dDatabase/GameDatabase/ITables/IMail.h b/dDatabase/GameDatabase/ITables/IMail.h new file mode 100644 index 00000000..7fbc8230 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IMail.h @@ -0,0 +1,54 @@ +#ifndef __IMAIL__H__ +#define __IMAIL__H__ + +#include +#include +#include + +#include "dCommonVars.h" +#include "NiQuaternion.h" +#include "NiPoint3.h" + +class IMail { +public: + struct MailInfo { + std::string senderUsername; + std::string recipient; + std::string subject; + std::string body; + uint64_t id{}; + uint32_t senderId{}; + uint32_t receiverId{}; + uint64_t timeSent{}; + bool wasRead{}; + struct { + LWOOBJID itemID{}; + int32_t itemCount{}; + LOT itemLOT{}; + LWOOBJID itemSubkey{}; + }; + }; + + // Insert a new mail into the database. + virtual void InsertNewMail(const MailInfo& mail) = 0; + + // Get the mail for the given character id. + virtual std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) = 0; + + // Get the mail for the given mail id. + virtual std::optional GetMail(const uint64_t mailId) = 0; + + // Get the number of unread mail for the given character id. + virtual uint32_t GetUnreadMailCount(const uint32_t characterId) = 0; + + // Mark the given mail as read. + virtual void MarkMailRead(const uint64_t mailId) = 0; + + // Claim the item from the given mail. + virtual void ClaimMailItem(const uint64_t mailId) = 0; + + // Delete the given mail. + virtual void DeleteMail(const uint64_t mailId) = 0; +}; + +#endif //!__IMAIL__H__ diff --git a/dDatabase/GameDatabase/ITables/IMigrationHistory.h b/dDatabase/GameDatabase/ITables/IMigrationHistory.h new file mode 100644 index 00000000..21f27b4a --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IMigrationHistory.h @@ -0,0 +1,17 @@ +#ifndef __IMIGRATIONHISTORY__H__ +#define __IMIGRATIONHISTORY__H__ + +#include + +class IMigrationHistory { +public: + // Create the migration history table. + virtual void CreateMigrationHistoryTable() = 0; + + // Check if the given migration has been run. + virtual bool IsMigrationRun(const std::string_view str) = 0; + + // Insert the given migration into the migration history table. + virtual void InsertMigration(const std::string_view str) = 0; +}; +#endif //!__IMIGRATIONHISTORY__H__ diff --git a/dDatabase/GameDatabase/ITables/IObjectIdTracker.h b/dDatabase/GameDatabase/ITables/IObjectIdTracker.h new file mode 100644 index 00000000..cbe34b6d --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IObjectIdTracker.h @@ -0,0 +1,19 @@ +#ifndef __IOBJECTIDTRACKER__H__ +#define __IOBJECTIDTRACKER__H__ + +#include +#include + +class IObjectIdTracker { +public: + // Get the current persistent id. + virtual std::optional GetCurrentPersistentId() = 0; + + // Insert the default persistent id. + virtual void InsertDefaultPersistentId() = 0; + + // Update the persistent id. + virtual void UpdatePersistentId(const uint32_t newId) = 0; +}; + +#endif //!__IOBJECTIDTRACKER__H__ diff --git a/dDatabase/GameDatabase/ITables/IPetNames.h b/dDatabase/GameDatabase/ITables/IPetNames.h new file mode 100644 index 00000000..e82f4905 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPetNames.h @@ -0,0 +1,21 @@ +#ifndef __IPETNAMES__H__ +#define __IPETNAMES__H__ + +#include +#include + +class IPetNames { +public: + struct Info { + std::string petName; + int32_t approvalStatus{}; + }; + + // Set the pet name moderation status for the given pet id. + virtual void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) = 0; + + // Get pet info for the given pet id. + virtual std::optional GetPetNameInfo(const LWOOBJID& petId) = 0; +}; + +#endif //!__IPETNAMES__H__ diff --git a/dDatabase/GameDatabase/ITables/IPlayKeys.h b/dDatabase/GameDatabase/ITables/IPlayKeys.h new file mode 100644 index 00000000..0a1db389 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPlayKeys.h @@ -0,0 +1,15 @@ +#ifndef __IPLAYKEYS__H__ +#define __IPLAYKEYS__H__ + +#include +#include + +class IPlayKeys { +public: + // Get the playkey id for the given playkey. + // Optional of bool may seem pointless, however the optional indicates if the playkey exists + // and the bool indicates if the playkey is active. + virtual std::optional IsPlaykeyActive(const int32_t playkeyId) = 0; +}; + +#endif //!__IPLAYKEYS__H__ diff --git a/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h b/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h new file mode 100644 index 00000000..939f5118 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPlayerCheatDetections.h @@ -0,0 +1,20 @@ +#ifndef __IPLAYERCHEATDETECTIONS__H__ +#define __IPLAYERCHEATDETECTIONS__H__ + +#include +#include + +class IPlayerCheatDetections { +public: + struct Info { + std::optional userId = std::nullopt; + std::string username; + std::string systemAddress; + std::string extraMessage; + }; + + // Insert a new cheat detection. + virtual void InsertCheatDetection(const IPlayerCheatDetections::Info& info) = 0; +}; + +#endif //!__IPLAYERCHEATDETECTIONS__H__ diff --git a/dDatabase/GameDatabase/ITables/IProperty.h b/dDatabase/GameDatabase/ITables/IProperty.h new file mode 100644 index 00000000..54994b51 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IProperty.h @@ -0,0 +1,38 @@ +#ifndef __IPROPERTY__H__ +#define __IPROPERTY__H__ + +#include +#include + +class IProperty { +public: + struct Info { + std::string name; + std::string description; + std::string rejectionReason; + LWOOBJID id{}; + uint32_t ownerId{}; + LWOCLONEID cloneId{}; + int32_t privacyOption{}; + uint32_t modApproved{}; + uint32_t lastUpdatedTime{}; + uint32_t claimedTime{}; + uint32_t reputation{}; + }; + + // Get the property info for the given property id. + virtual std::optional GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0; + + // Update the property moderation info for the given property id. + virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0; + + // Update the property details for the given property id. + virtual void UpdatePropertyDetails(const IProperty::Info& info) = 0; + + // Update the property performance cost for the given property id. + virtual void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) = 0; + + // Insert a new property into the database. + virtual void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) = 0; +}; +#endif //!__IPROPERTY__H__ diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h new file mode 100644 index 00000000..c862ca94 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -0,0 +1,40 @@ +#ifndef __IPROPERTIESCONTENTS__H__ +#define __IPROPERTIESCONTENTS__H__ + +#include +#include + +class IPropertyContents { +public: + struct Model { + inline bool operator==(const LWOOBJID& other) const noexcept { + return id == other; + } + + NiPoint3 position; + NiQuaternion rotation; + LWOOBJID id{}; + LOT lot{}; + uint32_t ugcId{}; + }; + + // Inserts a new UGC model into the database. + virtual void InsertNewUgcModel( + std::istringstream& sd0Data, + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) = 0; + + // Get the property models for the given property id. + virtual std::vector GetPropertyModels(const LWOOBJID& propertyId) = 0; + + // Insert a new property model into the database. + 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 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; +}; +#endif //!__IPROPERTIESCONTENTS__H__ diff --git a/dDatabase/GameDatabase/ITables/IServers.h b/dDatabase/GameDatabase/ITables/IServers.h new file mode 100644 index 00000000..ee74bbb8 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IServers.h @@ -0,0 +1,21 @@ +#ifndef __ISERVERS__H__ +#define __ISERVERS__H__ + +#include +#include + +class IServers { +public: + struct MasterInfo { + std::string ip; + uint32_t port{}; + }; + + // Set the master server ip and port. + virtual void SetMasterIp(const std::string_view ip, const uint32_t port) = 0; + + // Get the master server info. + virtual std::optional GetMasterInfo() = 0; +}; + +#endif //!__ISERVERS__H__ diff --git a/dDatabase/GameDatabase/ITables/IUgc.h b/dDatabase/GameDatabase/ITables/IUgc.h new file mode 100644 index 00000000..024636ac --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IUgc.h @@ -0,0 +1,32 @@ +#ifndef __IUGC__H__ +#define __IUGC__H__ + +#include +#include +#include +#include +#include + +class IUgc { +public: + struct Model { + std::stringstream lxfmlData; + LWOOBJID id{}; + }; + + // Gets all UGC models for the given property id. + virtual std::vector GetUgcModels(const LWOOBJID& propertyId) = 0; + + // Gets all Ugcs models. + virtual std::vector GetAllUgcModels() = 0; + + // Removes ugc models that are not referenced by any property. + virtual void RemoveUnreferencedUgcModels() = 0; + + // Deletes the ugc model for the given model id. + virtual void DeleteUgcModelData(const LWOOBJID& modelId) = 0; + + // Inserts a new UGC model into the database. + virtual void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) = 0; +}; +#endif //!__IUGC__H__ diff --git a/dDatabase/MigrationRunner.cpp b/dDatabase/GameDatabase/MigrationRunner.cpp similarity index 58% rename from dDatabase/MigrationRunner.cpp rename to dDatabase/GameDatabase/MigrationRunner.cpp index 5e70c401..8034a3e2 100644 --- a/dDatabase/MigrationRunner.cpp +++ b/dDatabase/GameDatabase/MigrationRunner.cpp @@ -5,10 +5,10 @@ #include "Database.h" #include "Game.h" #include "GeneralUtils.h" -#include "dLogger.h" +#include "Logger.h" #include "BinaryPathFinder.h" -#include +#include Migration LoadMigration(std::string path) { Migration migration{}; @@ -32,9 +32,7 @@ Migration LoadMigration(std::string path) { } void MigrationRunner::RunMigrations() { - auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); - stmt->execute(); - delete stmt; + Database::Get()->CreateMigrationHistoryTable(); sql::SQLString finalSQL = ""; bool runSd0Migrations = false; @@ -45,41 +43,31 @@ void MigrationRunner::RunMigrations() { continue; } - stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); - stmt->setString(1, migration.name.c_str()); - auto* res = stmt->executeQuery(); - bool doExit = res->next(); - delete res; - delete stmt; - if (doExit) continue; + if (Database::Get()->IsMigrationRun(migration.name)) continue; - Game::logger->Log("MigrationRunner", "Running migration: %s", migration.name.c_str()); + LOG("Running migration: %s", migration.name.c_str()); if (migration.name == "dlu/5_brick_model_sd0.sql") { runSd0Migrations = true; } else { finalSQL.append(migration.data.c_str()); } - stmt = Database::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); - stmt->setString(1, migration.name.c_str()); - stmt->execute(); - delete stmt; + Database::Get()->InsertMigration(migration.name); } if (finalSQL.empty() && !runSd0Migrations) { - Game::logger->Log("MigrationRunner", "Server database is up to date."); + LOG("Server database is up to date."); return; } if (!finalSQL.empty()) { auto migration = GeneralUtils::SplitString(static_cast(finalSQL), ';'); - std::unique_ptr simpleStatement(Database::CreateStmt()); for (auto& query : migration) { try { if (query.empty()) continue; - simpleStatement->execute(query.c_str()); + Database::Get()->ExecuteCustomQuery(query.c_str()); } catch (sql::SQLException& e) { - Game::logger->Log("MigrationRunner", "Encountered error running migration: %s", e.what()); + LOG("Encountered error running migration: %s", e.what()); } } } @@ -87,9 +75,9 @@ void MigrationRunner::RunMigrations() { // Do this last on the off chance none of the other migrations have been run yet. if (runSd0Migrations) { uint32_t numberOfUpdatedModels = BrickByBrickFix::UpdateBrickByBrickModelsToSd0(); - Game::logger->Log("MasterServer", "%i models were updated from zlib to sd0.", numberOfUpdatedModels); + LOG("%i models were updated from zlib to sd0.", numberOfUpdatedModels); uint32_t numberOfTruncatedModels = BrickByBrickFix::TruncateBrokenBrickByBrickXml(); - Game::logger->Log("MasterServer", "%i models were truncated from the database.", numberOfTruncatedModels); + LOG("%i models were truncated from the database.", numberOfTruncatedModels); } } @@ -98,9 +86,7 @@ void MigrationRunner::RunSQLiteMigrations() { cdstmt.execQuery().finalize(); cdstmt.finalize(); - auto* stmt = Database::CreatePreppedStmt("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); - stmt->execute(); - delete stmt; + Database::Get()->CreateMigrationHistoryTable(); for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "migrations/cdserver/").string())) { auto migration = LoadMigration("cdserver/" + entry); @@ -109,50 +95,39 @@ void MigrationRunner::RunSQLiteMigrations() { // Check if there is an entry in the migration history table on the cdclient database. cdstmt = CDClientDatabase::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); - cdstmt.bind((int32_t) 1, migration.name.c_str()); + cdstmt.bind(static_cast(1), migration.name.c_str()); auto cdres = cdstmt.execQuery(); - bool doExit = !cdres.eof(); - cdres.finalize(); - cdstmt.finalize(); - if (doExit) continue; + if (!cdres.eof()) continue; // Check first if there is entry in the migration history table on the main database. - stmt = Database::CreatePreppedStmt("SELECT name FROM migration_history WHERE name = ?;"); - stmt->setString(1, migration.name.c_str()); - auto* res = stmt->executeQuery(); - doExit = res->next(); - delete res; - delete stmt; - if (doExit) { + if (Database::Get()->IsMigrationRun(migration.name)) { // Insert into cdclient database if there is an entry in the main database but not the cdclient database. cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); - cdstmt.bind((int32_t) 1, migration.name.c_str()); - cdstmt.execQuery().finalize(); - cdstmt.finalize(); + cdstmt.bind(static_cast(1), migration.name.c_str()); + cdstmt.execQuery(); continue; } // Doing these 1 migration at a time since one takes a long time and some may think it is crashing. // This will at the least guarentee that the full migration needs to be run in order to be counted as "migrated". - Game::logger->Log("MigrationRunner", "Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str()); + LOG("Executing migration: %s. This may take a while. Do not shut down server.", migration.name.c_str()); CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); for (const auto& dml : GeneralUtils::SplitString(migration.data, ';')) { if (dml.empty()) continue; try { CDClientDatabase::ExecuteDML(dml.c_str()); } catch (CppSQLite3Exception& e) { - Game::logger->Log("MigrationRunner", "Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage()); + LOG("Encountered error running DML command: (%i) : %s", e.errorCode(), e.errorMessage()); } } // Insert into cdclient database. cdstmt = CDClientDatabase::CreatePreppedStmt("INSERT INTO migration_history (name) VALUES (?);"); - cdstmt.bind((int32_t) 1, migration.name.c_str()); - cdstmt.execQuery().finalize(); - cdstmt.finalize(); + cdstmt.bind(static_cast(1), migration.name.c_str()); + cdstmt.execQuery(); CDClientDatabase::ExecuteQuery("COMMIT;"); } - Game::logger->Log("MigrationRunner", "CDServer database is up to date."); + LOG("CDServer database is up to date."); } diff --git a/dDatabase/MigrationRunner.h b/dDatabase/GameDatabase/MigrationRunner.h similarity index 100% rename from dDatabase/MigrationRunner.h rename to dDatabase/GameDatabase/MigrationRunner.h diff --git a/dDatabase/GameDatabase/MySQL/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/CMakeLists.txt new file mode 100644 index 00000000..9114445d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/CMakeLists.txt @@ -0,0 +1,11 @@ +SET(DDATABSE_DATABSES_MYSQL_SOURCES + "MySQLDatabase.cpp" +) + +add_subdirectory(Tables) + +foreach(file ${DDATABASES_DATABASES_MYSQL_TABLES_SOURCES}) + set(DDATABSE_DATABSES_MYSQL_SOURCES ${DDATABSE_DATABSES_MYSQL_SOURCES} "Tables/${file}") +endforeach() + +set(DDATABSE_DATABSES_MYSQL_SOURCES ${DDATABSE_DATABSES_MYSQL_SOURCES} PARENT_SCOPE) diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp b/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp new file mode 100644 index 00000000..259c3866 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp @@ -0,0 +1,113 @@ +#include "MySQLDatabase.h" + +#include "Database.h" +#include "Game.h" +#include "dConfig.h" +#include "Logger.h" + +namespace { + std::string databaseName; + sql::Properties properties; + sql::Driver* driver = nullptr; + sql::Connection* con = nullptr; +}; + +void MySQLDatabase::Connect() { + driver = sql::mariadb::get_driver_instance(); + + // The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where + // 1) it tries to parse a database from the connection string (like in tcp://localhost:3001/darkflame) based on the + // presence of a / + // 2) even avoiding that, the connector still assumes you're connecting with a tcp socket + // So, what we do in the presence of a unix socket or pipe is to set the hostname to the protocol and localhost, + // which avoids parsing errors while still ensuring the correct connection type is used, and then setting the appropriate + // property manually (which the URL parsing fails to do) + const std::string UNIX_PROTO = "unix://"; + const std::string PIPE_PROTO = "pipe://"; + std::string mysql_host = Game::config->GetValue("mysql_host"); + if (mysql_host.find(UNIX_PROTO) == 0) { + properties["hostName"] = "unix://localhost"; + properties["localSocket"] = mysql_host.substr(UNIX_PROTO.length()).c_str(); + } else if (mysql_host.find(PIPE_PROTO) == 0) { + properties["hostName"] = "pipe://localhost"; + properties["pipe"] = mysql_host.substr(PIPE_PROTO.length()).c_str(); + } else { + properties["hostName"] = mysql_host.c_str(); + } + properties["user"] = Game::config->GetValue("mysql_username").c_str(); + properties["password"] = Game::config->GetValue("mysql_password").c_str(); + properties["autoReconnect"] = "true"; + + databaseName = Game::config->GetValue("mysql_database").c_str(); + + // `connect(const Properties& props)` segfaults in windows debug, but + // `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly + if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) { + con = driver->connect(properties); + } else { + con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str()); + } + con->setSchema(databaseName.c_str()); +} + +void MySQLDatabase::Destroy(std::string source) { + if (!con) return; + + if (source.empty()) LOG("Destroying MySQL connection!"); + else LOG("Destroying MySQL connection from %s!", source.c_str()); + + con->close(); + delete con; + con = nullptr; +} + +void MySQLDatabase::ExecuteCustomQuery(const std::string_view query) { + std::unique_ptr(con->createStatement())->execute(query.data()); +} + +sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) { + if (!con) { + Connect(); + LOG("Trying to reconnect to MySQL"); + } + + if (!con->isValid() || con->isClosed()) { + delete con; + + con = nullptr; + + Connect(); + LOG("Trying to reconnect to MySQL from invalid or closed connection"); + } + + return con->prepareStatement(sql::SQLString(query.c_str(), query.length())); +} + +void MySQLDatabase::Commit() { + con->commit(); +} + +bool MySQLDatabase::GetAutoCommit() { + // TODO This should not just access a pointer. A future PR should update this + // to check for null and throw an error if the connection is not valid. + return con->getAutoCommit(); +} + +void MySQLDatabase::SetAutoCommit(bool value) { + // TODO This should not just access a pointer. A future PR should update this + // to check for null and throw an error if the connection is not valid. + con->setAutoCommit(value); +} + +void MySQLDatabase::DeleteCharacter(const uint32_t characterId) { + ExecuteDelete("DELETE FROM charxml WHERE id=? LIMIT 1;", characterId); + ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId); + ExecuteDelete("DELETE FROM leaderboard WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);", characterId); + ExecuteDelete("DELETE FROM properties WHERE owner_id=?;", characterId); + ExecuteDelete("DELETE FROM ugc WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM activity_log WHERE character_id=?;", characterId); + ExecuteDelete("DELETE FROM mail WHERE receiver_id=?;", characterId); + ExecuteDelete("DELETE FROM charinfo WHERE id=? LIMIT 1;", characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h new file mode 100644 index 00000000..836ab56c --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -0,0 +1,250 @@ +#ifndef __MYSQLDATABASE__H__ +#define __MYSQLDATABASE__H__ + +#include +#include + +#include "GameDatabase.h" + +typedef std::unique_ptr& UniquePreppedStmtRef; + +// Purposefully no definition for this to provide linker errors in the case someone tries to +// bind a parameter to a type that isn't defined. +template +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const ParamType param); + +// This is a function to set each parameter in a prepared statement. +// This is accomplished with a combination of parameter packing and Fold Expressions. +// The constexpr if statement is used to prevent the compiler from trying to call SetParam with 0 arguments. +template +void SetParams(UniquePreppedStmtRef stmt, Args&&... args) { + if constexpr (sizeof...(args) != 0) { + int i = 1; + (SetParam(stmt, i++, args), ...); + } +} + +class MySQLDatabase : public GameDatabase { +public: + void Connect() override; + void Destroy(std::string source = "") override; + + sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override; + void Commit() override; + bool GetAutoCommit() override; + void SetAutoCommit(bool value) override; + void ExecuteCustomQuery(const std::string_view query) override; + + // Overloaded queries + std::optional GetMasterInfo() override; + + std::vector GetApprovedCharacterNames() override; + + std::vector GetFriendsList(uint32_t charID) override; + + std::optional GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override; + void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override; + void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override; + void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override; + void DeleteUgcModelData(const LWOOBJID& modelId) override; + void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override; + std::vector GetAllUgcModels() override; + void CreateMigrationHistoryTable() override; + bool IsMigrationRun(const std::string_view str) override; + void InsertMigration(const std::string_view str) override; + std::optional GetCharacterInfo(const uint32_t charId) override; + std::optional GetCharacterInfo(const std::string_view charId) override; + std::string GetCharacterXml(const uint32_t accountId) override; + void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override; + std::optional GetAccountInfo(const std::string_view username) override; + void InsertNewCharacter(const ICharInfo::Info info) override; + void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override; + std::vector GetAccountCharacterIds(uint32_t accountId) override; + void DeleteCharacter(const uint32_t characterId) override; + void SetCharacterName(const uint32_t characterId, const std::string_view name) override; + void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override; + void UpdateLastLoggedInCharacter(const uint32_t characterId) override; + void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override; + std::optional GetPetNameInfo(const LWOOBJID& petId) override; + std::optional GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override; + void UpdatePropertyModerationInfo(const IProperty::Info& info) override; + void UpdatePropertyDetails(const IProperty::Info& info) override; + void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override; + std::vector GetPropertyModels(const LWOOBJID& propertyId) override; + void RemoveUnreferencedUgcModels() override; + void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) 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; + void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override; + void InsertNewMail(const IMail::MailInfo& mail) override; + void InsertNewUgcModel( + std::istringstream& sd0Data, + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) override; + std::vector GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override; + std::optional GetMail(const uint64_t mailId) override; + uint32_t GetUnreadMailCount(const uint32_t characterId) override; + void MarkMailRead(const uint64_t mailId) override; + void DeleteMail(const uint64_t mailId) override; + void ClaimMailItem(const uint64_t mailId) override; + void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override; + void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override; + void UpdateAccountBan(const uint32_t accountId, const bool banned) override; + void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override; + void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override; + void SetMasterIp(const std::string_view ip, const uint32_t port) override; + std::optional GetCurrentPersistentId() override; + void InsertDefaultPersistentId() override; + void UpdatePersistentId(const uint32_t id) override; + std::optional GetDonationTotal(const uint32_t activityId) override; + std::optional IsPlaykeyActive(const int32_t playkeyId) override; + std::vector GetUgcModels(const LWOOBJID& propertyId) override; + void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override; + std::vector GetIgnoreList(const uint32_t playerId) override; + void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; + std::vector GetRewardCodesByAccountID(const uint32_t account_id) override; +private: + + // Generic query functions that can be used for any query. + // Return type may be different depending on the query, so it is up to the caller to check the return type. + // The first argument is the query string, and the rest are the parameters to bind to the query. + // The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope + template + inline std::unique_ptr ExecuteSelect(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return std::unique_ptr(preppedStmt->executeQuery())); + } + + template + inline void ExecuteDelete(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(preppedStmt->execute()); + } + + template + inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->executeUpdate()); + } + + template + inline bool ExecuteInsert(const std::string& query, Args&&... args) { + std::unique_ptr preppedStmt(CreatePreppedStmt(query)); + SetParams(preppedStmt, std::forward(args)...); + DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt->execute()); + } +}; + +// Below are each of the definitions of SetParam for each supported type. + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string_view param) { + // LOG("%s", param.data()); + stmt->setString(index, param.data()); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const char* param) { + // LOG("%s", param); + stmt->setString(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::string param) { + // LOG("%s", param.c_str()); + stmt->setString(index, param.c_str()); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int8_t param) { + // LOG("%u", param); + stmt->setByte(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint8_t param) { + // LOG("%d", param); + stmt->setByte(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int16_t param) { + // LOG("%u", param); + stmt->setShort(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint16_t param) { + // LOG("%d", param); + stmt->setShort(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint32_t param) { + // LOG("%u", param); + stmt->setUInt(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int32_t param) { + // LOG("%d", param); + stmt->setInt(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const int64_t param) { + // LOG("%llu", param); + stmt->setInt64(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const uint64_t param) { + // LOG("%llu", param); + stmt->setUInt64(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const float param) { + // LOG("%f", param); + stmt->setFloat(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const double param) { + // LOG("%f", param); + stmt->setDouble(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const bool param) { + // LOG("%d", param); + stmt->setBoolean(index, param); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::istream* param) { + // LOG("Blob"); + // This is the one time you will ever see me use const_cast. + stmt->setBlob(index, const_cast(param)); +} + +template<> +inline void SetParam(UniquePreppedStmtRef stmt, const int index, const std::optional param) { + if (param) { + // LOG("%d", param.value()); + stmt->setInt(index, param.value()); + } else { + // LOG("Null"); + stmt->setNull(index, sql::DataType::SQLNULL); + } +} + +#endif //!__MYSQLDATABASE__H__ diff --git a/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp b/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp new file mode 100644 index 00000000..801f444d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp @@ -0,0 +1,37 @@ +#include "MySQLDatabase.h" + +#include "eGameMasterLevel.h" + +std::optional MySQLDatabase::GetAccountInfo(const std::string_view username) { + auto result = ExecuteSelect("SELECT id, password, banned, locked, play_key_id, gm_level FROM accounts WHERE name = ? LIMIT 1;", username); + + if (!result->next()) { + return std::nullopt; + } + + IAccounts::Info toReturn; + toReturn.id = result->getUInt("id"); + toReturn.maxGmLevel = static_cast(result->getInt("gm_level")); + toReturn.bcryptPassword = result->getString("password").c_str(); + toReturn.banned = result->getBoolean("banned"); + toReturn.locked = result->getBoolean("locked"); + toReturn.playKeyId = result->getUInt("play_key_id"); + + return toReturn; +} + +void MySQLDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) { + ExecuteUpdate("UPDATE accounts SET mute_expire = ? WHERE id = ?;", timeToUnmute, accountId); +} + +void MySQLDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) { + ExecuteUpdate("UPDATE accounts SET banned = ? WHERE id = ?;", banned, accountId); +} + +void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) { + ExecuteUpdate("UPDATE accounts SET password = ? WHERE id = ?;", bcryptpassword, accountId); +} + +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(eGameMasterLevel::OPERATOR)); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp b/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp new file mode 100644 index 00000000..7243ce3c --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp @@ -0,0 +1,17 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) { + ExecuteInsert("INSERT IGNORE INTO accounts_rewardcodes (account_id, rewardcode) VALUES (?, ?);", account_id, reward_code); +} + +std::vector MySQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) { + auto result = ExecuteSelect("SELECT rewardcode FROM accounts_rewardcodes WHERE account_id = ?;", account_id); + + std::vector toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + toReturn.push_back(result->getUInt("rewardcode")); + } + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp b/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp new file mode 100644 index 00000000..50fd6b79 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/ActivityLog.cpp @@ -0,0 +1,6 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) { + ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);", + characterId, static_cast(activityType), static_cast(time(NULL)), mapId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp b/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp new file mode 100644 index 00000000..4f23941d --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/BugReports.cpp @@ -0,0 +1,6 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertNewBugReport(const IBugReports::Info& info) { + ExecuteInsert("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)", + info.body, info.clientVersion, info.otherPlayer, info.selection, info.characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt new file mode 100644 index 00000000..9f0e7baa --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -0,0 +1,24 @@ +set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES + "Accounts.cpp" + "AccountsRewardCodes.cpp" + "ActivityLog.cpp" + "BugReports.cpp" + "CharInfo.cpp" + "CharXml.cpp" + "CommandLog.cpp" + "Friends.cpp" + "IgnoreList.cpp" + "Leaderboard.cpp" + "Mail.cpp" + "MigrationHistory.cpp" + "ObjectIdTracker.cpp" + "PetNames.cpp" + "PlayerCheatDetections.cpp" + "PlayKeys.cpp" + "Property.cpp" + "PropertyContents.cpp" + "Servers.cpp" + "Ugc.cpp" + PARENT_SCOPE +) + diff --git a/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp new file mode 100644 index 00000000..7406e69b --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CharInfo.cpp @@ -0,0 +1,78 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetApprovedCharacterNames() { + auto result = ExecuteSelect("SELECT name FROM charinfo;"); + + std::vector toReturn; + + while (result->next()) { + toReturn.push_back(result->getString("name").c_str()); + } + + return toReturn; +} + +std::optional CharInfoFromQueryResult(std::unique_ptr stmt) { + if (!stmt->next()) { + return std::nullopt; + } + + ICharInfo::Info toReturn; + + toReturn.id = stmt->getUInt("id"); + toReturn.name = stmt->getString("name").c_str(); + toReturn.pendingName = stmt->getString("pending_name").c_str(); + toReturn.needsRename = stmt->getBoolean("needs_rename"); + toReturn.cloneId = stmt->getUInt64("prop_clone_id"); + toReturn.accountId = stmt->getUInt("account_id"); + toReturn.permissionMap = static_cast(stmt->getUInt("permission_map")); + + return toReturn; +} + +std::optional MySQLDatabase::GetCharacterInfo(const uint32_t charId) { + return CharInfoFromQueryResult( + ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId) + ); +} + +std::optional MySQLDatabase::GetCharacterInfo(const std::string_view name) { + return CharInfoFromQueryResult( + ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name) + ); +} + +std::vector MySQLDatabase::GetAccountCharacterIds(const uint32_t accountId) { + auto result = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId); + + std::vector toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + toReturn.push_back(result->getUInt("id")); + } + + return toReturn; +} + +void MySQLDatabase::InsertNewCharacter(const ICharInfo::Info info) { + ExecuteInsert( + "INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)", + info.id, + info.accountId, + info.name, + info.pendingName, + false, + static_cast(time(NULL))); +} + +void MySQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) { + ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1;", name, static_cast(time(NULL)), characterId); +} + +void MySQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) { + ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ? LIMIT 1", name, static_cast(time(NULL)), characterId); +} + +void MySQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) { + ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ? LIMIT 1", static_cast(time(NULL)), characterId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp b/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp new file mode 100644 index 00000000..91a6351e --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CharXml.cpp @@ -0,0 +1,19 @@ +#include "MySQLDatabase.h" + +std::string MySQLDatabase::GetCharacterXml(const uint32_t charId) { + auto result = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId); + + if (!result->next()) { + return ""; + } + + return result->getString("xml_data").c_str(); +} + +void MySQLDatabase::UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) { + ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId); +} + +void MySQLDatabase::InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) { + ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp b/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp new file mode 100644 index 00000000..c8ae365a --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/CommandLog.cpp @@ -0,0 +1,5 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) { + ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp b/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp new file mode 100644 index 00000000..da9b34a3 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Friends.cpp @@ -0,0 +1,73 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetFriendsList(const uint32_t charId) { + auto friendsList = ExecuteSelect( + R"QUERY( + SELECT fr.requested_player AS player, best_friend AS bff, ci.name AS name FROM + ( + SELECT CASE + WHEN player_id = ? THEN friend_id + WHEN friend_id = ? THEN player_id + END AS requested_player, best_friend FROM friends + ) AS fr + JOIN charinfo AS ci ON ci.id = fr.requested_player + WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?; + )QUERY", charId, charId, charId); + + std::vector toReturn; + toReturn.reserve(friendsList->rowsCount()); + + while (friendsList->next()) { + FriendData fd; + fd.friendID = friendsList->getUInt("player"); + fd.isBestFriend = friendsList->getInt("bff") == 3; // 0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs + fd.friendName = friendsList->getString("name").c_str(); + + toReturn.push_back(fd); + } + + return toReturn; +} + +std::optional MySQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + auto result = ExecuteSelect("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); + + if (!result->next()) { + return std::nullopt; + } + + IFriends::BestFriendStatus toReturn; + toReturn.playerCharacterId = result->getUInt("player_id"); + toReturn.friendCharacterId = result->getUInt("friend_id"); + toReturn.bestFriendStatus = result->getUInt("best_friend"); + + return toReturn; +} + +void MySQLDatabase::SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) { + ExecuteUpdate("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + bestFriendStatus, + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); +} + +void MySQLDatabase::AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + ExecuteInsert("INSERT IGNORE INTO friends (player_id, friend_id, best_friend) VALUES (?, ?, 0);", playerCharacterId, friendCharacterId); +} + +void MySQLDatabase::RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) { + ExecuteDelete("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;", + playerCharacterId, + friendCharacterId, + friendCharacterId, + playerCharacterId + ); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp new file mode 100644 index 00000000..283df324 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/IgnoreList.cpp @@ -0,0 +1,22 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetIgnoreList(const uint32_t playerId) { + auto result = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId); + + std::vector ignoreList; + + ignoreList.reserve(result->rowsCount()); + while (result->next()) { + ignoreList.push_back(IIgnoreList::Info{ result->getString("name").c_str(), result->getUInt("ignore_id") }); + } + + return ignoreList; +} + +void MySQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + ExecuteInsert("INSERT IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId); +} + +void MySQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) { + ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp b/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp new file mode 100644 index 00000000..22403abb --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Leaderboard.cpp @@ -0,0 +1,11 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetDonationTotal(const uint32_t activityId) { + auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId); + + if (!donation_total->next()) { + return std::nullopt; + } + + return donation_total->getUInt("donation_total"); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp new file mode 100644 index 00000000..63f5ceca --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Mail.cpp @@ -0,0 +1,83 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertNewMail(const IMail::MailInfo& mail) { + ExecuteInsert( + "INSERT INTO `mail` " + "(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)" + " VALUES (?,?,?,?,?,?,?,?,?,?,?,0)", + mail.senderId, + mail.senderUsername, + mail.receiverId, + mail.recipient, + static_cast(time(NULL)), + mail.subject, + mail.body, + mail.itemID, + mail.itemLOT, + 0, + mail.itemCount); +} + +std::vector MySQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) { + auto res = ExecuteSelect( + "SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent" + " FROM mail WHERE receiver_id=? limit ?;", + characterId, numberOfMail); + + std::vector toReturn; + toReturn.reserve(res->rowsCount()); + + while (res->next()) { + IMail::MailInfo mail; + mail.id = res->getUInt64("id"); + mail.subject = res->getString("subject").c_str(); + mail.body = res->getString("body").c_str(); + mail.senderUsername = res->getString("sender_name").c_str(); + mail.itemID = res->getUInt("attachment_id"); + mail.itemLOT = res->getInt("attachment_lot"); + mail.itemSubkey = res->getInt("attachment_subkey"); + mail.itemCount = res->getInt("attachment_count"); + mail.timeSent = res->getUInt64("time_sent"); + mail.wasRead = res->getBoolean("was_read"); + + toReturn.push_back(std::move(mail)); + } + + return toReturn; +} + +std::optional MySQLDatabase::GetMail(const uint64_t mailId) { + auto res = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId); + + if (!res->next()) { + return std::nullopt; + } + + IMail::MailInfo toReturn; + toReturn.itemLOT = res->getInt("attachment_lot"); + toReturn.itemCount = res->getInt("attachment_count"); + + return toReturn; +} + +uint32_t MySQLDatabase::GetUnreadMailCount(const uint32_t characterId) { + auto res = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId); + + if (!res->next()) { + return 0; + } + + return res->getInt("number_unread"); +} + +void MySQLDatabase::MarkMailRead(const uint64_t mailId) { + ExecuteUpdate("UPDATE mail SET was_read=1 WHERE id=? LIMIT 1;", mailId); +} + +void MySQLDatabase::ClaimMailItem(const uint64_t mailId) { + ExecuteUpdate("UPDATE mail SET attachment_lot=0 WHERE id=? LIMIT 1;", mailId); +} + +void MySQLDatabase::DeleteMail(const uint64_t mailId) { + ExecuteDelete("DELETE FROM mail WHERE id=? LIMIT 1;", mailId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp b/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp new file mode 100644 index 00000000..a0afc341 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/MigrationHistory.cpp @@ -0,0 +1,13 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::CreateMigrationHistoryTable() { + ExecuteInsert("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP());"); +} + +bool MySQLDatabase::IsMigrationRun(const std::string_view str) { + return ExecuteSelect("SELECT name FROM migration_history WHERE name = ?;", str)->next(); +} + +void MySQLDatabase::InsertMigration(const std::string_view str) { + ExecuteInsert("INSERT INTO migration_history (name) VALUES (?);", str); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp b/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp new file mode 100644 index 00000000..f22cd855 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/ObjectIdTracker.cpp @@ -0,0 +1,17 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetCurrentPersistentId() { + auto result = ExecuteSelect("SELECT last_object_id FROM object_id_tracker"); + if (!result->next()) { + return std::nullopt; + } + return result->getUInt("last_object_id"); +} + +void MySQLDatabase::InsertDefaultPersistentId() { + ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);"); +} + +void MySQLDatabase::UpdatePersistentId(const uint32_t newId) { + ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp b/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp new file mode 100644 index 00000000..dd2a3a6c --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PetNames.cpp @@ -0,0 +1,26 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) { + ExecuteInsert( + "INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?) " + "ON DUPLICATE KEY UPDATE pet_name = ?, approved = ?;", + petId, + info.petName, + info.approvalStatus, + info.petName, + info.approvalStatus); +} + +std::optional MySQLDatabase::GetPetNameInfo(const LWOOBJID& petId) { + auto result = ExecuteSelect("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;", petId); + + if (!result->next()) { + return std::nullopt; + } + + IPetNames::Info toReturn; + toReturn.petName = result->getString("pet_name").c_str(); + toReturn.approvalStatus = result->getInt("approved"); + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp b/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp new file mode 100644 index 00000000..63f2822a --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PlayKeys.cpp @@ -0,0 +1,11 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::IsPlaykeyActive(const int32_t playkeyId) { + auto keyCheckRes = ExecuteSelect("SELECT active FROM `play_keys` WHERE id=?", playkeyId); + + if (!keyCheckRes->next()) { + return std::nullopt; + } + + return keyCheckRes->getBoolean("active"); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp b/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp new file mode 100644 index 00000000..753630c3 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PlayerCheatDetections.cpp @@ -0,0 +1,7 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) { + ExecuteInsert( + "INSERT INTO player_cheat_detections (account_id, name, violation_msg, violation_system_address) VALUES (?, ?, ?, ?)", + info.userId, info.username, info.extraMessage, info.systemAddress); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Property.cpp b/dDatabase/GameDatabase/MySQL/Tables/Property.cpp new file mode 100644 index 00000000..5d72a3b5 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Property.cpp @@ -0,0 +1,57 @@ +#include "MySQLDatabase.h" + +std::optional MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) { + auto propertyEntry = ExecuteSelect( + "SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved " + "FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId); + + if (!propertyEntry->next()) { + return std::nullopt; + } + + IProperty::Info toReturn; + toReturn.id = propertyEntry->getUInt64("id"); + toReturn.ownerId = propertyEntry->getUInt64("owner_id"); + toReturn.cloneId = propertyEntry->getUInt64("clone_id"); + toReturn.name = propertyEntry->getString("name").c_str(); + toReturn.description = propertyEntry->getString("description").c_str(); + toReturn.privacyOption = propertyEntry->getInt("privacy_option"); + toReturn.rejectionReason = propertyEntry->getString("rejection_reason").c_str(); + toReturn.lastUpdatedTime = propertyEntry->getUInt("last_updated"); + toReturn.claimedTime = propertyEntry->getUInt("time_claimed"); + toReturn.reputation = propertyEntry->getUInt("reputation"); + toReturn.modApproved = propertyEntry->getUInt("mod_approved"); + + return toReturn; +} + +void MySQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) { + ExecuteUpdate("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ? LIMIT 1;", + info.privacyOption, + info.rejectionReason, + info.modApproved, + info.id); +} + +void MySQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) { + ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ? LIMIT 1;", info.name, info.description, info.id); +} + +void MySQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) { + ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ? LIMIT 1;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID()); +} + +void MySQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) { + auto insertion = ExecuteInsert( + "INSERT INTO properties" + "(id, owner_id, template_id, clone_id, name, description, zone_id, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, performance_cost)" + "VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, 0.0)", + info.id, + info.ownerId, + templateId, + zoneId.GetCloneID(), + info.name, + info.description, + zoneId.GetMapID() + ); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp new file mode 100644 index 00000000..dba82d56 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -0,0 +1,54 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetPropertyModels(const LWOOBJID& 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 toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + IPropertyContents::Model model; + model.id = result->getUInt64("id"); + model.lot = static_cast(result->getUInt("lot")); + model.position.x = result->getFloat("x"); + model.position.y = result->getFloat("y"); + model.position.z = result->getFloat("z"); + model.rotation.w = result->getFloat("rw"); + model.rotation.x = result->getFloat("rx"); + model.rotation.y = result->getFloat("ry"); + model.rotation.z = result->getFloat("rz"); + model.ugcId = result->getUInt64("ugc_id"); + toReturn.push_back(std::move(model)); + } + return toReturn; +} + +void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) { + try { + ExecuteInsert( + "INSERT INTO properties_contents" + "(id, property_id, ugc_id, lot, x, y, z, rx, ry, rz, rw, model_name, model_description, behavior_1, behavior_2, behavior_3, behavior_4, behavior_5)" + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 18 + model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast(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. + 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::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) { + ExecuteUpdate( + "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) { + ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp new file mode 100644 index 00000000..4411ad21 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Servers.cpp @@ -0,0 +1,23 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) { + // We only want our 1 entry anyways, so we can just delete all and reinsert the one we want + // since it would be two queries anyways. + ExecuteDelete("TRUNCATE TABLE servers;"); + ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port); +} + +std::optional MySQLDatabase::GetMasterInfo() { + auto result = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;"); + + if (!result->next()) { + return std::nullopt; + } + + MasterInfo toReturn; + + toReturn.ip = result->getString("ip").c_str(); + toReturn.port = result->getInt("port"); + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp new file mode 100644 index 00000000..3b62a51b --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Ugc.cpp @@ -0,0 +1,71 @@ +#include "MySQLDatabase.h" + +std::vector MySQLDatabase::GetUgcModels(const LWOOBJID& propertyId) { + auto result = ExecuteSelect( + "SELECT lxfml, u.id FROM ugc AS u JOIN properties_contents AS pc ON u.id = pc.ugc_id WHERE lot = 14 AND property_id = ? AND pc.ugc_id IS NOT NULL;", + propertyId); + + std::vector toReturn; + + while (result->next()) { + IUgc::Model model; + + // blob is owned by the query, so we need to do a deep copy :/ + std::unique_ptr blob(result->getBlob("lxfml")); + model.lxfmlData << blob->rdbuf(); + model.id = result->getUInt64("id"); + toReturn.push_back(std::move(model)); + } + + return toReturn; +} + +std::vector MySQLDatabase::GetAllUgcModels() { + auto result = ExecuteSelect("SELECT id, lxfml FROM ugc;"); + + std::vector models; + models.reserve(result->rowsCount()); + while (result->next()) { + IUgc::Model model; + model.id = result->getInt64("id"); + + // blob is owned by the query, so we need to do a deep copy :/ + std::unique_ptr blob(result->getBlob("lxfml")); + model.lxfmlData << blob->rdbuf(); + models.push_back(std::move(model)); + } + + return models; +} + +void MySQLDatabase::RemoveUnreferencedUgcModels() { + ExecuteDelete("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents WHERE ugc_id IS NOT NULL);"); +} + +void MySQLDatabase::InsertNewUgcModel( + std::istringstream& sd0Data, // cant be const sad + const uint32_t blueprintId, + const uint32_t accountId, + const uint32_t characterId) { + const std::istream stream(sd0Data.rdbuf()); + ExecuteInsert( + "INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)", + blueprintId, + accountId, + characterId, + 0, + &stream, + false, + "weedeater.lxfml" + ); +} + +void MySQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) { + ExecuteDelete("DELETE FROM ugc WHERE id = ?;", modelId); + ExecuteDelete("DELETE FROM properties_contents WHERE ugc_id = ?;", modelId); +} + +void MySQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) { + const std::istream stream(lxfml.rdbuf()); + ExecuteUpdate("UPDATE ugc SET lxfml = ? WHERE id = ?;", &stream, modelId); +} diff --git a/dDatabase/Tables/CDActivitiesTable.h b/dDatabase/Tables/CDActivitiesTable.h deleted file mode 100644 index 4b60afbd..00000000 --- a/dDatabase/Tables/CDActivitiesTable.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDActivities { - unsigned int ActivityID; - unsigned int locStatus; - unsigned int instanceMapID; - unsigned int minTeams; - unsigned int maxTeams; - unsigned int minTeamSize; - unsigned int maxTeamSize; - unsigned int waitTime; - unsigned int startDelay; - bool requiresUniqueData; - unsigned int leaderboardType; - bool localize; - int optionalCostLOT; - int optionalCostCount; - bool showUIRewards; - unsigned int CommunityActivityFlagID; - std::string gate_version; - bool noTeamLootOnDeath; - float optionalPercentage; -}; - -class CDActivitiesTable : public CDTable { -private: - std::vector entries; - -public: - CDActivitiesTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; -}; diff --git a/dDatabase/Tables/CDActivityRewardsTable.h b/dDatabase/Tables/CDActivityRewardsTable.h deleted file mode 100644 index b5503fb6..00000000 --- a/dDatabase/Tables/CDActivityRewardsTable.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDActivityRewards { - unsigned int objectTemplate; //!< The object template (?) - unsigned int ActivityRewardIndex; //!< The activity reward index - int activityRating; //!< The activity rating - unsigned int LootMatrixIndex; //!< The loot matrix index - unsigned int CurrencyIndex; //!< The currency index - unsigned int ChallengeRating; //!< The challenge rating - std::string description; //!< The description -}; - -class CDActivityRewardsTable : public CDTable { -private: - std::vector entries; - -public: - CDActivityRewardsTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; - -}; diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.h b/dDatabase/Tables/CDBehaviorTemplateTable.h deleted file mode 100644 index f9ac9a09..00000000 --- a/dDatabase/Tables/CDBehaviorTemplateTable.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" -#include -#include - -struct CDBehaviorTemplate { - unsigned int behaviorID; //!< The Behavior ID - unsigned int templateID; //!< The Template ID (LOT) - unsigned int effectID; //!< The Effect ID attached - std::unordered_set::iterator effectHandle; //!< The effect handle -}; - - -class CDBehaviorTemplateTable : public CDTable { -private: - std::vector entries; - std::unordered_map entriesMappedByBehaviorID; - std::unordered_set m_EffectHandles; -public: - CDBehaviorTemplateTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; - - const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID); -}; diff --git a/dDatabase/Tables/CDComponentsRegistryTable.cpp b/dDatabase/Tables/CDComponentsRegistryTable.cpp deleted file mode 100644 index 32012f6c..00000000 --- a/dDatabase/Tables/CDComponentsRegistryTable.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "CDComponentsRegistryTable.h" -#include "eReplicaComponentType.h" - -#define CDCLIENT_CACHE_ALL - -CDComponentsRegistryTable::CDComponentsRegistryTable(void) { - -#ifdef CDCLIENT_CACHE_ALL - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ComponentsRegistry"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - //this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ComponentsRegistry"); - while (!tableData.eof()) { - CDComponentsRegistry entry; - entry.id = tableData.getIntField("id", -1); - entry.component_type = static_cast(tableData.getIntField("component_type", 0)); - entry.component_id = tableData.getIntField("component_id", -1); - - this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id); - - tableData.nextRow(); - } - - tableData.finalize(); -#endif -} - -int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) { - const auto& iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id)); - - if (iter == this->mappedEntries.end()) { - return defaultValue; - } - - return iter->second; - -#ifndef CDCLIENT_CACHE_ALL - // Now get the data - std::stringstream query; - - query << "SELECT * FROM ComponentsRegistry WHERE id = " << std::to_string(id); - - auto tableData = CDClientDatabase::ExecuteQuery(query.str()); - while (!tableData.eof()) { - CDComponentsRegistry entry; - entry.id = tableData.getIntField("id", -1); - entry.component_type = tableData.getIntField("component_type", -1); - entry.component_id = tableData.getIntField("component_id", -1); - - //this->entries.push_back(entry); - - //Darwin's stuff: - const auto& it = this->mappedEntries.find(entry.id); - if (it != mappedEntries.end()) { - const auto& iter = it->second.find(entry.component_type); - if (iter == it->second.end()) { - it->second.insert(std::make_pair(entry.component_type, entry.component_id)); - } - } else { - std::map map; - map.insert(std::make_pair(entry.component_type, entry.component_id)); - this->mappedEntries.insert(std::make_pair(entry.id, map)); - } - - tableData.nextRow(); - } - - tableData.finalize(); - - const auto& it2 = this->mappedEntries.find(id); - if (it2 != mappedEntries.end()) { - const auto& iter = it2->second.find(componentType); - if (iter != it2->second.end()) { - return iter->second; - } - } - - return defaultValue; -#endif -} - diff --git a/dDatabase/Tables/CDCurrencyTableTable.h b/dDatabase/Tables/CDCurrencyTableTable.h deleted file mode 100644 index ec700320..00000000 --- a/dDatabase/Tables/CDCurrencyTableTable.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -/*! - \file CDCurrencyTableTable.hpp - \brief Contains data for the CurrencyTable table - */ - - //! CurrencyTable Struct -struct CDCurrencyTable { - unsigned int currencyIndex; //!< The Currency Index - unsigned int npcminlevel; //!< The minimum level of the npc - unsigned int minvalue; //!< The minimum currency - unsigned int maxvalue; //!< The maximum currency - unsigned int id; //!< The ID of the currency index -}; - -//! CurrencyTable table -class CDCurrencyTableTable : public CDTable { -private: - std::vector entries; - -public: - CDCurrencyTableTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; -}; diff --git a/dDatabase/Tables/CDDestructibleComponentTable.h b/dDatabase/Tables/CDDestructibleComponentTable.h deleted file mode 100644 index e42cf486..00000000 --- a/dDatabase/Tables/CDDestructibleComponentTable.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDDestructibleComponent { - unsigned int id; //!< The component ID from the ComponentsRegistry Table - int faction; //!< The Faction ID of the object - std::string factionList; //!< A list of the faction IDs - int life; //!< The amount of life of the object - unsigned int imagination; //!< The amount of imagination of the object - int LootMatrixIndex; //!< The Loot Matrix Index - int CurrencyIndex; //!< The Currency Index - unsigned int level; //!< ??? - float armor; //!< The amount of armor of the object - unsigned int death_behavior; //!< The behavior ID of the death behavior - bool isnpc; //!< Whether or not the object is an NPC - unsigned int attack_priority; //!< ??? - bool isSmashable; //!< Whether or not the object is smashable - int difficultyLevel; //!< ??? -}; - -class CDDestructibleComponentTable : public CDTable { -private: - std::vector entries; - -public: - CDDestructibleComponentTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; -}; diff --git a/dDatabase/Tables/CDEmoteTable.cpp b/dDatabase/Tables/CDEmoteTable.cpp deleted file mode 100644 index aacbdd55..00000000 --- a/dDatabase/Tables/CDEmoteTable.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "CDEmoteTable.h" - -//! Constructor -CDEmoteTableTable::CDEmoteTableTable(void) { - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Emotes"); - while (!tableData.eof()) { - CDEmoteTable* entry = new CDEmoteTable(); - entry->ID = tableData.getIntField("id", -1); - entry->animationName = tableData.getStringField("animationName", ""); - entry->iconFilename = tableData.getStringField("iconFilename", ""); - entry->channel = tableData.getIntField("channel", -1); - entry->locked = tableData.getIntField("locked", -1) != 0; - entry->localize = tableData.getIntField("localize", -1) != 0; - entry->locState = tableData.getIntField("locStatus", -1); - entry->gateVersion = tableData.getStringField("gate_version", ""); - - entries.insert(std::make_pair(entry->ID, entry)); - tableData.nextRow(); - } - - tableData.finalize(); -} - -//! Destructor -CDEmoteTableTable::~CDEmoteTableTable(void) { - for (auto e : entries) { - if (e.second) delete e.second; - } - - entries.clear(); -} - -CDEmoteTable* CDEmoteTableTable::GetEmote(int id) { - for (auto e : entries) { - if (e.first == id) return e.second; - } - - return nullptr; -} - diff --git a/dDatabase/Tables/CDItemComponentTable.h b/dDatabase/Tables/CDItemComponentTable.h deleted file mode 100644 index 11c34dd6..00000000 --- a/dDatabase/Tables/CDItemComponentTable.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" -#include "dCommonVars.h" - -struct CDItemComponent { - unsigned int id; //!< The Component ID - std::string equipLocation; //!< The equip location - unsigned int baseValue; //!< The monetary base value of the item - bool isKitPiece; //!< Whether or not the item belongs to a kit - unsigned int rarity; //!< The rarity of the item - unsigned int itemType; //!< The item type - int64_t itemInfo; //!< The item info - bool inLootTable; //!< Whether or not the item is in a loot table - bool inVendor; //!< Whether or not the item is in a vendor inventory - bool isUnique; //!< ??? - bool isBOP; //!< ??? - bool isBOE; //!< ??? - unsigned int reqFlagID; //!< User must have completed this flag to get the item - unsigned int reqSpecialtyID; //!< ??? - unsigned int reqSpecRank; //!< ??? - unsigned int reqAchievementID; //!< The required achievement must be completed - unsigned int stackSize; //!< The stack size of the item - unsigned int color1; //!< Something to do with item color... - unsigned int decal; //!< The decal of the item - unsigned int offsetGroupID; //!< Something to do with group IDs - unsigned int buildTypes; //!< Something to do with building - std::string reqPrecondition; //!< The required precondition - unsigned int animationFlag; //!< The Animation Flag - unsigned int equipEffects; //!< The effect played when the item is equipped - bool readyForQA; //!< ??? - unsigned int itemRating; //!< ??? - bool isTwoHanded; //!< Whether or not the item is double handed - unsigned int minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object? - unsigned int delResIndex; //!< ??? - unsigned int currencyLOT; //!< ??? - unsigned int altCurrencyCost; //!< ??? - std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set) - UNUSED(std::string audioEventUse); //!< ??? - bool noEquipAnimation; //!< Whether or not there is an equip animation - unsigned int commendationLOT; //!< The commendation LOT - unsigned int commendationCost; //!< The commendation cost - UNUSED(std::string audioEquipMetaEventSet); //!< ??? - std::string currencyCosts; //!< Used for crafting - UNUSED(std::string ingredientInfo); //!< Unused - unsigned int locStatus; //!< ??? - unsigned int forgeType; //!< Forge Type - float SellMultiplier; //!< Something to do with early vendors perhaps (but replaced) -}; - -class CDItemComponentTable : public CDTable { -private: - std::map entries; - -public: - CDItemComponentTable(); - static std::map ParseCraftingCurrencies(const CDItemComponent& itemComponent); - - // Gets an entry by ID - const CDItemComponent& GetItemComponentByID(unsigned int skillID); - - static CDItemComponent Default; -}; diff --git a/dDatabase/Tables/CDItemSetSkillsTable.h b/dDatabase/Tables/CDItemSetSkillsTable.h deleted file mode 100644 index 8328c66b..00000000 --- a/dDatabase/Tables/CDItemSetSkillsTable.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDItemSetSkills { - unsigned int SkillSetID; //!< The skill set ID - unsigned int SkillID; //!< The skill ID - unsigned int SkillCastType; //!< The skill cast type -}; - -class CDItemSetSkillsTable : public CDTable { -private: - std::vector entries; - -public: - CDItemSetSkillsTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; - - std::vector GetBySkillID(unsigned int SkillSetID); -}; diff --git a/dDatabase/Tables/CDItemSetsTable.h b/dDatabase/Tables/CDItemSetsTable.h deleted file mode 100644 index 6756e7fa..00000000 --- a/dDatabase/Tables/CDItemSetsTable.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDItemSets { - unsigned int setID; //!< The item set ID - unsigned int locStatus; //!< The loc status - std::string itemIDs; //!< THe item IDs - unsigned int kitType; //!< The item kit type - unsigned int kitRank; //!< The item kit rank - unsigned int kitImage; //!< The item kit image - unsigned int skillSetWith2; //!< The skill set with 2 - unsigned int skillSetWith3; //!< The skill set with 3 - unsigned int skillSetWith4; //!< The skill set with 4 - unsigned int skillSetWith5; //!< The skill set with 5 - unsigned int skillSetWith6; //!< The skill set with 6 - bool localize; //!< Whether or localize - std::string gate_version; //!< The gate version - unsigned int kitID; //!< The kit ID - float priority; //!< The priority -}; - -class CDItemSetsTable : public CDTable { -private: - std::vector entries; - -public: - CDItemSetsTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; -}; - diff --git a/dDatabase/Tables/CDLootMatrixTable.cpp b/dDatabase/Tables/CDLootMatrixTable.cpp deleted file mode 100644 index 8f25e8a3..00000000 --- a/dDatabase/Tables/CDLootMatrixTable.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "CDLootMatrixTable.h" - -//! Constructor -CDLootMatrixTable::CDLootMatrixTable(void) { - - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootMatrix"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix"); - while (!tableData.eof()) { - CDLootMatrix entry; - entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); - entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1); - entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1); - entry.percent = tableData.getFloatField("percent", -1.0f); - entry.minToDrop = tableData.getIntField("minToDrop", -1); - entry.maxToDrop = tableData.getIntField("maxToDrop", -1); - entry.id = tableData.getIntField("id", -1); - entry.flagID = tableData.getIntField("flagID", -1); - UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); - - this->entries.push_back(entry); - tableData.nextRow(); - } - - tableData.finalize(); -} - -std::vector CDLootMatrixTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) - >> cpplinq::where(predicate) - >> cpplinq::to_vector(); - - return data; -} - -const std::vector& CDLootMatrixTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDLootMatrixTable.h b/dDatabase/Tables/CDLootMatrixTable.h deleted file mode 100644 index c6035841..00000000 --- a/dDatabase/Tables/CDLootMatrixTable.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDLootMatrix { - unsigned int LootMatrixIndex; //!< The Loot Matrix Index - unsigned int LootTableIndex; //!< The Loot Table Index - unsigned int RarityTableIndex; //!< The Rarity Table Index - float percent; //!< The percent that this matrix is used? - unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop - unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop - unsigned int id; //!< The ID of the Loot Matrix - unsigned int flagID; //!< ??? - UNUSED(std::string gate_version); //!< The Gate Version -}; - -class CDLootMatrixTable : public CDTable { -private: - std::vector entries; - -public: - CDLootMatrixTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - const std::vector& GetEntries(void) const; -}; - diff --git a/dDatabase/Tables/CDLootTableTable.cpp b/dDatabase/Tables/CDLootTableTable.cpp deleted file mode 100644 index 0a46784a..00000000 --- a/dDatabase/Tables/CDLootTableTable.cpp +++ /dev/null @@ -1,52 +0,0 @@ -#include "CDLootTableTable.h" - -//! Constructor -CDLootTableTable::CDLootTableTable(void) { - - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootTable"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable"); - while (!tableData.eof()) { - CDLootTable entry; - entry.id = tableData.getIntField("id", -1); - entry.itemid = tableData.getIntField("itemid", -1); - entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1); - entry.id = tableData.getIntField("id", -1); - entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false; - entry.sortPriority = tableData.getIntField("sortPriority", -1); - - this->entries.push_back(entry); - tableData.nextRow(); - } - - tableData.finalize(); -} - -//! Queries the table with a custom "where" clause -std::vector CDLootTableTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) - >> cpplinq::where(predicate) - >> cpplinq::to_vector(); - - return data; -} - -//! Gets all the entries in the table -const std::vector& CDLootTableTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDLootTableTable.h b/dDatabase/Tables/CDLootTableTable.h deleted file mode 100644 index ba6f207e..00000000 --- a/dDatabase/Tables/CDLootTableTable.h +++ /dev/null @@ -1,25 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDLootTable { - unsigned int itemid; //!< The LOT of the item - unsigned int LootTableIndex; //!< The Loot Table Index - unsigned int id; //!< The ID - bool MissionDrop; //!< Whether or not this loot table is a mission drop - unsigned int sortPriority; //!< The sorting priority -}; - -class CDLootTableTable : public CDTable { -private: - std::vector entries; - -public: - CDLootTableTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - const std::vector& GetEntries(void) const; -}; - diff --git a/dDatabase/Tables/CDMissionEmailTable.h b/dDatabase/Tables/CDMissionEmailTable.h deleted file mode 100644 index db2310d4..00000000 --- a/dDatabase/Tables/CDMissionEmailTable.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDMissionEmail { - unsigned int ID; - unsigned int messageType; - unsigned int notificationGroup; - unsigned int missionID; - unsigned int attachmentLOT; - bool localize; - unsigned int locStatus; - std::string gate_version; -}; - - -class CDMissionEmailTable : public CDTable { -private: - std::vector entries; - -public: - CDMissionEmailTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetEntries(void) const; -}; diff --git a/dDatabase/Tables/CDMissionTasksTable.h b/dDatabase/Tables/CDMissionTasksTable.h deleted file mode 100644 index fa213faf..00000000 --- a/dDatabase/Tables/CDMissionTasksTable.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDMissionTasks { - unsigned int id; //!< The Mission ID that the task belongs to - UNUSED(unsigned int locStatus); //!< ??? - unsigned int taskType; //!< The task type - unsigned int target; //!< The mission target - std::string targetGroup; //!< The mission target group - int targetValue; //!< The target value - std::string taskParam1; //!< The task param 1 - UNUSED(std::string largeTaskIcon); //!< ??? - UNUSED(unsigned int IconID); //!< ??? - unsigned int uid; //!< ??? - UNUSED(unsigned int largeTaskIconID); //!< ??? - UNUSED(bool localize); //!< Whether or not the task should be localized - UNUSED(std::string gate_version); //!< ??? -}; - -class CDMissionTasksTable : public CDTable { -private: - std::vector entries; - -public: - CDMissionTasksTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - std::vector GetByMissionID(uint32_t missionID); - - const std::vector& GetEntries(void) const; -}; - diff --git a/dDatabase/Tables/CDMissionsTable.h b/dDatabase/Tables/CDMissionsTable.h deleted file mode 100644 index e6a44b02..00000000 --- a/dDatabase/Tables/CDMissionsTable.h +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" -#include -#include - -struct CDMissions { - int id; //!< The Mission ID - std::string defined_type; //!< The type of mission - std::string defined_subtype; //!< The subtype of the mission - int UISortOrder; //!< The UI Sort Order for the mission - int offer_objectID; //!< The LOT of the mission giver - int target_objectID; //!< The LOT of the mission's target - int64_t reward_currency; //!< The amount of currency to reward the player - int LegoScore; //!< The amount of LEGO Score to reward the player - int64_t reward_reputation; //!< The reputation to award the player - bool isChoiceReward; //!< Whether or not the user has the option to choose their loot - int reward_item1; //!< The first rewarded item - int reward_item1_count; //!< The count of the first item to be rewarded - int reward_item2; //!< The second rewarded item - int reward_item2_count; //!< The count of the second item to be rewarded - int reward_item3; //!< The third rewarded item - int reward_item3_count; //!< The count of the third item to be rewarded - int reward_item4; //!< The fourth rewarded item - int reward_item4_count; //!< The count of the fourth item to be rewarded - int reward_emote; //!< The first emote to be rewarded - int reward_emote2; //!< The second emote to be rewarded - int reward_emote3; //!< The third emote to be rewarded - int reward_emote4; //!< The fourth emote to be rewarded - int reward_maximagination; //!< The amount of max imagination to reward - int reward_maxhealth; //!< The amount of max health to reward - int reward_maxinventory; //!< The amount of max inventory to reward - int reward_maxmodel; //!< ??? - int reward_maxwidget; //!< ??? - int reward_maxwallet; //!< ??? - bool repeatable; //!< Whether or not this mission can be repeated (for instance, is it a daily mission) - int64_t reward_currency_repeatable; //!< The repeatable reward - int reward_item1_repeatable; //!< The first rewarded item - int reward_item1_repeat_count; //!< The count of the first item to be rewarded - int reward_item2_repeatable; //!< The second rewarded item - int reward_item2_repeat_count; //!< The count of the second item to be rewarded - int reward_item3_repeatable; //!< The third rewarded item - int reward_item3_repeat_count; //!< The count of the third item to be rewarded - int reward_item4_repeatable; //!< The fourth rewarded item - int reward_item4_repeat_count; //!< The count of the fourth item to be rewarded - int time_limit; //!< The time limit of the mission - bool isMission; //!< Maybe to differentiate between missions and achievements? - int missionIconID; //!< The mission icon ID - std::string prereqMissionID; //!< A '|' seperated list of prerequisite missions - bool localize; //!< Whether or not to localize the mission - bool inMOTD; //!< In Match of the Day(?) - int64_t cooldownTime; //!< The mission cooldown time - bool isRandom; //!< ??? - std::string randomPool; //!< ??? - int UIPrereqID; //!< ??? - UNUSED(std::string gate_version); //!< The gate version - UNUSED(std::string HUDStates); //!< ??? - UNUSED(int locStatus); //!< ??? - int reward_bankinventory; //!< The amount of bank space this mission rewards -}; - -class CDMissionsTable : public CDTable { -private: - std::vector entries; - -public: - CDMissionsTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - // Gets all the entries in the table - const std::vector& GetEntries(void) const; - - const CDMissions* GetPtrByMissionID(uint32_t missionID) const; - - const CDMissions& GetByMissionID(uint32_t missionID, bool& found) const; - - static CDMissions Default; -}; - diff --git a/dDatabase/Tables/CDObjectSkillsTable.h b/dDatabase/Tables/CDObjectSkillsTable.h deleted file mode 100644 index 4ceaa447..00000000 --- a/dDatabase/Tables/CDObjectSkillsTable.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDObjectSkills { - unsigned int objectTemplate; //!< The LOT of the item - unsigned int skillID; //!< The Skill ID of the object - unsigned int castOnType; //!< ??? - unsigned int AICombatWeight; //!< ??? -}; - -class CDObjectSkillsTable : public CDTable { -private: - std::vector entries; - -public: - CDObjectSkillsTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - // Gets all the entries in the table - std::vector GetEntries(void) const; - -}; - diff --git a/dDatabase/Tables/CDObjectsTable.h b/dDatabase/Tables/CDObjectsTable.h deleted file mode 100644 index 171eddef..00000000 --- a/dDatabase/Tables/CDObjectsTable.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDObjects { - unsigned int id; //!< The LOT of the object - std::string name; //!< The internal name of the object - UNUSED(unsigned int placeable); //!< Whether or not the object is placable - std::string type; //!< The object type - UNUSED(std::string description); //!< An internal description of the object - UNUSED(unsigned int localize); //!< Whether or not the object should localize - UNUSED(unsigned int npcTemplateID); //!< Something related to NPCs... - UNUSED(std::string displayName); //!< The display name of the object - float interactionDistance; //!< The interaction distance of the object - UNUSED(unsigned int nametag); //!< ??? - UNUSED(std::string _internalNotes); //!< Some internal notes (rarely used) - UNUSED(unsigned int locStatus); //!< ??? - UNUSED(std::string gate_version); //!< The gate version for the object - UNUSED(unsigned int HQ_valid); //!< Probably used for the Nexus HQ database on LEGOUniverse.com -}; - -class CDObjectsTable : public CDTable { -private: - std::map entries; - CDObjects m_default; - -public: - CDObjectsTable(); - // Gets an entry by ID - const CDObjects& GetByID(unsigned int LOT); -}; - diff --git a/dDatabase/Tables/CDPhysicsComponentTable.cpp b/dDatabase/Tables/CDPhysicsComponentTable.cpp deleted file mode 100644 index bb21ed7f..00000000 --- a/dDatabase/Tables/CDPhysicsComponentTable.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "CDPhysicsComponentTable.h" - -CDPhysicsComponentTable::CDPhysicsComponentTable(void) { - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent"); - while (!tableData.eof()) { - CDPhysicsComponent* entry = new CDPhysicsComponent(); - entry->id = tableData.getIntField("id", -1); - entry->bStatic = tableData.getIntField("static", -1) != 0; - entry->physicsAsset = tableData.getStringField("physics_asset", ""); - UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0); - UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0); - entry->speed = tableData.getFloatField("speed", -1); - UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1)); - entry->playerHeight = tableData.getFloatField("playerHeight"); - entry->playerRadius = tableData.getFloatField("playerRadius"); - entry->pcShapeType = tableData.getIntField("pcShapeType"); - entry->collisionGroup = tableData.getIntField("collisionGroup"); - UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed")); - UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset")); - UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed")); - UNUSED(entry->friction = tableData.getFloatField("friction")); - UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset")); - - m_entries.insert(std::make_pair(entry->id, entry)); - tableData.nextRow(); - } - - tableData.finalize(); -} - -CDPhysicsComponentTable::~CDPhysicsComponentTable() { - for (auto e : m_entries) { - if (e.second) delete e.second; - } - - m_entries.clear(); -} - -CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) { - for (auto e : m_entries) { - if (e.first == componentID) return e.second; - } - - return nullptr; -} - diff --git a/dDatabase/Tables/CDRarityTableTable.cpp b/dDatabase/Tables/CDRarityTableTable.cpp deleted file mode 100644 index 0b1212c0..00000000 --- a/dDatabase/Tables/CDRarityTableTable.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "CDRarityTableTable.h" - -//! Constructor -CDRarityTableTable::CDRarityTableTable(void) { - - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable"); - while (!tableData.eof()) { - CDRarityTable entry; - entry.id = tableData.getIntField("id", -1); - entry.randmax = tableData.getFloatField("randmax", -1); - entry.rarity = tableData.getIntField("rarity", -1); - entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1); - - this->entries.push_back(entry); - tableData.nextRow(); - } - - tableData.finalize(); -} - -//! Queries the table with a custom "where" clause -std::vector CDRarityTableTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) - >> cpplinq::where(predicate) - >> cpplinq::to_vector(); - - return data; -} - -//! Gets all the entries in the table -const std::vector& CDRarityTableTable::GetEntries(void) const { - return this->entries; -} - diff --git a/dDatabase/Tables/CDRarityTableTable.h b/dDatabase/Tables/CDRarityTableTable.h deleted file mode 100644 index 592346ed..00000000 --- a/dDatabase/Tables/CDRarityTableTable.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDRarityTable { - unsigned int id; - float randmax; - unsigned int rarity; - unsigned int RarityTableIndex; - - friend bool operator> (const CDRarityTable& c1, const CDRarityTable& c2) { - return c1.rarity > c2.rarity; - } - - friend bool operator>= (const CDRarityTable& c1, const CDRarityTable& c2) { - return c1.rarity >= c2.rarity; - } - - friend bool operator< (const CDRarityTable& c1, const CDRarityTable& c2) { - return c1.rarity < c2.rarity; - } - - friend bool operator<= (const CDRarityTable& c1, const CDRarityTable& c2) { - return c1.rarity <= c2.rarity; - } -}; - -class CDRarityTableTable : public CDTable { -private: - std::vector entries; - -public: - CDRarityTableTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - const std::vector& GetEntries() const; -}; - diff --git a/dDatabase/Tables/CDRewardsTable.cpp b/dDatabase/Tables/CDRewardsTable.cpp deleted file mode 100644 index 55672add..00000000 --- a/dDatabase/Tables/CDRewardsTable.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include "CDRewardsTable.h" - -CDRewardsTable::CDRewardsTable(void) { - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards"); - while (!tableData.eof()) { - CDRewards* entry = new CDRewards(); - entry->id = tableData.getIntField("id", -1); - entry->levelID = tableData.getIntField("LevelID", -1); - entry->missionID = tableData.getIntField("MissionID", -1); - entry->rewardType = tableData.getIntField("RewardType", -1); - entry->value = tableData.getIntField("value", -1); - entry->count = tableData.getIntField("count", -1); - - m_entries.insert(std::make_pair(entry->id, entry)); - tableData.nextRow(); - } - - tableData.finalize(); -} - -CDRewardsTable::~CDRewardsTable(void) { - for (auto e : m_entries) { - if (e.second) delete e.second; - } - - m_entries.clear(); -} - -std::vector CDRewardsTable::GetByLevelID(uint32_t levelID) { - std::vector result{}; - for (const auto& e : m_entries) { - if (e.second->levelID == levelID) result.push_back(e.second); - } - - return result; -} - diff --git a/dDatabase/Tables/CDRewardsTable.h b/dDatabase/Tables/CDRewardsTable.h deleted file mode 100644 index 2e079a83..00000000 --- a/dDatabase/Tables/CDRewardsTable.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once -#include "CDTable.h" -#include - -struct CDRewards { - int32_t id; - int32_t levelID; - int32_t missionID; - int32_t rewardType; - int32_t value; - int32_t count; -}; - -class CDRewardsTable : public CDTable { -public: - CDRewardsTable(); - ~CDRewardsTable(); - - static const std::string GetTableName() { return "Rewards"; }; - std::vector GetByLevelID(uint32_t levelID); - -private: - std::map m_entries; -}; diff --git a/dDatabase/Tables/CDSkillBehaviorTable.h b/dDatabase/Tables/CDSkillBehaviorTable.h deleted file mode 100644 index eb3094e0..00000000 --- a/dDatabase/Tables/CDSkillBehaviorTable.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -// Custom Classes -#include "CDTable.h" - -struct CDSkillBehavior { - unsigned int skillID; //!< The Skill ID of the skill - UNUSED(unsigned int locStatus); //!< ?? - unsigned int behaviorID; //!< The Behavior ID of the skill - unsigned int imaginationcost; //!< The imagination cost of the skill - unsigned int cooldowngroup; //!< The cooldown group ID of the skill - float cooldown; //!< The cooldown time of the skill - UNUSED(bool isNpcEditor); //!< ??? - UNUSED(unsigned int skillIcon); //!< The Skill Icon ID - UNUSED(std::string oomSkillID); //!< ??? - UNUSED(unsigned int oomBehaviorEffectID); //!< ??? - UNUSED(unsigned int castTypeDesc); //!< The cast type description(?) - UNUSED(unsigned int imBonusUI); //!< The imagination bonus of the skill - UNUSED(nsigned int lifeBonusUI); //!< The life bonus of the skill - UNUSED(unsigned int armorBonusUI); //!< The armor bonus of the skill - UNUSED(unsigned int damageUI); //!< ??? - UNUSED(bool hideIcon); //!< Whether or not to show the icon - UNUSED(bool localize); //!< ??? - UNUSED(std::string gate_version); //!< ??? - UNUSED(unsigned int cancelType); //!< The cancel type (?) -}; - -class CDSkillBehaviorTable : public CDTable { -private: - std::map entries; - CDSkillBehavior m_empty; - -public: - CDSkillBehaviorTable(); - // Queries the table with a custom "where" clause - std::vector Query(std::function predicate); - - // Gets an entry by skillID - const CDSkillBehavior& GetSkillByID(unsigned int skillID); -}; - diff --git a/dDatabase/Tables/CDTable.h b/dDatabase/Tables/CDTable.h deleted file mode 100644 index e4c11fb9..00000000 --- a/dDatabase/Tables/CDTable.h +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include "CDClientDatabase.h" -#include "Singleton.h" -#include "DluAssert.h" - -#include -#include -#include -#include - -// CPPLinq -#ifdef _WIN32 -#define NOMINMAX -// windows.h has min and max macros that breaks cpplinq -#endif -#include "cpplinq.hpp" - -// Used for legacy -#define UNUSED(x) - -// Enable this to skip some unused columns in some tables -#define UNUSED_COLUMN(v) - -#pragma warning (disable : 4244) //Disable double to float conversion warnings -#pragma warning (disable : 4715) //Disable "not all control paths return a value" - -template -class CDTable : public Singleton
{ -protected: - virtual ~CDTable() = default; -}; - -template -class LookupResult { - typedef std::pair DataType; -public: - LookupResult() { m_data.first = T(); m_data.second = false; }; - LookupResult(T& data) { m_data.first = data; m_data.second = true; }; - inline const T& Data() { return m_data.first; }; - inline const bool& FoundData() { return m_data.second; }; -private: - DataType m_data; -}; diff --git a/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 80f16042..627f163a 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -2,64 +2,33 @@ set(DGAME_SOURCES "Character.cpp" "Entity.cpp" "EntityManager.cpp" "LeaderboardManager.cpp" - "Player.cpp" + "PlayerManager.cpp" "TeamManager.cpp" "TradingManager.cpp" "User.cpp" "UserManager.cpp") +include_directories( + ${PROJECT_SOURCE_DIR}/dScripts + ${PROJECT_SOURCE_DIR}/dGame +) + +add_library(dGameBase ${DGAME_SOURCES}) +target_precompile_headers(dGameBase PRIVATE ${HEADERS_DGAME}) +target_link_libraries(dGameBase + PUBLIC dDatabase dPhysics + INTERFACE dComponents dEntity) + add_subdirectory(dBehaviors) - -foreach(file ${DGAME_DBEHAVIORS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dBehaviors/${file}") -endforeach() - add_subdirectory(dComponents) - -foreach(file ${DGAME_DCOMPONENTS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dComponents/${file}") -endforeach() - add_subdirectory(dEntity) - -foreach(file ${DGAME_DENTITY_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dEntity/${file}") -endforeach() - add_subdirectory(dGameMessages) - -foreach(file ${DGAME_DGAMEMESSAGES_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dGameMessages/${file}") -endforeach() - add_subdirectory(dInventory) - -foreach(file ${DGAME_DINVENTORY_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dInventory/${file}") -endforeach() - add_subdirectory(dMission) - -foreach(file ${DGAME_DMISSION_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}") -endforeach() - add_subdirectory(dPropertyBehaviors) - -foreach(file ${DGAME_DPROPERTYBEHAVIORS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dPropertyBehaviors/${file}") -endforeach() - add_subdirectory(dUtilities) -foreach(file ${DGAME_DUTILITIES_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dUtilities/${file}") -endforeach() - -foreach(file ${DSCRIPTS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "${PROJECT_SOURCE_DIR}/dScripts/${file}") -endforeach() - -add_library(dGame STATIC ${DGAME_SOURCES}) - -target_link_libraries(dGame dDatabase Recast Detour) +add_library(dGame INTERFACE) +target_link_libraries(dGame INTERFACE + dGameBase dBehaviors dComponents dEntity dGameMessages dInventory dMission dPropertyBehaviors dUtilities dScripts +) diff --git a/dGame/Character.cpp b/dGame/Character.cpp index c5602bf2..eab7583f 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -2,7 +2,7 @@ #include "User.h" #include "Database.h" #include "GeneralUtils.h" -#include "dLogger.h" +#include "Logger.h" #include "BitStream.h" #include "Game.h" #include @@ -25,104 +25,36 @@ Character::Character(uint32_t id, User* parentUser) { //First load the name, etc: m_ID = id; - - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;" - ); - - stmt->setInt64(1, id); - - sql::ResultSet* res = stmt->executeQuery(); - - while (res->next()) { - m_Name = res->getString(1).c_str(); - m_UnapprovedName = res->getString(2).c_str(); - m_NameRejected = res->getBoolean(3); - m_PropertyCloneID = res->getUInt(4); - m_PermissionMap = static_cast(res->getUInt64(5)); - } - - delete res; - delete stmt; - - //Load the xmlData now: - sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt( - "SELECT xml_data FROM charxml WHERE id=? LIMIT 1;" - ); - - xmlStmt->setInt64(1, id); - - sql::ResultSet* xmlRes = xmlStmt->executeQuery(); - while (xmlRes->next()) { - m_XMLData = xmlRes->getString(1).c_str(); - } - - delete xmlRes; - delete xmlStmt; - - m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing. - 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(); - - //Set our objectID: - m_ObjectID = m_ID; - GeneralUtils::SetBit(m_ObjectID, eObjectBits::CHARACTER); - GeneralUtils::SetBit(m_ObjectID, eObjectBits::PERSISTENT); - m_ParentUser = parentUser; m_OurEntity = nullptr; - m_BuildMode = false; + m_Doc = nullptr; } Character::~Character() { - delete m_Doc; + if (m_Doc) delete m_Doc; m_Doc = nullptr; + m_OurEntity = nullptr; + m_ParentUser = nullptr; } -void Character::UpdateFromDatabase() { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "SELECT name, pending_name, needs_rename, prop_clone_id, permission_map FROM charinfo WHERE id=? LIMIT 1;" - ); +void Character::UpdateInfoFromDatabase() { + auto charInfo = Database::Get()->GetCharacterInfo(m_ID); - stmt->setInt64(1, m_ID); - - sql::ResultSet* res = stmt->executeQuery(); - - while (res->next()) { - m_Name = res->getString(1).c_str(); - m_UnapprovedName = res->getString(2).c_str(); - m_NameRejected = res->getBoolean(3); - m_PropertyCloneID = res->getUInt(4); - m_PermissionMap = static_cast(res->getUInt64(5)); + if (charInfo) { + m_Name = charInfo->name; + m_UnapprovedName = charInfo->pendingName; + m_NameRejected = charInfo->needsRename; + m_PropertyCloneID = charInfo->cloneId; + m_PermissionMap = charInfo->permissionMap; } - delete res; - delete stmt; - //Load the xmlData now: - sql::PreparedStatement* xmlStmt = Database::CreatePreppedStmt( - "SELECT xml_data FROM charxml WHERE id=? LIMIT 1;" - ); - xmlStmt->setInt64(1, m_ID); - - sql::ResultSet* xmlRes = xmlStmt->executeQuery(); - while (xmlRes->next()) { - m_XMLData = xmlRes->getString(1).c_str(); - } - - delete xmlRes; - delete xmlStmt; + m_XMLData = Database::Get()->GetCharacterXml(m_ID); m_ZoneID = 0; //TEMP! Set back to 0 when done. This is so we can see loading screen progress for testing. m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused. m_ZoneCloneID = 0; - delete m_Doc; m_Doc = nullptr; //Quickly and dirtly parse the xmlData to get the info we need: @@ -137,6 +69,11 @@ void Character::UpdateFromDatabase() { m_BuildMode = false; } +void Character::UpdateFromDatabase() { + if (m_Doc) delete m_Doc; + UpdateInfoFromDatabase(); +} + void Character::DoQuickXMLDataParse() { if (m_XMLData.size() == 0) return; @@ -145,16 +82,16 @@ void Character::DoQuickXMLDataParse() { if (!m_Doc) return; if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) { - Game::logger->Log("Character", "Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID); + LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID); } else { - Game::logger->Log("Character", "Failed to load xmlData!"); + LOG("Failed to load xmlData!"); //Server::rakServer->CloseConnection(m_ParentUser->GetSystemAddress(), true); return; } tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf"); if (!mf) { - Game::logger->Log("Character", "Failed to find mf tag!"); + LOG("Failed to find mf tag!"); return; } @@ -173,14 +110,14 @@ void Character::DoQuickXMLDataParse() { tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv"); if (!inv) { - Game::logger->Log("Character", "Char has no inv!"); + LOG("Char has no inv!"); return; } tinyxml2::XMLElement* bag = inv->FirstChildElement("items")->FirstChildElement("in"); if (!bag) { - Game::logger->Log("Character", "Couldn't find bag0!"); + LOG("Couldn't find bag0!"); return; } @@ -314,7 +251,7 @@ void Character::SaveXMLToDatabase() { auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); // lzid garbage, binary concat of zoneID, zoneInstance and zoneClone - if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) { + if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { uint64_t lzidConcat = zoneInfo.GetCloneID(); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID()); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID()); @@ -373,7 +310,7 @@ void Character::SaveXMLToDatabase() { //Call upon the entity to update our xmlDoc: if (!m_OurEntity) { - Game::logger->Log("Character", "%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str()); + LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str()); return; } @@ -384,7 +321,7 @@ void Character::SaveXMLToDatabase() { //For metrics, log the time it took to save: auto end = std::chrono::system_clock::now(); std::chrono::duration elapsed = end - start; - Game::logger->Log("Character", "%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count()); + LOG("%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count()); } void Character::SetIsNewLogin() { @@ -394,28 +331,23 @@ void Character::SetIsNewLogin() { auto* currentChild = flags->FirstChildElement(); while (currentChild) { + auto* nextChild = currentChild->NextSiblingElement(); if (currentChild->Attribute("si")) { flags->DeleteChild(currentChild); - Game::logger->Log("Character", "Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str()); + LOG("Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str()); WriteToDatabase(); } - currentChild = currentChild->NextSiblingElement(); + currentChild = nextChild; } } void Character::WriteToDatabase() { //Dump our xml into m_XMLData: - auto* printer = new tinyxml2::XMLPrinter(0, true, 0); - m_Doc->Print(printer); - m_XMLData = printer->CStr(); + tinyxml2::XMLPrinter printer(0, true, 0); + m_Doc->Print(&printer); //Finally, save to db: - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charxml SET xml_data=? WHERE id=?"); - stmt->setString(1, m_XMLData.c_str()); - stmt->setUInt(2, m_ID); - stmt->execute(); - delete stmt; - delete printer; + Database::Get()->UpdateCharacterXml(m_ID, printer.CStr()); } void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { @@ -519,7 +451,7 @@ void Character::LoadXmlRespawnCheckpoints() { auto* r = points->FirstChildElement("r"); while (r != nullptr) { int32_t map = 0; - NiPoint3 point = NiPoint3::ZERO; + NiPoint3 point = NiPoint3Constant::ZERO; r->QueryAttribute("w", &map); r->QueryAttribute("x", &point.x); @@ -554,15 +486,6 @@ void Character::OnZoneLoad() { return; } - /** - * Restrict old character to 1 million coins - */ - if (HasPermission(ePermissionMap::Old)) { - if (GetCoins() > 1000000) { - SetCoins(1000000, eLootSourceType::NONE); - } - } - auto* inventoryComponent = m_OurEntity->GetComponent(); if (inventoryComponent == nullptr) { @@ -590,7 +513,7 @@ void Character::SetRespawnPoint(LWOMAPID map, const NiPoint3& point) { const NiPoint3& Character::GetRespawnPoint(LWOMAPID map) const { const auto& pair = m_WorldRespawnCheckpoints.find(map); - if (pair == m_WorldRespawnCheckpoints.end()) return NiPoint3::ZERO; + if (pair == m_WorldRespawnCheckpoints.end()) return NiPoint3Constant::ZERO; return pair->second; } diff --git a/dGame/Character.h b/dGame/Character.h index 79ce0c0c..b994fb61 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -3,7 +3,7 @@ #include "dCommonVars.h" #include -#include "../thirdparty/tinyxml2/tinyxml2.h" +#include "tinyxml2.h" #include #include @@ -457,7 +457,10 @@ public: void SetBillboardVisible(bool visible); + User* GetParentUser() const { return m_ParentUser; } + private: + void UpdateInfoFromDatabase(); /** * The ID of this character. First 32 bits of the ObjectID. */ diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index e3bacb8b..16169887 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -2,8 +2,7 @@ #include "Entity.h" #include "CDClientManager.h" #include "Game.h" -#include "dLogger.h" -#include +#include "Logger.h" #include #include "CDDestructibleComponentTable.h" #include "CDClientDatabase.h" @@ -16,7 +15,6 @@ #include "Spawner.h" #include "UserManager.h" #include "dpWorld.h" -#include "Player.h" #include "LUTriggers.h" #include "User.h" #include "EntityTimer.h" @@ -25,12 +23,15 @@ #include "eMissionTaskType.h" #include "eTriggerEventType.h" #include "eObjectBits.h" +#include "PositionUpdate.h" +#include "eChatMessageType.h" +#include "PlayerManager.h" //Component includes: #include "Component.h" #include "ControllablePhysicsComponent.h" #include "RenderComponent.h" -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "CharacterComponent.h" #include "DestroyableComponent.h" #include "BuffComponent.h" @@ -47,7 +48,7 @@ #include "MovingPlatformComponent.h" #include "MissionComponent.h" #include "MissionOfferComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "BuildBorderComponent.h" #include "MovementAIComponent.h" #include "VendorComponent.h" @@ -62,7 +63,7 @@ #include "ModelComponent.h" #include "ZCompression.h" #include "PetComponent.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "PossessableComponent.h" #include "PossessorComponent.h" #include "ModuleAssemblyComponent.h" @@ -71,10 +72,16 @@ #include "ShootingGalleryComponent.h" #include "RailActivatorComponent.h" #include "LUPExhibitComponent.h" +#include "RacingSoundTriggerComponent.h" #include "TriggerComponent.h" #include "eGameMasterLevel.h" #include "eReplicaComponentType.h" #include "eReplicaPacketType.h" +#include "MiniGameControlComponent.h" +#include "RacingStatsComponent.h" +#include "CollectibleComponent.h" +#include "ItemComponent.h" +#include "GhostComponent.h" // Table includes #include "CDComponentsRegistryTable.h" @@ -88,13 +95,12 @@ #include "CDSkillBehaviorTable.h" #include "CDZoneTableTable.h" -Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) { +Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) { m_ObjectID = objectID; m_TemplateID = info.lot; m_ParentEntity = parentEntity; m_Character = nullptr; m_GMLevel = eGameMasterLevel::CIVILIAN; - m_CollectibleID = 0; m_NetworkID = 0; m_Groups = {}; m_OwnerOverride = LWOOBJID_EMPTY; @@ -118,9 +124,42 @@ Entity::Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity) m_SpawnerNodeID = info.spawnerNodeID; if (info.lot != 1) m_PlayerIsReadyForUpdates = true; + if (parentUser) { + m_Character = parentUser->GetLastUsedChar(); + parentUser->SetLoggedInChar(objectID); + m_GMLevel = m_Character->GetGMLevel(); + + m_Character->SetEntity(this); + + PlayerManager::AddPlayer(this); + } } Entity::~Entity() { + if (IsPlayer()) { + LOG("Deleted player"); + + // Make sure the player exists first. Remove afterwards to prevent the OnPlayerExist functions from not being able to find the player. + if (!PlayerManager::RemovePlayer(this)) { + LOG("Unable to find player to remove from manager."); + return; + } + + Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); + for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { + script->OnPlayerExit(zoneControl, this); + } + + std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); + for (Entity* scriptEntity : scriptedActs) { + if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds + for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { + script->OnPlayerExit(scriptEntity, this); + } + } + } + } + if (m_Character) { m_Character->SaveXMLToDatabase(); } @@ -152,7 +191,7 @@ void Entity::Initialize() { const auto triggerInfo = GetVarAsString(u"trigger_id"); - if (!triggerInfo.empty()) m_Components.emplace(eReplicaComponentType::TRIGGER, new TriggerComponent(this, triggerInfo)); + if (!triggerInfo.empty()) AddComponent(triggerInfo); /** * Setup groups @@ -162,7 +201,9 @@ void Entity::Initialize() { if (!groupIDs.empty()) { m_Groups = GeneralUtils::SplitString(groupIDs, ';'); - m_Groups.erase(m_Groups.end() - 1); + if (!m_Groups.empty()) { + if (m_Groups.back().empty()) m_Groups.erase(m_Groups.end() - 1); + } } /** @@ -174,7 +215,7 @@ void Entity::Initialize() { } // Get the registry table - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); /** * Special case for BBB models. They have components not corresponding to the registry. @@ -183,21 +224,17 @@ void Entity::Initialize() { if (m_TemplateID == 14) { const auto simplePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SIMPLE_PHYSICS); - SimplePhysicsComponent* comp = new SimplePhysicsComponent(simplePhysicsComponentID, this); - m_Components.insert(std::make_pair(eReplicaComponentType::SIMPLE_PHYSICS, comp)); + AddComponent(simplePhysicsComponentID); - ModelComponent* modelcomp = new ModelComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::MODEL, modelcomp)); + AddComponent(); - RenderComponent* render = new RenderComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::RENDER, render)); + AddComponent(); - auto destroyableComponent = new DestroyableComponent(this); + auto* destroyableComponent = AddComponent(); destroyableComponent->SetHealth(1); destroyableComponent->SetMaxHealth(1.0f); destroyableComponent->SetFaction(-1, true); destroyableComponent->SetIsSmashable(true); - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, destroyableComponent)); // We have all our components. return; } @@ -208,50 +245,47 @@ void Entity::Initialize() { * Not all components are implemented. Some are represented by a nullptr, as they hold no data. */ - if (GetParentUser()) { - auto missions = new MissionComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::MISSION, missions)); - missions->LoadFromXml(m_Character->GetXMLDoc()); + if (m_Character && m_Character->GetParentUser()) { + AddComponent()->LoadFromXml(m_Character->GetXMLDoc()); } uint32_t petComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PET); if (petComponentId > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::PET, new PetComponent(this, petComponentId))); + AddComponent(petComponentId); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ZONE_CONTROL) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::ZONE_CONTROL, nullptr)); + if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) { + AddComponent(); } uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE); if (possessableComponentId > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::POSSESSABLE, new PossessableComponent(this, possessableComponentId))); + AddComponent(possessableComponentId); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODULE_ASSEMBLY) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::MODULE_ASSEMBLY, new ModuleAssemblyComponent(this))); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_STATS) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RACING_STATS, nullptr)); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::LUP_EXHIBIT, -1) >= 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::LUP_EXHIBIT, new LUPExhibitComponent(this))); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_CONTROL) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RACING_CONTROL, new RacingControlComponent(this))); + AddComponent(); } const auto propertyEntranceComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_ENTRANCE); if (propertyEntranceComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::PROPERTY_ENTRANCE, - new PropertyEntranceComponent(propertyEntranceComponentID, this))); + AddComponent(propertyEntranceComponentID); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CONTROLLABLE_PHYSICS) > 0) { - ControllablePhysicsComponent* controllablePhysics = new ControllablePhysicsComponent(this); + auto* controllablePhysics = AddComponent(); if (m_Character) { controllablePhysics->LoadFromXml(m_Character->GetXMLDoc()); @@ -284,8 +318,6 @@ void Entity::Initialize() { controllablePhysics->SetPosition(m_DefaultPosition); controllablePhysics->SetRotation(m_DefaultRotation); } - - m_Components.insert(std::make_pair(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysics)); } // If an entity is marked a phantom, simple physics is made into phantom phyics. @@ -293,64 +325,56 @@ void Entity::Initialize() { const auto simplePhysicsComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SIMPLE_PHYSICS); if (!markedAsPhantom && simplePhysicsComponentID > 0) { - SimplePhysicsComponent* comp = new SimplePhysicsComponent(simplePhysicsComponentID, this); - m_Components.insert(std::make_pair(eReplicaComponentType::SIMPLE_PHYSICS, comp)); + AddComponent(simplePhysicsComponentID); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS) > 0) { - RigidbodyPhantomPhysicsComponent* comp = new RigidbodyPhantomPhysicsComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, comp)); + AddComponent(); } if (markedAsPhantom || compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PHANTOM_PHYSICS) > 0) { - PhantomPhysicsComponent* phantomPhysics = new PhantomPhysicsComponent(this); - phantomPhysics->SetPhysicsEffectActive(false); - m_Components.insert(std::make_pair(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysics)); + AddComponent()->SetPhysicsEffectActive(false); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VEHICLE_PHYSICS) > 0) { - VehiclePhysicsComponent* vehiclePhysicsComponent = new VehiclePhysicsComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)); - vehiclePhysicsComponent->SetPosition(m_DefaultPosition); - vehiclePhysicsComponent->SetRotation(m_DefaultRotation); + if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) > 0) { + auto* havokVehiclePhysicsComponent = AddComponent(); + havokVehiclePhysicsComponent->SetPosition(m_DefaultPosition); + havokVehiclePhysicsComponent->SetRotation(m_DefaultRotation); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) { - auto* comp = new SoundTriggerComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SOUND_TRIGGER, comp)); + AddComponent(); + } else if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1) != -1) { + AddComponent(); } - //Also check for the collectible id: - m_CollectibleID = GetVarAs(u"collectible_id"); - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF) > 0) { - BuffComponent* comp = new BuffComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::BUFF, comp)); + AddComponent(); } int collectibleComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::COLLECTIBLE); if (collectibleComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::COLLECTIBLE, nullptr)); + AddComponent(GetVarAs(u"collectible_id")); } /** * Multiple components require the destructible component. */ - int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF); - int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD); + int quickBuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD); - int componentID = 0; + int componentID = -1; if (collectibleComponentID > 0) componentID = collectibleComponentID; - if (rebuildComponentID > 0) componentID = rebuildComponentID; + if (quickBuildComponentID > 0) componentID = quickBuildComponentID; if (buffComponentID > 0) componentID = buffComponentID; - CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable(); + CDDestructibleComponentTable* destCompTable = CDClientManager::GetTable(); std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); - if (buffComponentID > 0 || collectibleComponentID > 0) { - DestroyableComponent* comp = new DestroyableComponent(this); + bool isSmashable = GetVarAs(u"is_smashable") != 0; + if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) { + DestroyableComponent* comp = AddComponent(); if (m_Character) { comp->LoadFromXml(m_Character->GetXMLDoc()); } else { @@ -369,16 +393,18 @@ void Entity::Initialize() { comp->SetMaxHealth(destCompData[0].life); comp->SetMaxImagination(destCompData[0].imagination); comp->SetMaxArmor(destCompData[0].armor); + comp->SetDeathBehavior(destCompData[0].death_behavior); comp->SetIsSmashable(destCompData[0].isSmashable); comp->SetLootMatrixID(destCompData[0].LootMatrixIndex); + Loot::CacheMatrix(destCompData[0].LootMatrixIndex); // Now get currency information uint32_t npcMinLevel = destCompData[0].level; uint32_t currencyIndex = destCompData[0].CurrencyIndex; - CDCurrencyTableTable* currencyTable = CDClientManager::Instance().GetTable(); + CDCurrencyTableTable* currencyTable = CDClientManager::GetTable(); std::vector currencyValues = currencyTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == currencyIndex && entry.npcminlevel == npcMinLevel); }); if (currencyValues.size() > 0) { @@ -388,7 +414,7 @@ void Entity::Initialize() { } // extraInfo overrides. Client ORs the database smashable and the luz smashable. - comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs(u"is_smashable") != 0)); + comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable); } } else { comp->SetHealth(1); @@ -421,42 +447,49 @@ void Entity::Initialize() { } } - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp)); + // override the factions if needed. + auto setFaction = GetVarAsString(u"set_faction"); + if (!setFaction.empty()) { + // TODO also split on space here however we do not have a general util for splitting on multiple characters yet. + std::vector factionsToAdd = GeneralUtils::SplitString(setFaction, ';'); + for (const auto faction : factionsToAdd) { + const auto factionToAdd = GeneralUtils::TryParse(faction); + if (factionToAdd) { + comp->AddFaction(factionToAdd.value(), true); + } + } + } } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::CHARACTER) > 0 || m_Character) { // Character Component always has a possessor, level, and forced movement components - m_Components.insert(std::make_pair(eReplicaComponentType::POSSESSOR, new PossessorComponent(this))); + AddComponent(); // load in the xml for the level - auto* levelComp = new LevelProgressionComponent(this); - levelComp->LoadFromXml(m_Character->GetXMLDoc()); - m_Components.insert(std::make_pair(eReplicaComponentType::LEVEL_PROGRESSION, levelComp)); + AddComponent()->LoadFromXml(m_Character->GetXMLDoc()); - m_Components.insert(std::make_pair(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, new PlayerForcedMovementComponent(this))); + AddComponent(); - CharacterComponent* charComp = new CharacterComponent(this, m_Character); - charComp->LoadFromXml(m_Character->GetXMLDoc()); - m_Components.insert(std::make_pair(eReplicaComponentType::CHARACTER, charComp)); + auto& systemAddress = m_Character->GetParentUser() ? m_Character->GetParentUser()->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS; + AddComponent(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc()); + + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { - InventoryComponent* comp = nullptr; - if (m_Character) comp = new InventoryComponent(this, m_Character->GetXMLDoc()); - else comp = new InventoryComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::INVENTORY, comp)); + auto* xmlDoc = m_Character ? m_Character->GetXMLDoc() : nullptr; + AddComponent(xmlDoc); } // if this component exists, then we initialize it. it's value is always 0 - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH_LUP, -1) != -1) { - auto comp = new RocketLaunchLupComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH_LUP, comp)); + if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) { + AddComponent(); } /** * This is a bit of a mess */ - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); + CDScriptComponentTable* scriptCompTable = CDClientManager::GetTable(); int32_t scriptComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPT, -1); std::string scriptName = ""; @@ -500,164 +533,159 @@ void Entity::Initialize() { } if (!scriptName.empty() || client || m_Character || scriptComponentID >= 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, new ScriptComponent(this, scriptName, true, client && scriptName.empty()))); + AddComponent(scriptName, true, client && scriptName.empty()); } // ZoneControl script if (m_TemplateID == 2365) { - CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable(); + CDZoneTableTable* zoneTable = CDClientManager::GetTable(); const auto zoneID = Game::zoneManager->GetZoneID(); const CDZoneTable* zoneData = zoneTable->Query(zoneID.GetMapID()); if (zoneData != nullptr) { int zoneScriptID = zoneData->scriptID; CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID); - - ScriptComponent* comp = new ScriptComponent(this, zoneScriptData.script_name, true); - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp)); + AddComponent(zoneScriptData.script_name, true); } } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SKILL, -1) != -1 || m_Character) { - SkillComponent* comp = new SkillComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SKILL, comp)); + AddComponent(); } const auto combatAiId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BASE_COMBAT_AI); if (combatAiId > 0) { - BaseCombatAIComponent* comp = new BaseCombatAIComponent(this, combatAiId); - m_Components.insert(std::make_pair(eReplicaComponentType::BASE_COMBAT_AI, comp)); + AddComponent(combatAiId); } if (int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) { - RebuildComponent* comp = new RebuildComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::QUICK_BUILD, comp)); + auto* quickBuildComponent = AddComponent(); - CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable(); - std::vector rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == rebuildComponentID); }); + CDRebuildComponentTable* rebCompTable = CDClientManager::GetTable(); + std::vector rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); }); if (rebCompData.size() > 0) { - comp->SetResetTime(rebCompData[0].reset_time); - comp->SetCompleteTime(rebCompData[0].complete_time); - comp->SetTakeImagination(rebCompData[0].take_imagination); - comp->SetInterruptible(rebCompData[0].interruptible); - comp->SetSelfActivator(rebCompData[0].self_activator); - comp->SetActivityId(rebCompData[0].activityID); - comp->SetPostImaginationCost(rebCompData[0].post_imagination_cost); - comp->SetTimeBeforeSmash(rebCompData[0].time_before_smash); + quickBuildComponent->SetResetTime(rebCompData[0].reset_time); + quickBuildComponent->SetCompleteTime(rebCompData[0].complete_time); + quickBuildComponent->SetTakeImagination(rebCompData[0].take_imagination); + quickBuildComponent->SetInterruptible(rebCompData[0].interruptible); + quickBuildComponent->SetSelfActivator(rebCompData[0].self_activator); + quickBuildComponent->SetActivityId(rebCompData[0].activityID); + quickBuildComponent->SetPostImaginationCost(rebCompData[0].post_imagination_cost); + quickBuildComponent->SetTimeBeforeSmash(rebCompData[0].time_before_smash); const auto rebuildResetTime = GetVar(u"rebuild_reset_time"); if (rebuildResetTime != 0.0f) { - comp->SetResetTime(rebuildResetTime); + quickBuildComponent->SetResetTime(rebuildResetTime); - if (m_TemplateID == 9483) // Look away! - { - comp->SetResetTime(comp->GetResetTime() + 25); + // Known bug with moving platform in FV that casues it to build at the end instead of the start. + // This extends the smash time so players can ride up the lift. + if (m_TemplateID == 9483) { + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() + 25); } } const auto activityID = GetVar(u"activityID"); if (activityID > 0) { - comp->SetActivityId(activityID); + quickBuildComponent->SetActivityId(activityID); + Loot::CacheMatrix(activityID); + } + + const auto timeBeforeSmash = GetVar(u"tmeSmsh"); + + if (timeBeforeSmash > 0) { + quickBuildComponent->SetTimeBeforeSmash(timeBeforeSmash); } const auto compTime = GetVar(u"compTime"); if (compTime > 0) { - comp->SetCompleteTime(compTime); + quickBuildComponent->SetCompleteTime(compTime); } } } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SWITCH, -1) != -1) { - SwitchComponent* comp = new SwitchComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::SWITCH, comp)); + AddComponent(); } if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) { - VendorComponent* comp = new VendorComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::VENDOR, comp)); + AddComponent(); } else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) { - DonationVendorComponent* comp = new DonationVendorComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::DONATION_VENDOR, comp)); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) { - auto* component = new PropertyVendorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROPERTY_VENDOR, component); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_MANAGEMENT, -1) != -1) { - auto* component = new PropertyManagementComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROPERTY_MANAGEMENT, component); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BOUNCER, -1) != -1) { // you have to determine it like this because all bouncers have a componentID of 0 - BouncerComponent* comp = new BouncerComponent(this); - m_Components.insert(std::make_pair(eReplicaComponentType::BOUNCER, comp)); + AddComponent(); } int32_t renderComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RENDER); if ((renderComponentId > 0 && m_TemplateID != 2365) || m_Character) { - RenderComponent* render = new RenderComponent(this, renderComponentId); - m_Components.insert(std::make_pair(eReplicaComponentType::RENDER, render)); + AddComponent(renderComponentId); } if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MISSION_OFFER) > 0) || m_Character) { - m_Components.insert(std::make_pair(eReplicaComponentType::MISSION_OFFER, new MissionOfferComponent(this, m_TemplateID))); + AddComponent(m_TemplateID); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUILD_BORDER, -1) != -1) { - m_Components.insert(std::make_pair(eReplicaComponentType::BUILD_BORDER, new BuildBorderComponent(this))); + AddComponent(); } // Scripted activity component - int scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY); - if ((scriptedActivityID > 0)) { - m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPTED_ACTIVITY, new ScriptedActivityComponent(this, scriptedActivityID))); + int scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1); + if ((scriptedActivityID != -1)) { + AddComponent(scriptedActivityID); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent()) { - m_Components.insert(std::make_pair(eReplicaComponentType::MODEL, new ModelComponent(this))); - if (m_Components.find(eReplicaComponentType::DESTROYABLE) == m_Components.end()) { - auto destroyableComponent = new DestroyableComponent(this); + AddComponent(); + if (!HasComponent(eReplicaComponentType::DESTROYABLE)) { + auto* destroyableComponent = AddComponent(); destroyableComponent->SetHealth(1); destroyableComponent->SetMaxHealth(1.0f); destroyableComponent->SetFaction(-1, true); destroyableComponent->SetIsSmashable(true); - m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, destroyableComponent)); } } PetComponent* petComponent; if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ITEM) > 0 && !TryGetComponent(eReplicaComponentType::PET, petComponent) && !HasComponent(eReplicaComponentType::MODEL)) { - m_Components.insert(std::make_pair(eReplicaComponentType::ITEM, nullptr)); + AddComponent(); } // Shooting gallery component if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SHOOTING_GALLERY) > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::SHOOTING_GALLERY, new ShootingGalleryComponent(this))); + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY, -1) != -1) { - m_Components.insert(std::make_pair(eReplicaComponentType::PROPERTY, new PropertyComponent(this))); + AddComponent(); } const int rocketId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH); if ((rocketId > 0)) { - m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH, new RocketLaunchpadControlComponent(this, rocketId))); + AddComponent(rocketId); } const int32_t railComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RAIL_ACTIVATOR); if (railComponentID > 0) { - m_Components.insert(std::make_pair(eReplicaComponentType::RAIL_ACTIVATOR, new RailActivatorComponent(this, railComponentID))); + AddComponent(railComponentID); } int movementAIID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVEMENT_AI); if (movementAIID > 0) { - CDMovementAIComponentTable* moveAITable = CDClientManager::Instance().GetTable(); + CDMovementAIComponentTable* moveAITable = CDClientManager::GetTable(); std::vector moveAIComp = moveAITable->Query([=](CDMovementAIComponent entry) {return (entry.id == movementAIID); }); if (moveAIComp.size() > 0) { @@ -680,7 +708,7 @@ void Entity::Initialize() { } } - m_Components.insert(std::make_pair(eReplicaComponentType::MOVEMENT_AI, new MovementAIComponent(this, moveInfo))); + AddComponent(moveInfo); } } else if (petComponentId > 0 || combatAiId > 0 && GetComponent()->GetTetherSpeed() > 0) { MovementAIInfo moveInfo = MovementAIInfo(); @@ -691,19 +719,18 @@ void Entity::Initialize() { moveInfo.wanderDelayMax = 5; moveInfo.wanderDelayMin = 2; - m_Components.insert(std::make_pair(eReplicaComponentType::MOVEMENT_AI, new MovementAIComponent(this, moveInfo))); + AddComponent(moveInfo); } std::string pathName = GetVarAsString(u"attached_path"); const Path* path = Game::zoneManager->GetZone()->GetPath(pathName); //Check to see if we have an attached path and add the appropiate component to handle it: - if (path){ + if (path) { // if we have a moving platform path, then we need a moving platform component if (path->pathType == PathType::MovingPlatform) { - MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName); - m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat)); - // else if we are a movement path + AddComponent(pathName); + // else if we are a movement path } /*else if (path->pathType == PathType::Movement) { auto movementAIcomp = GetComponent(); if (movementAIcomp){ @@ -716,19 +743,17 @@ void Entity::Initialize() { // 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); if (movingPlatformComponentId >= 0) { - MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName); - m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat)); + AddComponent(pathName); } } int proximityMonitorID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROXIMITY_MONITOR); if (proximityMonitorID > 0) { - CDProximityMonitorComponentTable* proxCompTable = CDClientManager::Instance().GetTable(); + CDProximityMonitorComponentTable* proxCompTable = CDClientManager::GetTable(); std::vector proxCompData = proxCompTable->Query([=](CDProximityMonitorComponent entry) { return (entry.id == proximityMonitorID); }); if (proxCompData.size() > 0) { std::vector proximityStr = GeneralUtils::SplitString(proxCompData[0].Proximities, ','); - ProximityMonitorComponent* comp = new ProximityMonitorComponent(this, std::stoi(proximityStr[0]), std::stoi(proximityStr[1])); - m_Components.insert(std::make_pair(eReplicaComponentType::PROXIMITY_MONITOR, comp)); + AddComponent(std::stoi(proximityStr[0]), std::stoi(proximityStr[1])); } } @@ -758,7 +783,7 @@ void Entity::Initialize() { !HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && !HasComponent(eReplicaComponentType::PROPERTY) && !HasComponent(eReplicaComponentType::RACING_CONTROL) && - !HasComponent(eReplicaComponentType::VEHICLE_PHYSICS) + !HasComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS) ) //if (HasComponent(eReplicaComponentType::BASE_COMBAT_AI)) { @@ -797,14 +822,6 @@ bool Entity::operator!=(const Entity& other) const { return other.m_ObjectID != m_ObjectID; } -User* Entity::GetParentUser() const { - if (!IsPlayer()) { - return nullptr; - } - - return static_cast(this)->GetParentUser(); -} - Component* Entity::GetComponent(eReplicaComponentType componentID) const { const auto& index = m_Components.find(componentID); @@ -819,14 +836,6 @@ bool Entity::HasComponent(const eReplicaComponentType componentId) const { return m_Components.find(componentId) != m_Components.end(); } -void Entity::AddComponent(const eReplicaComponentType componentId, Component* component) { - if (HasComponent(componentId)) { - return; - } - - m_Components.insert_or_assign(componentId, component); -} - std::vector Entity::GetScriptComponents() { std::vector comps; for (std::pair p : m_Components) { @@ -855,37 +864,36 @@ void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationNa } void Entity::SetProximityRadius(float proxRadius, std::string name) { - ProximityMonitorComponent* proxMon = GetComponent(); - if (!proxMon) { - proxMon = new ProximityMonitorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROXIMITY_MONITOR, proxMon); - } + auto* proxMon = GetComponent(); + if (!proxMon) proxMon = AddComponent(); proxMon->SetProximityRadius(proxRadius, name); } void Entity::SetProximityRadius(dpEntity* entity, std::string name) { - ProximityMonitorComponent* proxMon = GetComponent(); - if (!proxMon) { - proxMon = new ProximityMonitorComponent(this); - m_Components.insert_or_assign(eReplicaComponentType::PROXIMITY_MONITOR, proxMon); - } + ProximityMonitorComponent* proxMon = AddComponent(); proxMon->SetProximityRadius(entity, name); } void Entity::SetGMLevel(eGameMasterLevel value) { m_GMLevel = value; - if (GetParentUser()) { - Character* character = GetParentUser()->GetLastUsedChar(); + if (m_Character) m_Character->SetGMLevel(value); - if (character) { - character->SetGMLevel(value); - } - } + auto* characterComponent = GetComponent(); + if (!characterComponent) return; - CharacterComponent* character = GetComponent(); - if (character) character->SetGMLevel(value); + characterComponent->SetGMLevel(value); GameMessages::SendGMLevelBroadcast(m_ObjectID, value); + + // Update the chat server of our GM Level + { + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GMLEVEL_UPDATE); + bitStream.Write(m_ObjectID); + bitStream.Write(m_GMLevel); + + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); + } } void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType) { @@ -918,10 +926,20 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke outBitStream->Write1(); //ldf data RakNet::BitStream settingStream; - settingStream.Write(m_Settings.size()); + int32_t numberOfValidKeys = m_Settings.size(); + + // Writing keys value pairs the client does not expect to receive or interpret will result in undefined behavior, + // so we need to filter out any keys that are not valid and fix the number of valid keys to be correct. + // TODO should make this more efficient so that we dont waste loops evaluating the same condition twice + for (LDFBaseData* data : m_Settings) { + if (!data || data->GetValueType() == eLDFType::LDF_TYPE_UNKNOWN) { + numberOfValidKeys--; + } + } + settingStream.Write(numberOfValidKeys); for (LDFBaseData* data : m_Settings) { - if (data) { + if (data && data->GetValueType() != eLDFType::LDF_TYPE_UNKNOWN) { data->WriteToPacket(&settingStream); } } @@ -940,7 +958,6 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke RakNet::BitStream settingStream; settingStream.Write(ldfData.size()); - for (LDFBaseData* data : ldfData) { if (data) { data->WriteToPacket(&settingStream); @@ -1001,9 +1018,9 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke } outBitStream->Write(m_ChildEntities.size() > 0); if (m_ChildEntities.size() > 0) { - outBitStream->Write((uint16_t)m_ChildEntities.size()); + outBitStream->Write(m_ChildEntities.size()); for (Entity* child : m_ChildEntities) { - outBitStream->Write((uint64_t)child->GetObjectID()); + outBitStream->Write(child->GetObjectID()); } } } @@ -1016,73 +1033,77 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType */ bool destroyableSerialized = false; - bool bIsInitialUpdate = false; - if (packetType == eReplicaPacketType::CONSTRUCTION) bIsInitialUpdate = true; - unsigned int flags = 0; + bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION; PossessableComponent* possessableComponent; if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) { - possessableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + possessableComponent->Serialize(outBitStream, bIsInitialUpdate); } ModuleAssemblyComponent* moduleAssemblyComponent; if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) { - moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate); } ControllablePhysicsComponent* controllablePhysicsComponent; if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) { - controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); } SimplePhysicsComponent* simplePhysicsComponent; if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) { - simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); } RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics; if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) { - rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate, flags); + rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate); } - VehiclePhysicsComponent* vehiclePhysicsComponent; - if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) { - vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent; + if (TryGetComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, havokVehiclePhysicsComponent)) { + havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); } PhantomPhysicsComponent* phantomPhysicsComponent; if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) { - phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); } SoundTriggerComponent* soundTriggerComponent; if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) { - soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate); + } + + RacingSoundTriggerComponent* racingSoundTriggerComponent; + if (TryGetComponent(eReplicaComponentType::RACING_SOUND_TRIGGER, racingSoundTriggerComponent)) { + racingSoundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate); } BuffComponent* buffComponent; if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) { - buffComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + buffComponent->Serialize(outBitStream, bIsInitialUpdate); DestroyableComponent* destroyableComponent; if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + destroyableComponent->Serialize(outBitStream, bIsInitialUpdate); } destroyableSerialized = true; } - if (HasComponent(eReplicaComponentType::COLLECTIBLE)) { + CollectibleComponent* collectibleComponent; + if (TryGetComponent(eReplicaComponentType::COLLECTIBLE, collectibleComponent)) { DestroyableComponent* destroyableComponent; if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + destroyableComponent->Serialize(outBitStream, bIsInitialUpdate); } destroyableSerialized = true; - outBitStream->Write(m_CollectibleID); // Collectable component + collectibleComponent->Serialize(outBitStream, bIsInitialUpdate); } PetComponent* petComponent; if (TryGetComponent(eReplicaComponentType::PET, petComponent)) { - petComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + petComponent->Serialize(outBitStream, bIsInitialUpdate); } CharacterComponent* characterComponent; @@ -1090,7 +1111,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType PossessorComponent* possessorComponent; if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) { - possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + possessorComponent->Serialize(outBitStream, bIsInitialUpdate); } else { // Should never happen, but just to be safe outBitStream->Write0(); @@ -1098,7 +1119,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType LevelProgressionComponent* levelProgressionComponent; if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) { - levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate); } else { // Should never happen, but just to be safe outBitStream->Write0(); @@ -1106,114 +1127,116 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType PlayerForcedMovementComponent* playerForcedMovementComponent; if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) { - playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate); } else { // Should never happen, but just to be safe outBitStream->Write0(); } - characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + characterComponent->Serialize(outBitStream, bIsInitialUpdate); } - if (HasComponent(eReplicaComponentType::ITEM)) { - outBitStream->Write0(); + ItemComponent* itemComponent; + if (TryGetComponent(eReplicaComponentType::ITEM, itemComponent)) { + itemComponent->Serialize(outBitStream, bIsInitialUpdate); } InventoryComponent* inventoryComponent; if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) { - inventoryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + inventoryComponent->Serialize(outBitStream, bIsInitialUpdate); } ScriptComponent* scriptComponent; if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) { - scriptComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + scriptComponent->Serialize(outBitStream, bIsInitialUpdate); } SkillComponent* skillComponent; if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) { - skillComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + skillComponent->Serialize(outBitStream, bIsInitialUpdate); } BaseCombatAIComponent* baseCombatAiComponent; if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) { - baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate); } - RebuildComponent* rebuildComponent; - if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, rebuildComponent)) { + QuickBuildComponent* quickBuildComponent; + if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, quickBuildComponent)) { DestroyableComponent* destroyableComponent; if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + destroyableComponent->Serialize(outBitStream, bIsInitialUpdate); } destroyableSerialized = true; - rebuildComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + quickBuildComponent->Serialize(outBitStream, bIsInitialUpdate); } MovingPlatformComponent* movingPlatformComponent; if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) { - movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate); } SwitchComponent* switchComponent; if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) { - switchComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + switchComponent->Serialize(outBitStream, bIsInitialUpdate); } VendorComponent* vendorComponent; if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) { - vendorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + vendorComponent->Serialize(outBitStream, bIsInitialUpdate); } DonationVendorComponent* donationVendorComponent; if (TryGetComponent(eReplicaComponentType::DONATION_VENDOR, donationVendorComponent)) { - donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate); } BouncerComponent* bouncerComponent; if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) { - bouncerComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + bouncerComponent->Serialize(outBitStream, bIsInitialUpdate); } ScriptedActivityComponent* scriptedActivityComponent; if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) { - scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate); } ShootingGalleryComponent* shootingGalleryComponent; if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) { - shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate); } RacingControlComponent* racingControlComponent; if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) { - racingControlComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + racingControlComponent->Serialize(outBitStream, bIsInitialUpdate); } LUPExhibitComponent* lupExhibitComponent; if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) { - lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate); } ModelComponent* modelComponent; if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) { - modelComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + modelComponent->Serialize(outBitStream, bIsInitialUpdate); } RenderComponent* renderComponent; if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) { - renderComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + renderComponent->Serialize(outBitStream, bIsInitialUpdate); } - if (modelComponent) { + if (modelComponent || !destroyableSerialized) { DestroyableComponent* destroyableComponent; if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) { - destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags); + destroyableComponent->Serialize(outBitStream, bIsInitialUpdate); destroyableSerialized = true; } } - if (HasComponent(eReplicaComponentType::ZONE_CONTROL)) { - outBitStream->Write(0x40000000); + MiniGameControlComponent* miniGameControlComponent; + if (TryGetComponent(eReplicaComponentType::MINI_GAME_CONTROL, miniGameControlComponent)) { + miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate); } // BBB Component, unused currently @@ -1222,10 +1245,6 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType outBitStream->Write0(); } -void Entity::ResetFlags() { - // Unused -} - 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. @@ -1239,39 +1258,56 @@ void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { void Entity::Update(const float deltaTime) { uint32_t timerPosition; - timerPosition = 0; - while (timerPosition < m_Timers.size()) { - m_Timers[timerPosition]->Update(deltaTime); - if (m_Timers[timerPosition]->GetTime() <= 0) { - const auto timerName = m_Timers[timerPosition]->GetName(); - - delete m_Timers[timerPosition]; + for (timerPosition = 0; timerPosition < m_Timers.size();) { + auto& timer = m_Timers[timerPosition]; + timer.Update(deltaTime); + // If the timer is expired, erase it and dont increment the position because the next timer will be at the same position. + // Before: [0, 1, 2, 3, ..., n] + // timerPosition ^ + // After: [0, 1, 3, ..., n] + // timerPosition ^ + if (timer.GetTime() <= 0) { + // Remove the timer from the list of timers first so that scripts and events can remove timers without causing iterator invalidation + auto timerName = timer.GetName(); m_Timers.erase(m_Timers.begin() + timerPosition); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(this)) { script->OnTimerDone(this, timerName); } + TriggerEvent(eTriggerEventType::TIMER_DONE, this); + } else { + // If the timer isnt expired, go to the next timer. + timerPosition++; + } + } + + for (timerPosition = 0; timerPosition < m_CallbackTimers.size(); ) { + // If the timer is expired, erase it and dont increment the position because the next timer will be at the same position. + // Before: [0, 1, 2, 3, ..., n] + // timerPosition ^ + // After: [0, 1, 3, ..., n] + // timerPosition ^ + auto& callbackTimer = m_CallbackTimers[timerPosition]; + callbackTimer.Update(deltaTime); + if (callbackTimer.GetTime() <= 0) { + // Remove the timer from the list of timers first so that callbacks can remove timers without causing iterator invalidation + auto callback = callbackTimer.GetCallback(); + m_CallbackTimers.erase(m_CallbackTimers.begin() + timerPosition); + callback(); } else { timerPosition++; } } - for (int i = 0; i < m_CallbackTimers.size(); i++) { - m_CallbackTimers[i]->Update(deltaTime); - if (m_CallbackTimers[i]->GetTime() <= 0) { - m_CallbackTimers[i]->GetCallback()(); - delete m_CallbackTimers[i]; - m_CallbackTimers.erase(m_CallbackTimers.begin() + i); - } + // Add pending timers to the list of timers so they start next tick. + if (!m_PendingTimers.empty()) { + m_Timers.insert(m_Timers.end(), m_PendingTimers.begin(), m_PendingTimers.end()); + m_PendingTimers.clear(); } - // Add pending timers to the list of timers so they start next tick. - if (m_PendingTimers.size() > 0) { - for (auto namedTimer : m_PendingTimers) { - m_Timers.push_back(namedTimer); - } - m_PendingTimers.clear(); + if (!m_PendingCallbackTimers.empty()) { + m_CallbackTimers.insert(m_CallbackTimers.end(), m_PendingCallbackTimers.begin(), m_PendingCallbackTimers.end()); + m_PendingCallbackTimers.clear(); } if (IsSleeping()) { @@ -1342,17 +1378,11 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) { } if (!other->GetIsDead()) { - auto* combat = GetComponent(); - - if (combat != nullptr) { + if (GetComponent() != nullptr) { const auto index = std::find(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), otherEntity); if (index != m_TargetsInPhantom.end()) return; - const auto valid = combat->IsEnemy(otherEntity); - - if (!valid) return; - m_TargetsInPhantom.push_back(otherEntity); } } @@ -1527,7 +1557,7 @@ void Entity::Smash(const LWOOBJID source, const eKillType killType, const std::u destroyableComponent->Smash(source, killType, deathType); } -void Entity::Kill(Entity* murderer) { +void Entity::Kill(Entity* murderer, const eKillType killType) { if (!m_PlayerIsReadyForUpdates) return; for (const auto& cb : m_DieCallbacks) { @@ -1547,7 +1577,17 @@ void Entity::Kill(Entity* murderer) { } if (!IsPlayer()) { - Game::entityManager->DestroyEntity(this); + auto* destroyableComponent = GetComponent(); + bool waitForDeathAnimation = false; + + if (destroyableComponent) { + waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT; + } + + // Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction! + constexpr float DelayDeathTime = 12.0f; + if (waitForDeathAnimation) AddCallbackTimer(DelayDeathTime, [this]() { Game::entityManager->DestroyEntity(this); }); + else Game::entityManager->DestroyEntity(this); } const auto& grpNameQBShowBricks = GetVar(u"grpNameQBShowBricks"); @@ -1595,10 +1635,10 @@ void Entity::AddCollisionPhantomCallback(const std::function& callback) const { - auto* rebuildComponent = GetComponent(); - if (rebuildComponent != nullptr) { - rebuildComponent->AddRebuildCompleteCallback(callback); +void Entity::AddQuickBuildCompleteCallback(const std::function& callback) const { + auto* quickBuildComponent = GetComponent(); + if (quickBuildComponent != nullptr) { + quickBuildComponent->AddQuickBuildCompleteCallback(callback); } } @@ -1611,18 +1651,23 @@ bool Entity::GetIsDead() const { void Entity::AddLootItem(const Loot::Info& info) { if (!IsPlayer()) return; - auto& droppedLoot = static_cast(this)->GetDroppedLoot(); + + auto* characterComponent = GetComponent(); + if (!characterComponent) return; + + auto& droppedLoot = characterComponent->GetDroppedLoot(); droppedLoot.insert(std::make_pair(info.id, info)); } void Entity::PickupItem(const LWOOBJID& objectID) { if (!IsPlayer()) return; InventoryComponent* inv = GetComponent(); - if (!inv) return; + auto* characterComponent = GetComponent(); + if (!inv || !characterComponent) return; - CDObjectsTable* objectsTable = CDClientManager::Instance().GetTable(); + CDObjectsTable* objectsTable = CDClientManager::GetTable(); - auto& droppedLoot = static_cast(this)->GetDroppedLoot(); + auto& droppedLoot = characterComponent->GetDroppedLoot(); for (const auto& p : droppedLoot) { if (p.first == objectID) { @@ -1633,10 +1678,10 @@ void Entity::PickupItem(const LWOOBJID& objectID) { const CDObjects& object = objectsTable->GetByID(p.second.lot); if (object.id != 0 && object.type == "Powerup") { - CDObjectSkillsTable* skillsTable = CDClientManager::Instance().GetTable(); + CDObjectSkillsTable* skillsTable = CDClientManager::GetTable(); std::vector skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); }); for (CDObjectSkills skill : skills) { - CDSkillBehaviorTable* skillBehTable = CDClientManager::Instance().GetTable(); + CDSkillBehaviorTable* skillBehTable = CDClientManager::GetTable(); CDSkillBehavior behaviorData = skillBehTable->GetSkillByID(skill.skillID); SkillComponent::HandleUnmanaged(behaviorData.behaviorID, GetObjectID()); @@ -1658,22 +1703,28 @@ void Entity::PickupItem(const LWOOBJID& objectID) { bool Entity::CanPickupCoins(uint64_t count) { if (!IsPlayer()) return false; - auto* player = static_cast(this); - auto droppedCoins = player->GetDroppedCoins(); + + auto* characterComponent = GetComponent(); + if (!characterComponent) return false; + + auto droppedCoins = characterComponent->GetDroppedCoins(); if (count > droppedCoins) { return false; } else { - player->SetDroppedCoins(droppedCoins - count); + characterComponent->SetDroppedCoins(droppedCoins - count); return true; } } void Entity::RegisterCoinDrop(uint64_t count) { if (!IsPlayer()) return; - auto* player = static_cast(this); - auto droppedCoins = player->GetDroppedCoins(); + + auto* characterComponent = GetComponent(); + if (!characterComponent) return; + + auto droppedCoins = characterComponent->GetDroppedCoins(); droppedCoins += count; - player->SetDroppedCoins(droppedCoins); + characterComponent->SetDroppedCoins(droppedCoins); } void Entity::AddChild(Entity* child) { @@ -1699,31 +1750,20 @@ void Entity::RemoveParent() { } void Entity::AddTimer(std::string name, float time) { - EntityTimer* timer = new EntityTimer(name, time); - m_PendingTimers.push_back(timer); + m_PendingTimers.emplace_back(name, time); } void Entity::AddCallbackTimer(float time, std::function callback) { - EntityCallbackTimer* timer = new EntityCallbackTimer(time, callback); - m_CallbackTimers.push_back(timer); + m_PendingCallbackTimers.emplace_back(time, callback); } bool Entity::HasTimer(const std::string& name) { - for (auto* timer : m_Timers) { - if (timer->GetName() == name) { - return true; - } - } - - return false; + return std::find(m_Timers.begin(), m_Timers.end(), name) != m_Timers.end(); } void Entity::CancelCallbackTimers() { - for (auto* callback : m_CallbackTimers) { - delete callback; - } - m_CallbackTimers.clear(); + m_PendingCallbackTimers.clear(); } void Entity::ScheduleKillAfterUpdate(Entity* murderer) { @@ -1735,8 +1775,8 @@ void Entity::ScheduleKillAfterUpdate(Entity* murderer) { void Entity::CancelTimer(const std::string& name) { for (int i = 0; i < m_Timers.size(); i++) { - if (m_Timers[i]->GetName() == name) { - delete m_Timers[i]; + auto& timer = m_Timers[i]; + if (timer == name) { m_Timers.erase(m_Timers.begin() + i); return; } @@ -1744,21 +1784,10 @@ void Entity::CancelTimer(const std::string& name) { } void Entity::CancelAllTimers() { - /*for (auto timer : m_Timers) { - if (timer) delete timer; - }*/ - - for (auto* timer : m_Timers) { - delete timer; - } - m_Timers.clear(); - - for (auto* callBackTimer : m_CallbackTimers) { - delete callBackTimer; - } - + m_PendingTimers.clear(); m_CallbackTimers.clear(); + m_PendingCallbackTimers.clear(); } bool Entity::IsPlayer() const { @@ -1854,13 +1883,13 @@ const NiPoint3& Entity::GetPosition() const { return simple->GetPosition(); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { return vehicel->GetPosition(); } - return NiPoint3::ZERO; + return NiPoint3Constant::ZERO; } const NiQuaternion& Entity::GetRotation() const { @@ -1882,16 +1911,16 @@ const NiQuaternion& Entity::GetRotation() const { return simple->GetRotation(); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { return vehicel->GetRotation(); } - return NiQuaternion::IDENTITY; + return NiQuaternionConstant::IDENTITY; } -void Entity::SetPosition(NiPoint3 position) { +void Entity::SetPosition(const NiPoint3& position) { auto* controllable = GetComponent(); if (controllable != nullptr) { @@ -1910,7 +1939,7 @@ void Entity::SetPosition(NiPoint3 position) { simple->SetPosition(position); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { vehicel->SetPosition(position); @@ -1919,7 +1948,7 @@ void Entity::SetPosition(NiPoint3 position) { Game::entityManager->SerializeEntity(this); } -void Entity::SetRotation(NiQuaternion rotation) { +void Entity::SetRotation(const NiQuaternion& rotation) { auto* controllable = GetComponent(); if (controllable != nullptr) { @@ -1938,7 +1967,7 @@ void Entity::SetRotation(NiQuaternion rotation) { simple->SetRotation(rotation); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { vehicel->SetRotation(rotation); @@ -1989,25 +2018,21 @@ void Entity::SetNetworkId(const uint16_t id) { m_NetworkID = id; } -std::vector& Entity::GetTargetsInPhantom() { - std::vector valid; - +std::vector Entity::GetTargetsInPhantom() { // Clean up invalid targets, like disconnected players - for (auto i = 0u; i < m_TargetsInPhantom.size(); ++i) { - const auto id = m_TargetsInPhantom.at(i); + m_TargetsInPhantom.erase(std::remove_if(m_TargetsInPhantom.begin(), m_TargetsInPhantom.end(), [](const LWOOBJID id) { + return !Game::entityManager->GetEntity(id); + }), m_TargetsInPhantom.end()); - auto* entity = Game::entityManager->GetEntity(id); + std::vector enemies; + for (const auto id : m_TargetsInPhantom) { + auto* combat = GetComponent(); + if (!combat || !combat->IsEnemy(id)) continue; - if (entity == nullptr) { - continue; - } - - valid.push_back(id); + enemies.push_back(id); } - m_TargetsInPhantom = valid; - - return m_TargetsInPhantom; + return enemies; } void Entity::SendNetworkVar(const std::string& data, const SystemAddress& sysAddr) { @@ -2063,3 +2088,104 @@ void Entity::RetroactiveVaultSize() { modelVault->SetSize(itemsVault->GetSize()); } + +uint8_t Entity::GetCollectibleID() const { + auto* collectible = GetComponent(); + return collectible ? collectible->GetCollectibleId() : 0; +} + +void Entity::ProcessPositionUpdate(PositionUpdate& update) { + if (!IsPlayer()) return; + auto* controllablePhysicsComponent = GetComponent(); + if (!controllablePhysicsComponent) return; + + auto* possessorComponent = GetComponent(); + bool updateChar = true; + + if (possessorComponent) { + auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); + + if (possassableEntity) { + auto* possessableComponent = possassableEntity->GetComponent(); + + // While possessing something, only update char if we are attached to the thing we are possessing + updateChar = possessableComponent && possessableComponent->GetPossessionType() == ePossessionType::ATTACHED_VISIBLE; + + auto* havokVehiclePhysicsComponent = possassableEntity->GetComponent(); + if (havokVehiclePhysicsComponent) { + havokVehiclePhysicsComponent->SetPosition(update.position); + havokVehiclePhysicsComponent->SetRotation(update.rotation); + havokVehiclePhysicsComponent->SetIsOnGround(update.onGround); + havokVehiclePhysicsComponent->SetIsOnRail(update.onRail); + havokVehiclePhysicsComponent->SetVelocity(update.velocity); + havokVehiclePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); + havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity); + havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); + havokVehiclePhysicsComponent->SetRemoteInputInfo(update.remoteInputInfo); + } else { + // Need to get the mount's controllable physics + auto* possessedControllablePhysicsComponent = possassableEntity->GetComponent(); + if (!possessedControllablePhysicsComponent) return; + possessedControllablePhysicsComponent->SetPosition(update.position); + possessedControllablePhysicsComponent->SetRotation(update.rotation); + possessedControllablePhysicsComponent->SetIsOnGround(update.onGround); + possessedControllablePhysicsComponent->SetIsOnRail(update.onRail); + possessedControllablePhysicsComponent->SetVelocity(update.velocity); + possessedControllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); + possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); + } + Game::entityManager->SerializeEntity(possassableEntity); + } + } + + if (!updateChar) { + update.velocity = NiPoint3Constant::ZERO; + update.angularVelocity = NiPoint3Constant::ZERO; + } + + // Handle statistics + auto* characterComponent = GetComponent(); + if (characterComponent) { + characterComponent->TrackPositionUpdate(update.position); + } + + controllablePhysicsComponent->SetPosition(update.position); + controllablePhysicsComponent->SetRotation(update.rotation); + controllablePhysicsComponent->SetIsOnGround(update.onGround); + controllablePhysicsComponent->SetIsOnRail(update.onRail); + controllablePhysicsComponent->SetVelocity(update.velocity); + controllablePhysicsComponent->SetDirtyVelocity(update.velocity != NiPoint3Constant::ZERO); + controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3Constant::ZERO); + + auto* ghostComponent = GetComponent(); + if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position); + Game::entityManager->QueueGhostUpdate(GetObjectID()); + + if (updateChar) Game::entityManager->SerializeEntity(this); +} + +const SystemAddress& Entity::GetSystemAddress() const { + auto* characterComponent = GetComponent(); + return characterComponent ? characterComponent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS; +} + +const NiPoint3& Entity::GetRespawnPosition() const { + auto* characterComponent = GetComponent(); + return characterComponent ? characterComponent->GetRespawnPosition() : NiPoint3Constant::ZERO; +} + +const NiQuaternion& Entity::GetRespawnRotation() const { + auto* characterComponent = GetComponent(); + return characterComponent ? characterComponent->GetRespawnRotation() : NiQuaternionConstant::IDENTITY; +} + +void Entity::SetRespawnPos(const NiPoint3& position) { + auto* characterComponent = GetComponent(); + if (characterComponent) characterComponent->SetRespawnPos(position); +} +void Entity::SetRespawnRot(const NiQuaternion& rotation) { + auto* characterComponent = GetComponent(); + if (characterComponent) characterComponent->SetRespawnRot(rotation); +} diff --git a/dGame/Entity.h b/dGame/Entity.h index ca007912..6546e458 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -31,6 +31,7 @@ class Component; class Item; class Character; class EntityCallbackTimer; +class PositionUpdate; enum class eTriggerEventType; enum class eGameMasterLevel : uint8_t; enum class eReplicaComponentType : uint32_t; @@ -46,10 +47,10 @@ namespace CppScripts { */ class Entity { public: - explicit Entity(const LWOOBJID& objectID, EntityInfo info, Entity* parentEntity = nullptr); - virtual ~Entity(); + explicit Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser = nullptr, Entity* parentEntity = nullptr); + ~Entity(); - virtual void Initialize(); + void Initialize(); bool operator==(const Entity& other) const; bool operator!=(const Entity& other) const; @@ -66,7 +67,7 @@ public: eGameMasterLevel GetGMLevel() const { return m_GMLevel; } - uint8_t GetCollectibleID() const { return uint8_t(m_CollectibleID); } + uint8_t GetCollectibleID() const; Entity* GetParentEntity() const { return m_ParentEntity; } @@ -103,9 +104,7 @@ public: const NiQuaternion& GetRotation() const; - virtual User* GetParentUser() const; - - virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; + const SystemAddress& GetSystemAddress() const; /** * Setters @@ -123,15 +122,13 @@ public: void SetNetworkId(uint16_t id); - void SetPosition(NiPoint3 position); + void SetPosition(const NiPoint3& position); - void SetRotation(NiQuaternion rotation); + void SetRotation(const NiQuaternion& rotation); - virtual void SetRespawnPos(NiPoint3 position) {} + void SetRespawnPos(const NiPoint3& position); - virtual void SetRespawnRot(NiQuaternion rotation) {} - - virtual void SetSystemAddress(const SystemAddress& value) {}; + void SetRespawnRot(const NiQuaternion& rotation); /** * Component management @@ -160,6 +157,8 @@ public: void AddChild(Entity* child); void RemoveChild(Entity* child); void RemoveParent(); + + // Adds a timer to start next frame with the given name and time. void AddTimer(std::string name, float time); void AddCallbackTimer(float time, std::function callback); bool HasTimer(const std::string& name); @@ -174,7 +173,6 @@ public: void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType); void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType); - void ResetFlags(); void UpdateXMLDoc(tinyxml2::XMLDocument* doc); void Update(float deltaTime); @@ -211,8 +209,8 @@ public: void RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled); void Smash(const LWOOBJID source = LWOOBJID_EMPTY, const eKillType killType = eKillType::VIOLENT, const std::u16string& deathType = u""); - void Kill(Entity* murderer = nullptr); - void AddRebuildCompleteCallback(const std::function& callback) const; + void Kill(Entity* murderer = nullptr, const eKillType killType = eKillType::SILENT); + void AddQuickBuildCompleteCallback(const std::function& callback) const; void AddCollisionPhantomCallback(const std::function& callback); void AddDieCallback(const std::function& callback); void Resurrect(); @@ -227,8 +225,8 @@ public: void TriggerEvent(eTriggerEventType event, Entity* optionalTarget = nullptr); void ScheduleDestructionAfterUpdate() { m_ShouldDestroyAfterUpdate = true; } - virtual NiPoint3 GetRespawnPosition() const { return NiPoint3::ZERO; } - virtual NiQuaternion GetRespawnRotation() const { return NiQuaternion::IDENTITY; } + const NiPoint3& GetRespawnPosition() const; + const NiQuaternion& GetRespawnRotation() const; void Sleep(); void Wake(); @@ -275,6 +273,9 @@ public: template T GetVarAs(const std::u16string& name) const; + template + ComponentType* AddComponent(VaArgs... args); + /** * Get the LDF data. */ @@ -288,10 +289,12 @@ public: /* * Collision */ - std::vector& GetTargetsInPhantom(); + std::vector GetTargetsInPhantom(); Entity* GetScheduledKiller() { return m_ScheduleKiller; } + void ProcessPositionUpdate(PositionUpdate& update); + protected: LWOOBJID m_ObjectID; @@ -322,9 +325,10 @@ protected: std::vector> m_PhantomCollisionCallbacks; std::unordered_map m_Components; - std::vector m_Timers; - std::vector m_PendingTimers; - std::vector m_CallbackTimers; + std::vector m_Timers; + std::vector m_PendingTimers; + std::vector m_CallbackTimers; + std::vector m_PendingCallbackTimers; bool m_ShouldDestroyAfterUpdate = false; @@ -391,14 +395,8 @@ const T& Entity::GetVar(const std::u16string& name) const { template T Entity::GetVarAs(const std::u16string& name) const { const auto data = GetVarAsString(name); - - T value; - - if (!GeneralUtils::TryParse(data, value)) { - return LDFData::Default; - } - - return value; + + return GeneralUtils::TryParse(data).value_or(LDFData::Default); } template @@ -502,3 +500,36 @@ T Entity::GetNetworkVar(const std::u16string& name) { return LDFData::Default; } + +/** + * @brief Adds a component of type ComponentType to this entity and forwards the arguments to the constructor. + * + * @tparam ComponentType The component class type to add. Must derive from Component. + * @tparam VaArgs The argument types to forward to the constructor. + * @param args The arguments to forward to the constructor. The first argument passed to the ComponentType constructor will be this entity. + * @return ComponentType* The added component. Will never return null. + */ +template +inline ComponentType* Entity::AddComponent(VaArgs... args) { + static_assert(std::is_base_of_v, "ComponentType must be a Component"); + + // Get the component if it already exists, or default construct a nullptr + auto*& componentToReturn = m_Components[ComponentType::ComponentType]; + + // If it doesn't exist, create it and forward the arguments to the constructor + if (!componentToReturn) { + componentToReturn = new ComponentType(this, std::forward(args)...); + } else { + // In this case the block is already allocated and ready for use + // so we use a placement new to construct the component again as was requested by the caller. + // Placement new means we already have memory allocated for the object, so this just calls its constructor again. + // This is useful for when we want to create a new object in the same memory location as an old one. + componentToReturn->~Component(); + new(componentToReturn) ComponentType(this, std::forward(args)...); + } + + // Finally return the created or already existing component. + // Because of the assert above, this should always be a ComponentType* but I need a way to guarantee the map cannot be modifed outside this function + // To allow a static cast here instead of a dynamic one. + return dynamic_cast(componentToReturn); +} diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index 4f1b7373..c3c3d1b5 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -2,21 +2,19 @@ #include "RakNetTypes.h" #include "Game.h" #include "User.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "Character.h" #include "GeneralUtils.h" #include "dServer.h" #include "Spawner.h" -#include "Player.h" #include "SkillComponent.h" #include "SwitchComponent.h" #include "UserManager.h" -#include "PacketUtils.h" #include "Metrics.hpp" #include "dZoneManager.h" #include "MissionComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "MessageIdentifiers.h" #include "dConfig.h" #include "eTriggerEventType.h" @@ -24,6 +22,8 @@ #include "eGameMasterLevel.h" #include "eReplicaComponentType.h" #include "eReplicaPacketType.h" +#include "PlayerManager.h" +#include "GhostComponent.h" // Configure which zones have ghosting disabled, mostly small worlds. std::vector EntityManager::m_GhostingExcludedZones = { @@ -47,10 +47,6 @@ std::vector EntityManager::m_GhostingExcludedZones = { // Configure some exceptions for ghosting, nessesary for some special objects. std::vector EntityManager::m_GhostingExcludedLOTs = { - // NT - Pipes - 9524, - 12408, - // AG - Footrace 4967 }; @@ -93,7 +89,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE // Entities with no ID already set, often spawned entities, we'll generate a new sequencial ID if (info.id == 0) { - id = ObjectIDManager::Instance()->GenerateObjectID(); + id = ObjectIDManager::GenerateObjectID(); } // Entities with an ID already set, often level entities, we'll use that ID as a base @@ -121,14 +117,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE info.id = id; - Entity* entity; - - // Check if the entitty if a player, in case use the extended player entity class - if (user != nullptr) { - entity = new Player(id, info, user, parentEntity); - } else { - entity = new Entity(id, info, parentEntity); - } + Entity* entity = new Entity(id, info, user, parentEntity); // Initialize the entity entity->Initialize(); @@ -176,23 +165,25 @@ void EntityManager::DestroyEntity(Entity* entity) { } void EntityManager::SerializeEntities() { - for (auto entry = m_EntitiesToSerialize.begin(); entry != m_EntitiesToSerialize.end(); entry++) { - auto* entity = GetEntity(*entry); + for (int32_t i = 0; i < m_EntitiesToSerialize.size(); i++) { + const LWOOBJID toSerialize = m_EntitiesToSerialize.at(i); + auto* entity = GetEntity(toSerialize); if (!entity) continue; m_SerializationCounter++; RakNet::BitStream stream; - stream.Write(static_cast(ID_REPLICA_MANAGER_SERIALIZE)); - stream.Write(static_cast(entity->GetNetworkId())); + stream.Write(ID_REPLICA_MANAGER_SERIALIZE); + stream.Write(entity->GetNetworkId()); entity->WriteBaseReplicaData(&stream, eReplicaPacketType::SERIALIZATION); entity->WriteComponents(&stream, eReplicaPacketType::SERIALIZATION); if (entity->GetIsGhostingCandidate()) { - for (auto* player : Player::GetAllPlayers()) { - if (player->IsObserved(*entry)) { + for (auto* player : PlayerManager::GetAllPlayers()) { + auto* ghostComponent = player->GetComponent(); + if (ghostComponent && ghostComponent->IsObserved(toSerialize)) { Game::server->Send(&stream, player->GetSystemAddress(), false); } } @@ -204,11 +195,12 @@ void EntityManager::SerializeEntities() { } void EntityManager::KillEntities() { - for (auto entry = m_EntitiesToKill.begin(); entry != m_EntitiesToKill.end(); entry++) { - auto* entity = GetEntity(*entry); + for (int32_t i = 0; i < m_EntitiesToKill.size(); i++) { + const LWOOBJID toKill = m_EntitiesToKill.at(i); + auto* entity = GetEntity(toKill); if (!entity) { - Game::logger->Log("EntityManager", "Attempting to kill null entity %llu", *entry); + LOG("Attempting to kill null entity %llu", toKill); continue; } @@ -222,8 +214,9 @@ void EntityManager::KillEntities() { } void EntityManager::DeleteEntities() { - for (auto entry = m_EntitiesToDelete.begin(); entry != m_EntitiesToDelete.end(); entry++) { - auto entityToDelete = GetEntity(*entry); + for (int32_t i = 0; i < m_EntitiesToDelete.size(); i++) { + const LWOOBJID toDelete = m_EntitiesToDelete.at(i); + auto entityToDelete = GetEntity(toDelete); if (entityToDelete) { // Get all this info first before we delete the player. auto networkIdToErase = entityToDelete->GetNetworkId(); @@ -237,9 +230,9 @@ void EntityManager::DeleteEntities() { if (ghostingToDelete != m_EntitiesToGhost.end()) m_EntitiesToGhost.erase(ghostingToDelete); } else { - Game::logger->Log("EntityManager", "Attempted to delete non-existent entity %llu", *entry); + LOG("Attempted to delete non-existent entity %llu", toDelete); } - m_Entities.erase(*entry); + m_Entities.erase(toDelete); } m_EntitiesToDelete.clear(); } @@ -298,6 +291,16 @@ std::vector EntityManager::GetEntitiesByLOT(const LOT& lot) const { return entities; } +std::vector EntityManager::GetEntitiesByProximity(NiPoint3 reference, float radius) const{ + std::vector entities = {}; + if (radius > 1000.0f) return entities; + for (const auto& entity : m_Entities) { + if (NiPoint3::Distance(reference, entity.second->GetPosition()) <= radius) entities.push_back(entity.second); + } + return entities; +} + + Entity* EntityManager::GetZoneControlEntity() const { return m_ZoneControlEntity; } @@ -320,7 +323,7 @@ const std::unordered_map& EntityManager::GetSpawnPointEnt void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr, const bool skipChecks) { if (!entity) { - Game::logger->Log("EntityManager", "Attempted to construct null entity"); + LOG("Attempted to construct null entity"); return; } @@ -357,9 +360,9 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr RakNet::BitStream stream; - stream.Write(static_cast(ID_REPLICA_MANAGER_CONSTRUCTION)); + stream.Write(ID_REPLICA_MANAGER_CONSTRUCTION); stream.Write(true); - stream.Write(static_cast(entity->GetNetworkId())); + stream.Write(entity->GetNetworkId()); entity->WriteBaseReplicaData(&stream, eReplicaPacketType::CONSTRUCTION); entity->WriteComponents(&stream, eReplicaPacketType::CONSTRUCTION); @@ -368,11 +371,12 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr if (skipChecks) { Game::server->Send(&stream, UNASSIGNED_SYSTEM_ADDRESS, true); } else { - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { if (player->GetPlayerReadyForUpdates()) { Game::server->Send(&stream, player->GetSystemAddress(), false); } else { - player->AddLimboConstruction(entity->GetObjectID()); + auto* ghostComponent = player->GetComponent(); + if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID()); } } } @@ -380,8 +384,6 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr Game::server->Send(&stream, sysAddr, false); } - // PacketUtils::SavePacket("[24]_"+std::to_string(entity->GetObjectID()) + "_" + std::to_string(m_SerializationCounter) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed()); - if (entity->IsPlayer()) { if (entity->GetGMLevel() > eGameMasterLevel::CIVILIAN) { GameMessages::SendToggleGMInvis(entity->GetObjectID(), true, sysAddr); @@ -399,7 +401,7 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) { } } - UpdateGhosting(Player::GetPlayer(sysAddr)); + UpdateGhosting(PlayerManager::GetPlayer(sysAddr)); } void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) { @@ -407,14 +409,15 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) RakNet::BitStream stream; - stream.Write(static_cast(ID_REPLICA_MANAGER_DESTRUCTION)); - stream.Write(static_cast(entity->GetNetworkId())); + stream.Write(ID_REPLICA_MANAGER_DESTRUCTION); + stream.Write(entity->GetNetworkId()); Game::server->Send(&stream, sysAddr, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { if (!player->GetPlayerReadyForUpdates()) { - player->RemoveLimboConstruction(entity->GetObjectID()); + auto* ghostComponent = player->GetComponent(); + if (ghostComponent) ghostComponent->RemoveLimboConstruction(entity->GetObjectID()); } } } @@ -425,8 +428,6 @@ void EntityManager::SerializeEntity(Entity* entity) { if (std::find(m_EntitiesToSerialize.begin(), m_EntitiesToSerialize.end(), entity->GetObjectID()) == m_EntitiesToSerialize.end()) { m_EntitiesToSerialize.push_back(entity->GetObjectID()); } - - //PacketUtils::SavePacket(std::to_string(m_SerializationCounter) + "_[27]_"+std::to_string(entity->GetObjectID()) + ".bin", (char*)stream.GetData(), stream.GetNumberOfBytesUsed()); } void EntityManager::DestructAllEntities(const SystemAddress& sysAddr) { @@ -461,7 +462,7 @@ void EntityManager::QueueGhostUpdate(LWOOBJID playerID) { void EntityManager::UpdateGhosting() { for (const auto playerID : m_PlayersToUpdateGhosting) { - auto* player = Player::GetPlayer(playerID); + auto* player = PlayerManager::GetPlayer(playerID); if (player == nullptr) { continue; @@ -473,19 +474,20 @@ void EntityManager::UpdateGhosting() { m_PlayersToUpdateGhosting.clear(); } -void EntityManager::UpdateGhosting(Player* player) { +void EntityManager::UpdateGhosting(Entity* player) { if (player == nullptr) { return; } auto* missionComponent = player->GetComponent(); + auto* ghostComponent = player->GetComponent(); - if (missionComponent == nullptr) { + if (missionComponent == nullptr || !ghostComponent) { return; } - const auto& referencePoint = player->GetGhostReferencePoint(); - const auto isOverride = player->GetGhostOverride(); + const auto& referencePoint = ghostComponent->GetGhostReferencePoint(); + const auto isOverride = ghostComponent->GetGhostOverride(); for (auto* entity : m_EntitiesToGhost) { const auto isAudioEmitter = entity->GetLOT() == 6368; @@ -494,7 +496,7 @@ void EntityManager::UpdateGhosting(Player* player) { const int32_t id = entity->GetObjectID(); - const auto observed = player->IsObserved(id); + const auto observed = ghostComponent->IsObserved(id); const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint); @@ -506,7 +508,7 @@ void EntityManager::UpdateGhosting(Player* player) { } if (observed && distance > ghostingDistanceMax && !isOverride) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); @@ -523,7 +525,7 @@ void EntityManager::UpdateGhosting(Player* player) { } } - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); @@ -544,23 +546,26 @@ void EntityManager::CheckGhosting(Entity* entity) { const auto isAudioEmitter = entity->GetLOT() == 6368; - for (auto* player : Player::GetAllPlayers()) { - const auto& entityPoint = player->GetGhostReferencePoint(); + for (auto* player : PlayerManager::GetAllPlayers()) { + auto* ghostComponent = player->GetComponent(); + if (!ghostComponent) continue; + + const auto& entityPoint = ghostComponent->GetGhostReferencePoint(); const int32_t id = entity->GetObjectID(); - const auto observed = player->IsObserved(id); + const auto observed = ghostComponent->IsObserved(id); const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint); if (observed && distance > ghostingDistanceMax) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); entity->SetObservers(entity->GetObservers() - 1); } else if (!observed && ghostingDistanceMin > distance) { - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); @@ -583,12 +588,6 @@ bool EntityManager::GetGhostingEnabled() const { return m_GhostingEnabled; } -void EntityManager::ResetFlags() { - for (const auto& e : m_Entities) { - e.second->ResetFlags(); - } -} - void EntityManager::ScheduleForKill(Entity* entity) { // Deactivate switches if they die if (!entity) diff --git a/dGame/EntityManager.h b/dGame/EntityManager.h index 9bea0618..352b927b 100644 --- a/dGame/EntityManager.h +++ b/dGame/EntityManager.h @@ -28,6 +28,7 @@ public: std::vector GetEntitiesInGroup(const std::string& group); std::vector GetEntitiesByComponent(eReplicaComponentType componentType) const; std::vector GetEntitiesByLOT(const LOT& lot) const; + std::vector GetEntitiesByProximity(NiPoint3 reference, float radius) const; Entity* GetZoneControlEntity() const; // Get spawn point entity by spawn name @@ -54,13 +55,11 @@ public: float GetGhostDistanceMin() const; void QueueGhostUpdate(LWOOBJID playerID); void UpdateGhosting(); - void UpdateGhosting(Player* player); + void UpdateGhosting(Entity* player); void CheckGhosting(Entity* entity); Entity* GetGhostCandidate(int32_t id); bool GetGhostingEnabled() const; - void ResetFlags(); - void ScheduleForKill(Entity* entity); void ScheduleForDeletion(LWOOBJID entity); diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index 0e64b6b3..ba9055a7 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -8,7 +8,7 @@ #include "Character.h" #include "Game.h" #include "GameMessages.h" -#include "dLogger.h" +#include "Logger.h" #include "dConfig.h" #include "CDClientManager.h" #include "GeneralUtils.h" @@ -193,7 +193,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking WHERE leaderboardsRanked.ranking BETWEEN - LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9) + LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), CAST(lowestRanking.lowestRank AS SIGNED) - 9) AND LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank) ORDER BY ranking ASC; @@ -236,8 +236,8 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r baseLookup += std::to_string(static_cast(this->relatedPlayer)); } baseLookup += " LIMIT 1"; - Game::logger->LogDebug("LeaderboardManager", "query is %s", baseLookup.c_str()); - std::unique_ptr baseQuery(Database::CreatePreppedStmt(baseLookup)); + LOG_DEBUG("query is %s", baseLookup.c_str()); + std::unique_ptr baseQuery(Database::Get()->CreatePreppedStmt(baseLookup)); baseQuery->setInt(1, this->gameID); std::unique_ptr baseResult(baseQuery->executeQuery()); @@ -250,8 +250,8 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r std::unique_ptr lookupBuffer = std::make_unique(STRING_LENGTH); int32_t res = snprintf(lookupBuffer.get(), STRING_LENGTH, queryBase.c_str(), orderBase.data(), filter.c_str(), resultStart, resultEnd); DluAssert(res != -1); - std::unique_ptr query(Database::CreatePreppedStmt(lookupBuffer.get())); - Game::logger->LogDebug("LeaderboardManager", "Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId); + std::unique_ptr query(Database::Get()->CreatePreppedStmt(lookupBuffer.get())); + LOG_DEBUG("Query is %s vars are %i %i %i", lookupBuffer.get(), this->gameID, this->relatedPlayer, relatedPlayerLeaderboardId); query->setInt(1, this->gameID); if (this->infoType == InfoType::Friends) { query->setInt(2, this->relatedPlayer); @@ -301,7 +301,7 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) { const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId); - std::unique_ptr query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;")); + std::unique_ptr query(Database::Get()->CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;")); query->setInt(1, playerID); query->setInt(2, activityId); std::unique_ptr myScoreResult(query->executeQuery()); @@ -358,7 +358,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi } case Leaderboard::Type::None: default: - Game::logger->Log("LeaderboardManager", "Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId); + LOG("Unknown leaderboard type %i for game %i. Cannot save score!", leaderboardType, activityId); return; } bool newHighScore = lowerScoreBetter ? newScore < oldScore : newScore > oldScore; @@ -377,15 +377,15 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi } else { saveQuery = FormatInsert(leaderboardType, newScore, false); } - Game::logger->Log("LeaderboardManager", "save query %s %i %i", saveQuery.c_str(), playerID, activityId); - std::unique_ptr saveStatement(Database::CreatePreppedStmt(saveQuery)); + LOG("save query %s %i %i", saveQuery.c_str(), playerID, activityId); + std::unique_ptr saveStatement(Database::Get()->CreatePreppedStmt(saveQuery)); saveStatement->setInt(1, playerID); saveStatement->setInt(2, activityId); saveStatement->execute(); // track wins separately if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) { - std::unique_ptr winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;")); + std::unique_ptr winUpdate(Database::Get()->CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;")); winUpdate->setInt(1, playerID); winUpdate->setInt(2, activityId); winUpdate->execute(); @@ -402,7 +402,7 @@ Leaderboard::Type LeaderboardManager::GetLeaderboardType(const GameID gameID) { auto lookup = leaderboardCache.find(gameID); if (lookup != leaderboardCache.end()) return lookup->second; - auto* activitiesTable = CDClientManager::Instance().GetTable(); + auto* activitiesTable = CDClientManager::GetTable(); std::vector activities = activitiesTable->Query([gameID](const CDActivities& entry) { return entry.ActivityID == gameID; }); diff --git a/dGame/LeaderboardManager.h b/dGame/LeaderboardManager.h index e2ce3f97..89537ba0 100644 --- a/dGame/LeaderboardManager.h +++ b/dGame/LeaderboardManager.h @@ -6,7 +6,6 @@ #include #include -#include "Singleton.h" #include "dCommonVars.h" #include "LDFFormat.h" diff --git a/dGame/Player.cpp b/dGame/Player.cpp deleted file mode 100644 index 48b983aa..00000000 --- a/dGame/Player.cpp +++ /dev/null @@ -1,304 +0,0 @@ -#include "Player.h" - -#include - -#include "Character.h" -#include "Database.h" -#include "MissionComponent.h" -#include "UserManager.h" -#include "EntityManager.h" -#include "dLogger.h" -#include "ZoneInstanceManager.h" -#include "WorldPackets.h" -#include "dZoneManager.h" -#include "CharacterComponent.h" -#include "Mail.h" -#include "User.h" -#include "CppScripts.h" -#include "Loot.h" -#include "eReplicaComponentType.h" - -std::vector Player::m_Players = {}; - -Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Entity* parentEntity) : Entity(objectID, info, parentEntity) { - m_ParentUser = user; - m_Character = m_ParentUser->GetLastUsedChar(); - m_ParentUser->SetLoggedInChar(objectID); - m_GMLevel = m_Character->GetGMLevel(); - m_SystemAddress = m_ParentUser->GetSystemAddress(); - m_DroppedLoot = {}; - m_DroppedCoins = 0; - - m_GhostReferencePoint = NiPoint3::ZERO; - m_GhostOverridePoint = NiPoint3::ZERO; - m_GhostOverride = false; - m_ObservedEntitiesLength = 256; - m_ObservedEntitiesUsed = 0; - m_ObservedEntities.resize(m_ObservedEntitiesLength); - - m_Character->SetEntity(this); - - const auto& iter = std::find(m_Players.begin(), m_Players.end(), this); - - if (iter != m_Players.end()) { - return; - } - - m_Players.push_back(this); -} - -User* Player::GetParentUser() const { - return m_ParentUser; -} - -SystemAddress Player::GetSystemAddress() const { - return m_SystemAddress; -} - -void Player::SetSystemAddress(const SystemAddress& value) { - m_SystemAddress = value; -} - -void Player::SetRespawnPos(const NiPoint3 position) { - m_respawnPos = position; - - m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position); -} - -void Player::SetRespawnRot(const NiQuaternion rotation) { - m_respawnRot = rotation; -} - -NiPoint3 Player::GetRespawnPosition() const { - return m_respawnPos; -} - -NiQuaternion Player::GetRespawnRotation() const { - return m_respawnRot; -} - -void Player::SendMail(const LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const { - Mail::SendMail(sender, senderName, this, subject, body, attachment, attachmentCount); -} - -void Player::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) { - const auto objid = GetObjectID(); - - ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - auto* entity = Game::entityManager->GetEntity(objid); - - if (entity == nullptr) { - return; - } - - const auto sysAddr = entity->GetSystemAddress(); - - auto* character = entity->GetCharacter(); - auto* characterComponent = entity->GetComponent(); - - if (character != nullptr && characterComponent != nullptr) { - character->SetZoneID(zoneID); - character->SetZoneInstance(zoneInstance); - character->SetZoneClone(zoneClone); - - characterComponent->SetLastRocketConfig(u""); - - character->SaveXMLToDatabase(); - } - - WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); - - Game::entityManager->DestructEntity(entity); - return; - }); -} - -void Player::AddLimboConstruction(LWOOBJID objectId) { - const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId); - - if (iter != m_LimboConstructions.end()) { - return; - } - - m_LimboConstructions.push_back(objectId); -} - -void Player::RemoveLimboConstruction(LWOOBJID objectId) { - const auto& iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId); - - if (iter == m_LimboConstructions.end()) { - return; - } - - m_LimboConstructions.erase(iter); -} - -void Player::ConstructLimboEntities() { - for (const auto objectId : m_LimboConstructions) { - auto* entity = Game::entityManager->GetEntity(objectId); - - if (entity == nullptr) { - continue; - } - - Game::entityManager->ConstructEntity(entity, m_SystemAddress); - } - - m_LimboConstructions.clear(); -} - -std::map& Player::GetDroppedLoot() { - return m_DroppedLoot; -} - -const NiPoint3& Player::GetGhostReferencePoint() const { - return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; -} - -const NiPoint3& Player::GetOriginGhostReferencePoint() const { - return m_GhostReferencePoint; -} - -void Player::SetGhostReferencePoint(const NiPoint3& value) { - m_GhostReferencePoint = value; -} - -void Player::SetGhostOverridePoint(const NiPoint3& value) { - m_GhostOverridePoint = value; -} - -const NiPoint3& Player::GetGhostOverridePoint() const { - return m_GhostOverridePoint; -} - -void Player::SetGhostOverride(bool value) { - m_GhostOverride = value; -} - -bool Player::GetGhostOverride() const { - return m_GhostOverride; -} - -void Player::ObserveEntity(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == 0 || m_ObservedEntities[i] == id) { - m_ObservedEntities[i] = id; - - return; - } - } - - const auto index = m_ObservedEntitiesUsed++; - - if (m_ObservedEntitiesUsed > m_ObservedEntitiesLength) { - m_ObservedEntities.resize(m_ObservedEntitiesLength + m_ObservedEntitiesLength); - - m_ObservedEntitiesLength = m_ObservedEntitiesLength + m_ObservedEntitiesLength; - } - - m_ObservedEntities[index] = id; -} - -bool Player::IsObserved(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == id) { - return true; - } - } - - return false; -} - -void Player::GhostEntity(int32_t id) { - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - if (m_ObservedEntities[i] == id) { - m_ObservedEntities[i] = 0; - } - } -} - -Player* Player::GetPlayer(const SystemAddress& sysAddr) { - auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity(); - - return static_cast(entity); -} - -Player* Player::GetPlayer(const std::string& name) { - const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); - - for (auto* character : characters) { - if (!character->IsPlayer()) continue; - - if (character->GetCharacter()->GetName() == name) { - return static_cast(character); - } - } - - return nullptr; -} - -Player* Player::GetPlayer(LWOOBJID playerID) { - for (auto* player : m_Players) { - if (player->GetObjectID() == playerID) { - return player; - } - } - - return nullptr; -} - -const std::vector& Player::GetAllPlayers() { - return m_Players; -} - -uint64_t Player::GetDroppedCoins() { - return m_DroppedCoins; -} - -void Player::SetDroppedCoins(uint64_t value) { - m_DroppedCoins = value; -} - -Player::~Player() { - Game::logger->Log("Player", "Deleted player"); - - for (int32_t i = 0; i < m_ObservedEntitiesUsed; i++) { - const auto id = m_ObservedEntities[i]; - - if (id == 0) { - continue; - } - - auto* entity = Game::entityManager->GetGhostCandidate(id); - - if (entity != nullptr) { - entity->SetObservers(entity->GetObservers() - 1); - } - } - - m_LimboConstructions.clear(); - - const auto& iter = std::find(m_Players.begin(), m_Players.end(), this); - - if (iter == m_Players.end()) { - return; - } - - if (IsPlayer()) { - Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { - script->OnPlayerExit(zoneControl, this); - } - - std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPTED_ACTIVITY); - for (Entity* scriptEntity : scriptedActs) { - if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds - for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { - script->OnPlayerExit(scriptEntity, this); - } - } - } - } - - m_Players.erase(iter); -} diff --git a/dGame/Player.h b/dGame/Player.h deleted file mode 100644 index 287ee613..00000000 --- a/dGame/Player.h +++ /dev/null @@ -1,136 +0,0 @@ -#pragma once - -#include "Entity.h" - -/** - * Extended Entity for player data and behavior. - * - * Contains properties only a player entity would require, like associated SystemAddress and User. - * - * Keeps track of which entities are observed by this user for ghosting. - */ -class Player final : public Entity -{ -public: - explicit Player(const LWOOBJID& objectID, EntityInfo info, User* user, Entity* parentEntity = nullptr); - - /** - * Getters - */ - - User* GetParentUser() const override; - - SystemAddress GetSystemAddress() const override; - - NiPoint3 GetRespawnPosition() const override; - - NiQuaternion GetRespawnRotation() const override; - - const NiPoint3& GetGhostReferencePoint() const; - - const NiPoint3& GetOriginGhostReferencePoint() const; - - const NiPoint3& GetGhostOverridePoint() const; - - bool GetGhostOverride() const; - - std::map& GetDroppedLoot(); - - uint64_t GetDroppedCoins(); - - /** - * Setters - */ - - void SetSystemAddress(const SystemAddress& value) override; - - void SetRespawnPos(NiPoint3 position) override; - - void SetRespawnRot(NiQuaternion rotation) override; - - void SetGhostReferencePoint(const NiPoint3& value); - - void SetGhostOverridePoint(const NiPoint3& value); - - void SetGhostOverride(bool value); - - void SetDroppedCoins(uint64_t value); - - /** - * Wrapper for sending an in-game mail. - * - * @param sender id of the sender. LWOOBJID_EMPTY for system mail - * @param senderName name of the sender. Max 32 characters. - * @param subject mail subject. Max 50 characters. - * @param body mail body. Max 400 characters. - * @param attachment LOT of the attached item. LOT_NULL if no attachment. - * @param attachmentCount stack size for attachment. - */ - void SendMail(LWOOBJID sender, const std::string& senderName, const std::string& subject, const std::string& body, LOT attachment, uint16_t attachmentCount) const; - - /** - * Wrapper for transfering the player to another instance. - * - * @param zoneId zoneID for the new instance. - * @param cloneId cloneID for the new instance. - */ - void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0); - - /** - * Ghosting - */ - - void AddLimboConstruction(LWOOBJID objectId); - - void RemoveLimboConstruction(LWOOBJID objectId); - - void ConstructLimboEntities(); - - void ObserveEntity(int32_t id); - - bool IsObserved(int32_t id); - - void GhostEntity(int32_t id); - - /** - * Static methods - */ - - static Player* GetPlayer(const SystemAddress& sysAddr); - - static Player* GetPlayer(const std::string& name); - - static Player* GetPlayer(LWOOBJID playerID); - - static const std::vector& GetAllPlayers(); - - ~Player() override; -private: - SystemAddress m_SystemAddress; - - NiPoint3 m_respawnPos; - - NiQuaternion m_respawnRot; - - User* m_ParentUser; - - NiPoint3 m_GhostReferencePoint; - - NiPoint3 m_GhostOverridePoint; - - bool m_GhostOverride; - - std::vector m_ObservedEntities; - - int32_t m_ObservedEntitiesLength; - - int32_t m_ObservedEntitiesUsed; - - std::vector m_LimboConstructions; - - std::map m_DroppedLoot; - - uint64_t m_DroppedCoins; - - static std::vector m_Players; -}; diff --git a/dGame/PlayerManager.cpp b/dGame/PlayerManager.cpp new file mode 100644 index 00000000..61e2568d --- /dev/null +++ b/dGame/PlayerManager.cpp @@ -0,0 +1,67 @@ +#include "PlayerManager.h" + +#include "Character.h" +#include "User.h" +#include "UserManager.h" +#include "eReplicaComponentType.h" + +namespace { + std::vector m_Players; +}; + +const std::vector& PlayerManager::GetAllPlayers() { + return m_Players; +} + +void PlayerManager::AddPlayer(Entity* player) { + const auto& iter = std::find(m_Players.begin(), m_Players.end(), player); + + if (iter == m_Players.end()) { + m_Players.push_back(player); + } +} + +bool PlayerManager::RemovePlayer(Entity* player) { + const auto iter = std::find(m_Players.begin(), m_Players.end(), player); + + const bool toReturn = iter != m_Players.end(); + if (toReturn) { + m_Players.erase(iter); + } + + return toReturn; +} + +Entity* PlayerManager::GetPlayer(const SystemAddress& sysAddr) { + auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity(); + + return entity; +} + +Entity* PlayerManager::GetPlayer(const std::string& name) { + const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); + + Entity* player = nullptr; + for (auto* character : characters) { + if (!character->IsPlayer()) continue; + + if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) { + player = character; + break; + } + } + + return player; +} + +Entity* PlayerManager::GetPlayer(LWOOBJID playerID) { + Entity* playerToReturn = nullptr; + for (auto* player : m_Players) { + if (player->GetObjectID() == playerID) { + playerToReturn = player; + break; + } + } + + return playerToReturn; +} diff --git a/dGame/PlayerManager.h b/dGame/PlayerManager.h new file mode 100644 index 00000000..7f6be5ba --- /dev/null +++ b/dGame/PlayerManager.h @@ -0,0 +1,25 @@ +#ifndef __PLAYERMANAGER__H__ +#define __PLAYERMANAGER__H__ + +#include "dCommonVars.h" + +#include + +class Entity; +struct SystemAddress; + +namespace PlayerManager { + void AddPlayer(Entity* player); + + bool RemovePlayer(Entity* player); + + Entity* GetPlayer(const SystemAddress& sysAddr); + + Entity* GetPlayer(const std::string& name); + + Entity* GetPlayer(LWOOBJID playerID); + + const std::vector& GetAllPlayers(); +}; + +#endif //!__PLAYERMANAGER__H__ diff --git a/dGame/TeamManager.cpp b/dGame/TeamManager.cpp index 0258ce3e..abb20c48 100644 --- a/dGame/TeamManager.cpp +++ b/dGame/TeamManager.cpp @@ -1,8 +1,14 @@ #include "TeamManager.h" #include "EntityManager.h" +#include "Game.h" +#include "dConfig.h" TeamManager* TeamManager::m_Address = nullptr; //For singleton method +Team::Team() { + lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1; +} + TeamManager::TeamManager() { } diff --git a/dGame/TeamManager.h b/dGame/TeamManager.h index eb1b5c5b..5d4716f8 100644 --- a/dGame/TeamManager.h +++ b/dGame/TeamManager.h @@ -2,16 +2,15 @@ #include "Entity.h" -struct Team -{ +struct Team { + Team(); LWOOBJID teamID = LWOOBJID_EMPTY; char lootOption = 0; std::vector members{}; char lootRound = 0; }; -class TeamManager -{ +class TeamManager { public: static TeamManager* Instance() { if (!m_Address) { diff --git a/dGame/TradingManager.cpp b/dGame/TradingManager.cpp index 09d452b3..3d9a209c 100644 --- a/dGame/TradingManager.cpp +++ b/dGame/TradingManager.cpp @@ -2,9 +2,9 @@ #include "EntityManager.h" #include "GameMessages.h" #include "InventoryComponent.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "Item.h" #include "Character.h" #include "CharacterComponent.h" @@ -67,7 +67,7 @@ void Trade::SetAccepted(LWOOBJID participant, bool value) { if (participant == m_ParticipantA) { m_AcceptedA = !value; - Game::logger->Log("Trade", "Accepted from A (%d), B: (%d)", value, m_AcceptedB); + LOG("Accepted from A (%d), B: (%d)", value, m_AcceptedB); auto* entityB = GetParticipantBEntity(); @@ -77,7 +77,7 @@ void Trade::SetAccepted(LWOOBJID participant, bool value) { } else if (participant == m_ParticipantB) { m_AcceptedB = !value; - Game::logger->Log("Trade", "Accepted from B (%d), A: (%d)", value, m_AcceptedA); + LOG("Accepted from B (%d), A: (%d)", value, m_AcceptedA); auto* entityA = GetParticipantAEntity(); @@ -125,7 +125,7 @@ void Trade::Complete() { // First verify both players have the coins and items requested for the trade. if (characterA->GetCoins() < m_CoinsA || characterB->GetCoins() < m_CoinsB) { - Game::logger->Log("TradingManager", "Possible coin trade cheating attempt! Aborting trade."); + LOG("Possible coin trade cheating attempt! Aborting trade."); return; } @@ -133,11 +133,11 @@ void Trade::Complete() { auto* itemToRemove = inventoryA->FindItemById(tradeItem.itemId); if (itemToRemove) { if (itemToRemove->GetCount() < tradeItem.itemCount) { - Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading!!! Aborting trade", characterA->GetName().c_str()); + LOG("Possible cheating attempt from %s in trading!!! Aborting trade", characterA->GetName().c_str()); return; } } else { - Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading due to item not being available!!!", characterA->GetName().c_str()); + LOG("Possible cheating attempt from %s in trading due to item not being available!!!", characterA->GetName().c_str()); return; } } @@ -146,11 +146,11 @@ void Trade::Complete() { auto* itemToRemove = inventoryB->FindItemById(tradeItem.itemId); if (itemToRemove) { if (itemToRemove->GetCount() < tradeItem.itemCount) { - Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading!!! Aborting trade", characterB->GetName().c_str()); + LOG("Possible cheating attempt from %s in trading!!! Aborting trade", characterB->GetName().c_str()); return; } } else { - Game::logger->Log("TradingManager", "Possible cheating attempt from %s in trading due to item not being available!!! Aborting trade", characterB->GetName().c_str()); + LOG("Possible cheating attempt from %s in trading due to item not being available!!! Aborting trade", characterB->GetName().c_str()); return; } } @@ -194,7 +194,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) { uint64_t coins; std::vector itemIds; - Game::logger->Log("Trade", "Attempting to send trade update"); + LOG("Attempting to send trade update"); if (participant == m_ParticipantA) { other = GetParticipantBEntity(); @@ -228,7 +228,7 @@ void Trade::SendUpdateToOther(LWOOBJID participant) { items.push_back(tradeItem); } - Game::logger->Log("Trade", "Sending trade update"); + LOG("Sending trade update"); GameMessages::SendServerTradeUpdate(other->GetObjectID(), coins, items, other->GetSystemAddress()); } @@ -273,13 +273,13 @@ void TradingManager::CancelTrade(LWOOBJID tradeId) { } Trade* TradingManager::NewTrade(LWOOBJID participantA, LWOOBJID participantB) { - const LWOOBJID tradeId = ObjectIDManager::Instance()->GenerateObjectID(); + const LWOOBJID tradeId = ObjectIDManager::GenerateObjectID(); auto* trade = new Trade(tradeId, participantA, participantB); trades[tradeId] = trade; - Game::logger->Log("TradingManager", "Created new trade between (%llu) <-> (%llu)", participantA, participantB); + LOG("Created new trade between (%llu) <-> (%llu)", participantA, participantB); return trade; } diff --git a/dGame/User.cpp b/dGame/User.cpp index 55bbcc09..0b2c3c3f 100644 --- a/dGame/User.cpp +++ b/dGame/User.cpp @@ -2,7 +2,7 @@ #include "Database.h" #include "Character.h" #include "dServer.h" -#include "dLogger.h" +#include "Logger.h" #include "Game.h" #include "dZoneManager.h" #include "eServerDisconnectIdentifiers.h" @@ -23,41 +23,23 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std: m_IsBestFriendMap = std::unordered_map(); - //HACK HACK HACK - //This needs to be re-enabled / updated whenever the mute stuff is moved to another table. - //This was only done because otherwise the website's account page dies and the website is waiting on a migration to wordpress. - - //sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gmlevel, mute_expire FROM accounts WHERE name=? LIMIT 1;"); - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id, gm_level FROM accounts WHERE name=? LIMIT 1;"); - stmt->setString(1, username.c_str()); - - sql::ResultSet* res = stmt->executeQuery(); - while (res->next()) { - m_AccountID = res->getUInt(1); - m_MaxGMLevel = static_cast(res->getInt(2)); + auto userInfo = Database::Get()->GetAccountInfo(username); + if (userInfo) { + m_AccountID = userInfo->id; + m_MaxGMLevel = userInfo->maxGmLevel; m_MuteExpire = 0; //res->getUInt64(3); } - delete res; - delete stmt; - //If we're loading a zone, we'll load the last used (aka current) character: if (Game::server->GetZoneID() != 0) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 1;"); - stmt->setUInt(1, m_AccountID); - - sql::ResultSet* res = stmt->executeQuery(); - if (res->rowsCount() > 0) { - while (res->next()) { - LWOOBJID objID = res->getUInt64(1); - Character* character = new Character(uint32_t(objID), this); - m_Characters.push_back(character); - Game::logger->Log("User", "Loaded %llu as it is the last used char", objID); - } + auto characterList = Database::Get()->GetAccountCharacterIds(m_AccountID); + if (!characterList.empty()) { + const uint32_t lastUsedCharacterId = characterList.front(); + Character* character = new Character(lastUsedCharacterId, this); + character->UpdateFromDatabase(); + m_Characters.push_back(character); + LOG("Loaded %i as it is the last used char", lastUsedCharacterId); } - - delete res; - delete stmt; } } @@ -92,10 +74,7 @@ User& User::operator= (const User& other) { } bool User::operator== (const User& other) const { - if (m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress) - return true; - - return false; + return m_Username == other.m_Username || m_SessionKey == other.m_SessionKey || m_SystemAddress == other.m_SystemAddress; } Character* User::GetLastUsedChar() { @@ -127,7 +106,11 @@ void User::UserOutOfSync() { m_AmountOfTimesOutOfSync++; if (m_AmountOfTimesOutOfSync > m_MaxDesyncAllowed) { //YEET - Game::logger->Log("User", "User %s was out of sync %i times out of %i, disconnecting for suspected speedhacking.", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed); + LOG("User %s was out of sync %i times out of %i, disconnecting for suspected speedhacking.", m_Username.c_str(), m_AmountOfTimesOutOfSync, m_MaxDesyncAllowed); Game::server->Disconnect(this->m_SystemAddress, eServerDisconnectIdentifiers::PLAY_SCHEDULE_TIME_DONE); } } + +void User::UpdateBestFriendValue(const std::string_view playerName, const bool newValue) { + m_IsBestFriendMap[playerName.data()] = newValue; +} diff --git a/dGame/User.h b/dGame/User.h index 3201538e..54e6ad52 100644 --- a/dGame/User.h +++ b/dGame/User.h @@ -3,7 +3,7 @@ #include #include -#include "../thirdparty/raknet/Source/RakNetTypes.h" +#include "RakNetTypes.h" #include "dCommonVars.h" #include @@ -43,8 +43,8 @@ public: bool GetLastChatMessageApproved() { return m_LastChatMessageApproved; } void SetLastChatMessageApproved(bool approved) { m_LastChatMessageApproved = approved; } - std::unordered_map GetIsBestFriendMap() { return m_IsBestFriendMap; } - void SetIsBestFriendMap(std::unordered_map mapToSet) { m_IsBestFriendMap = mapToSet; } + const std::unordered_map& GetIsBestFriendMap() { return m_IsBestFriendMap; } + void UpdateBestFriendValue(const std::string_view playerName, const bool newValue); bool GetIsMuted() const; diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 9e409019..b98569eb 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -6,14 +6,13 @@ #include "Database.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "User.h" -#include +#include "WorldPackets.h" #include "Character.h" -#include -#include "PacketUtils.h" -#include "../dWorldServer/ObjectIDManager.h" -#include "dLogger.h" +#include "BitStream.h" +#include "ObjectIDManager.h" +#include "Logger.h" #include "GeneralUtils.h" #include "ZoneInstanceManager.h" #include "dServer.h" @@ -28,6 +27,8 @@ #include "eRenameResponse.h" #include "eConnectionType.h" #include "eChatInternalMessageType.h" +#include "BitStreamUtils.h" +#include "CheatDetection.h" UserManager* UserManager::m_Address = nullptr; @@ -42,57 +43,53 @@ inline void StripCR(std::string& str) { void UserManager::Initialize() { std::string line; - AssetMemoryBuffer fnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_first.txt"); - if (!fnBuff.m_Success) { - Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str()); + auto fnStream = Game::assetManager->GetFile("names/minifigname_first.txt"); + if (!fnStream) { + LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_first.txt").string().c_str()); throw std::runtime_error("Aborting initialization due to missing minifigure name file."); } - std::istream fnStream = std::istream(&fnBuff); + while (std::getline(fnStream, line, '\n')) { std::string name = line; StripCR(name); m_FirstNames.push_back(name); } - fnBuff.close(); - AssetMemoryBuffer mnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_middle.txt"); - if (!mnBuff.m_Success) { - Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str()); + auto mnStream = Game::assetManager->GetFile("names/minifigname_middle.txt"); + if (!mnStream) { + LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_middle.txt").string().c_str()); throw std::runtime_error("Aborting initialization due to missing minifigure name file."); } - std::istream mnStream = std::istream(&mnBuff); + while (std::getline(mnStream, line, '\n')) { std::string name = line; StripCR(name); m_MiddleNames.push_back(name); } - mnBuff.close(); - AssetMemoryBuffer lnBuff = Game::assetManager->GetFileAsBuffer("names/minifigname_last.txt"); - if (!lnBuff.m_Success) { - Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str()); + auto lnStream = Game::assetManager->GetFile("names/minifigname_last.txt"); + if (!lnStream) { + LOG("Failed to load %s", (Game::assetManager->GetResPath() / "names/minifigname_last.txt").string().c_str()); throw std::runtime_error("Aborting initialization due to missing minifigure name file."); } - std::istream lnStream = std::istream(&lnBuff); + while (std::getline(lnStream, line, '\n')) { std::string name = line; StripCR(name); m_LastNames.push_back(name); } - lnBuff.close(); - //Load our pre-approved names: - AssetMemoryBuffer chatListBuff = Game::assetManager->GetFileAsBuffer("chatplus_en_us.txt"); - if (!chatListBuff.m_Success) { - Game::logger->Log("UserManager", "Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str()); + // Load our pre-approved names: + 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 whitelist file."); } - std::istream chatListStream = std::istream(&chatListBuff); + while (std::getline(chatListStream, line, '\n')) { StripCR(line); m_PreapprovedNames.push_back(line); } - chatListBuff.close(); } UserManager::~UserManager() { @@ -123,7 +120,7 @@ User* UserManager::GetUser(const SystemAddress& sysAddr) { User* UserManager::GetUser(const std::string& username) { for (auto p : m_Users) { if (p.second) { - if (p.second->GetUsername() == username) return p.second; + if (GeneralUtils::CaseInsensitiveStringCompare(p.second->GetUsername(), username)) return p.second; } } @@ -148,7 +145,7 @@ bool UserManager::DeleteUser(const SystemAddress& sysAddr) { void UserManager::DeletePendingRemovals() { for (auto* user : m_UsersToDelete) { - Game::logger->Log("UserManager", "Deleted user %i", user->GetAccountID()); + LOG("Deleted user %i", user->GetAccountID()); delete user; } @@ -156,20 +153,6 @@ void UserManager::DeletePendingRemovals() { m_UsersToDelete.clear(); } -bool UserManager::IsNameAvailable(const std::string& requestedName) { - bool toReturn = false; //To allow for a clean exit - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? OR pending_name=? LIMIT 1;"); - stmt->setString(1, requestedName.c_str()); - stmt->setString(2, requestedName.c_str()); - - sql::ResultSet* res = stmt->executeQuery(); - if (res->rowsCount() == 0) toReturn = true; - - delete stmt; - delete res; - return toReturn; -} - std::string UserManager::GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex) { if (firstNameIndex > m_FirstNames.size() || middleNameIndex > m_MiddleNames.size() || lastNameIndex > m_LastNames.size()) return std::string("INVALID"); return std::string(m_FirstNames[firstNameIndex] + m_MiddleNames[middleNameIndex] + m_LastNames[lastNameIndex]); @@ -198,279 +181,241 @@ bool UserManager::IsNamePreapproved(const std::string& requestedName) { void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { User* u = GetUser(sysAddr); if (!u) return; + std::vector& chars = u->GetCharacters(); - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE account_id=? ORDER BY last_login DESC LIMIT 4;"); - stmt->setUInt(1, u->GetAccountID()); - - sql::ResultSet* res = stmt->executeQuery(); - if (res->rowsCount() > 0) { - std::vector& chars = u->GetCharacters(); - - for (size_t i = 0; i < chars.size(); ++i) { - if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save - { - delete chars[i]; - - continue; - } - - auto* skillComponent = chars[i]->GetEntity()->GetComponent(); - - if (skillComponent != nullptr) { - skillComponent->Reset(); - } - - Game::entityManager->DestroyEntity(chars[i]->GetEntity()); - - chars[i]->SaveXMLToDatabase(); - - chars[i]->GetEntity()->SetCharacter(nullptr); - + for (size_t i = 0; i < chars.size(); ++i) { + if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save + { delete chars[i]; + + continue; } - chars.clear(); + auto* skillComponent = chars[i]->GetEntity()->GetComponent(); - while (res->next()) { - LWOOBJID objID = res->getUInt64(1); - Character* character = new Character(uint32_t(objID), u); - character->SetIsNewLogin(); - chars.push_back(character); + if (skillComponent != nullptr) { + skillComponent->Reset(); + } + + Game::entityManager->DestroyEntity(chars[i]->GetEntity()); + + chars[i]->SaveXMLToDatabase(); + + chars[i]->GetEntity()->SetCharacter(nullptr); + + delete chars[i]; + } + + chars.clear(); + + for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) { + Character* character = new Character(characterId, u); + character->UpdateFromDatabase(); + character->SetIsNewLogin(); + chars.push_back(character); + } + + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); + + std::vector characters = u->GetCharacters(); + bitStream.Write(characters.size()); + bitStream.Write(0); //TODO: Pick the most recent played index. character index in front, just picking 0 + + for (uint32_t i = 0; i < characters.size(); ++i) { + bitStream.Write(characters[i]->GetObjectID()); + bitStream.Write(0); + + bitStream.Write(LUWString(characters[i]->GetName())); + bitStream.Write(LUWString(characters[i]->GetUnapprovedName())); + + bitStream.Write(characters[i]->GetNameRejected()); + bitStream.Write(false); + + bitStream.Write(LUString("", 10)); + + bitStream.Write(characters[i]->GetShirtColor()); + bitStream.Write(characters[i]->GetShirtStyle()); + bitStream.Write(characters[i]->GetPantsColor()); + bitStream.Write(characters[i]->GetHairStyle()); + bitStream.Write(characters[i]->GetHairColor()); + bitStream.Write(characters[i]->GetLeftHand()); + bitStream.Write(characters[i]->GetRightHand()); + bitStream.Write(characters[i]->GetEyebrows()); + bitStream.Write(characters[i]->GetEyes()); + bitStream.Write(characters[i]->GetMouth()); + bitStream.Write(0); + + bitStream.Write(characters[i]->GetZoneID()); + bitStream.Write(characters[i]->GetZoneInstance()); + bitStream.Write(characters[i]->GetZoneClone()); + + bitStream.Write(characters[i]->GetLastLogin()); + + const auto& equippedItems = characters[i]->GetEquippedItems(); + bitStream.Write(equippedItems.size()); + + for (uint32_t j = 0; j < equippedItems.size(); ++j) { + bitStream.Write(equippedItems[j]); } } - delete res; - delete stmt; - - WorldPackets::SendCharacterList(sysAddr, u); + SEND_PACKET; } void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) { User* u = GetUser(sysAddr); if (!u) return; + + LUWString LUWStringName; + uint32_t firstNameIndex; + uint32_t middleNameIndex; + uint32_t lastNameIndex; + uint32_t shirtColor; + uint32_t shirtStyle; + uint32_t pantsColor; + uint32_t hairStyle; + uint32_t hairColor; + uint32_t lh; + uint32_t rh; + uint32_t eyebrows; + uint32_t eyes; + uint32_t mouth; - std::string name = PacketUtils::ReadString(8, packet, true); + CINSTREAM_SKIP_HEADER; + inStream.Read(LUWStringName); + inStream.Read(firstNameIndex); + inStream.Read(middleNameIndex); + inStream.Read(lastNameIndex); + inStream.IgnoreBytes(9); + inStream.Read(shirtColor); + inStream.Read(shirtStyle); + inStream.Read(pantsColor); + inStream.Read(hairStyle); + inStream.Read(hairColor); + inStream.Read(lh); + inStream.Read(rh); + inStream.Read(eyebrows); + inStream.Read(eyes); + inStream.Read(mouth); - uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet); - uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet); - uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet); + const auto name = LUWStringName.GetAsString(); std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex); - - uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet); - uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet); - uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet); - uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet); - uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet); - uint32_t lh = PacketUtils::ReadPacketU32(115, packet); - uint32_t rh = PacketUtils::ReadPacketU32(119, packet); - uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet); - uint32_t eyes = PacketUtils::ReadPacketU32(127, packet); - uint32_t mouth = PacketUtils::ReadPacketU32(131, packet); - LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT pantsLOT = FindCharPantsID(pantsColor); - if (name != "" && !UserManager::IsNameAvailable(name)) { - Game::logger->Log("UserManager", "AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str()); + if (!name.empty() && Database::Get()->GetCharacterInfo(name)) { + LOG("AccountID: %i chose unavailable name: %s", u->GetAccountID(), name.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::CUSTOM_NAME_IN_USE); return; } - if (!IsNameAvailable(predefinedName)) { - Game::logger->Log("UserManager", "AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str()); + if (Database::Get()->GetCharacterInfo(predefinedName)) { + LOG("AccountID: %i chose unavailable predefined name: %s", u->GetAccountID(), predefinedName.c_str()); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::PREDEFINED_NAME_IN_USE); return; } - if (name == "") { - Game::logger->Log("UserManager", "AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str()); + if (name.empty()) { + LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str()); } else { - Game::logger->Log("UserManager", "AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str()); + LOG("AccountID: %i is creating a character with name: %s (temporary: %s)", u->GetAccountID(), name.c_str(), predefinedName.c_str()); } //Now that the name is ok, we can get an objectID from Master: - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) { - sql::PreparedStatement* overlapStmt = Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE id = ?"); - overlapStmt->setUInt(1, objectID); - - auto* overlapResult = overlapStmt->executeQuery(); - - if (overlapResult->next()) { - Game::logger->Log("UserManager", "Character object id unavailable, check objectidtracker!"); + ObjectIDManager::RequestPersistentID([=, this](uint32_t objectID) { + if (Database::Get()->GetCharacterInfo(objectID)) { + LOG("Character object id unavailable, check object_id_tracker!"); WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::OBJECT_ID_UNAVAILABLE); return; } std::stringstream xml; - xml << ""; + + xml << ""; xml << "GetAccountID() << "\" cc=\"0\" gm=\"0\" ft=\"0\" llog=\"" << time(NULL) << "\" "; xml << "ls=\"0\" lzx=\"-626.5847\" lzy=\"613.3515\" lzz=\"-28.6374\" lzrx=\"0.0\" lzry=\"0.7015\" lzrz=\"0.0\" lzrw=\"0.7126\" "; xml << "stt=\"0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;0;\">"; + xml << ""; + xml << ""; - std::string xmlSave1 = xml.str(); - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforshirt) { - std::stringstream xml2; + LWOOBJID lwoidforshirt = ObjectIDManager::GenerateRandomObjectID(); + LWOOBJID lwoidforpants; - LWOOBJID lwoidforshirt = idforshirt; - GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER); - GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT); - xml2 << xmlSave1 << ""; + do { + lwoidforpants = ObjectIDManager::GenerateRandomObjectID(); + } while (lwoidforpants == lwoidforshirt); //Make sure we don't have the same ID for both shirt and pants - std::string xmlSave2 = xml2.str(); + GeneralUtils::SetBit(lwoidforshirt, eObjectBits::CHARACTER); + GeneralUtils::SetBit(lwoidforshirt, eObjectBits::PERSISTENT); + GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER); + GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT); - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t idforpants) { - LWOOBJID lwoidforpants = idforpants; - GeneralUtils::SetBit(lwoidforpants, eObjectBits::CHARACTER); - GeneralUtils::SetBit(lwoidforpants, eObjectBits::PERSISTENT); + xml << ""; + xml << ""; - std::stringstream xml3; - xml3 << xmlSave2 << ""; + xml << ""; - xml3 << ""; + //Check to see if our name was pre-approved: + bool nameOk = IsNamePreapproved(name); + if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true; - //Check to see if our name was pre-approved: - bool nameOk = IsNamePreapproved(name); - if (!nameOk && u->GetMaxGMLevel() > eGameMasterLevel::FORUM_MODERATOR) nameOk = true; + std::string_view nameToAssign = !name.empty() && nameOk ? name : predefinedName; + std::string pendingName = !name.empty() && !nameOk ? name : ""; - if (name != "") { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)"); - stmt->setUInt(1, objectID); - stmt->setUInt(2, u->GetAccountID()); - stmt->setString(3, predefinedName.c_str()); - stmt->setString(4, name.c_str()); - stmt->setBoolean(5, false); - stmt->setUInt64(6, time(NULL)); + ICharInfo::Info info; + info.name = nameToAssign; + info.pendingName = pendingName; + info.id = objectID; + info.accountId = u->GetAccountID(); - if (nameOk) { - stmt->setString(3, name.c_str()); - stmt->setString(4, ""); - } + Database::Get()->InsertNewCharacter(info); - stmt->execute(); - delete stmt; - } else { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`) VALUES (?,?,?,?,?,?)"); - stmt->setUInt(1, objectID); - stmt->setUInt(2, u->GetAccountID()); - stmt->setString(3, predefinedName.c_str()); - stmt->setString(4, ""); - stmt->setBoolean(5, false); - stmt->setUInt64(6, time(NULL)); + //Now finally insert our character xml: + Database::Get()->InsertCharacterXml(objectID, xml.str()); - stmt->execute(); - delete stmt; - } - - //Now finally insert our character xml: - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("INSERT INTO `charxml`(`id`, `xml_data`) VALUES (?,?)"); - stmt->setUInt(1, objectID); - stmt->setString(2, xml3.str().c_str()); - stmt->execute(); - delete stmt; - - WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); - UserManager::RequestCharacterList(sysAddr); - }); - }); + WorldPackets::SendCharacterCreationResponse(sysAddr, eCharacterCreationResponse::SUCCESS); + UserManager::RequestCharacterList(sysAddr); }); } void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) { User* u = GetUser(sysAddr); if (!u) { - Game::logger->Log("UserManager", "Couldn't get user to delete character"); + LOG("Couldn't get user to delete character"); return; } - LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet); + CINSTREAM_SKIP_HEADER; + LWOOBJID objectID; + inStream.Read(objectID); uint32_t charID = static_cast(objectID); - Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID); + LOG("Received char delete req for ID: %llu (%u)", objectID, charID); - //Check if this user has this character: - bool hasCharacter = false; - std::vector& characters = u->GetCharacters(); - for (size_t i = 0; i < characters.size(); ++i) { - if (characters[i]->GetID() == charID) { hasCharacter = true; } - } + bool hasCharacter = CheatDetection::VerifyLwoobjidIsSender( + objectID, + sysAddr, + CheckType::User, + "User %i tried to delete a character that it does not own!", + u->GetAccountID()); if (!hasCharacter) { - Game::logger->Log("UserManager", "User %i tried to delete a character that it does not own!", u->GetAccountID()); WorldPackets::SendCharacterDeleteResponse(sysAddr, false); } else { - Game::logger->Log("UserManager", "Deleting character %i", charID); - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charxml WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM command_log WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM friends WHERE player_id=? OR friend_id=?;"); - stmt->setUInt(1, charID); - stmt->setUInt(2, charID); - stmt->execute(); - delete stmt; - CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); - bitStream.Write(objectID); - Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM leaderboard WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt( - "DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);" - ); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM properties WHERE owner_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM ugc WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM activity_log WHERE character_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM mail WHERE receiver_id=?;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } - { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("DELETE FROM charinfo WHERE id=? LIMIT 1;"); - stmt->setUInt64(1, charID); - stmt->execute(); - delete stmt; - } + LOG("Deleting character %i", charID); + Database::Get()->DeleteCharacter(charID); + + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); + bitStream.Write(objectID); + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); WorldPackets::SendCharacterDeleteResponse(sysAddr, true); } @@ -479,58 +424,58 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) { User* u = GetUser(sysAddr); if (!u) { - Game::logger->Log("UserManager", "Couldn't get user to delete character"); + LOG("Couldn't get user to delete character"); return; } - LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet); + CINSTREAM_SKIP_HEADER; + LWOOBJID objectID; + inStream.Read(objectID); GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER); GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT); uint32_t charID = static_cast(objectID); - Game::logger->Log("UserManager", "Received char rename request for ID: %llu (%u)", objectID, charID); + LOG("Received char rename request for ID: %llu (%u)", objectID, charID); - std::string newName = PacketUtils::ReadString(16, packet, true); + LUWString LUWStringName; + inStream.Read(LUWStringName); + const auto newName = LUWStringName.GetAsString(); Character* character = nullptr; //Check if this user has this character: - bool hasCharacter = false; - std::vector& characters = u->GetCharacters(); - for (size_t i = 0; i < characters.size(); ++i) { - if (characters[i]->GetID() == charID) { hasCharacter = true; character = characters[i]; } - } + bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender( + objectID, + sysAddr, + CheckType::User, + "User %i tried to rename a character that it does not own!", + u->GetAccountID()); - if (!hasCharacter || !character) { - Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID()); + auto unusedItr = std::find_if(u->GetCharacters().begin(), u->GetCharacters().end(), [&](Character* c) { + if (c->GetID() == charID) { + character = c; + return true; + } + return false; + }); + + if (!ownsCharacter || !character) { WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR); - } else if (hasCharacter && character) { + } else if (ownsCharacter && character) { if (newName == character->GetName()) { WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE); return; } - if (IsNameAvailable(newName)) { + if (!Database::Get()->GetCharacterInfo(newName)) { if (IsNamePreapproved(newName)) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET name=?, pending_name='', needs_rename=0, last_login=? WHERE id=? LIMIT 1"); - stmt->setString(1, newName); - stmt->setUInt64(2, time(NULL)); - stmt->setUInt(3, character->GetID()); - stmt->execute(); - delete stmt; - - Game::logger->Log("UserManager", "Character %s now known as %s", character->GetName().c_str(), newName.c_str()); + Database::Get()->SetCharacterName(charID, newName); + LOG("Character %s now known as %s", character->GetName().c_str(), newName.c_str()); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); } else { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET pending_name=?, needs_rename=0, last_login=? WHERE id=? LIMIT 1"); - stmt->setString(1, newName); - stmt->setUInt64(2, time(NULL)); - stmt->setUInt(3, character->GetID()); - stmt->execute(); - delete stmt; - - Game::logger->Log("UserManager", "Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str()); + Database::Get()->SetPendingCharacterName(charID, newName); + LOG("Character %s has been renamed to %s and is pending approval by a moderator.", character->GetName().c_str(), newName.c_str()); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::SUCCESS); UserManager::RequestCharacterList(sysAddr); } @@ -538,7 +483,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_IN_USE); } } else { - Game::logger->Log("UserManager", "Unknown error occurred when renaming character, either hasCharacter or character variable != true."); + LOG("Unknown error occurred when renaming character, either hasCharacter or character variable != true."); WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR); } } @@ -546,7 +491,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID) { User* u = GetUser(sysAddr); if (!u) { - Game::logger->Log("UserManager", "Couldn't get user to log in character"); + LOG("Couldn't get user to log in character"); return; } @@ -559,17 +504,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID } if (hasCharacter && character) { - sql::PreparedStatement* stmt = Database::CreatePreppedStmt("UPDATE charinfo SET last_login=? WHERE id=? LIMIT 1"); - stmt->setUInt64(1, time(NULL)); - stmt->setUInt(2, playerID); - stmt->execute(); - delete stmt; + Database::Get()->UpdateLastLoggedInCharacter(playerID); uint32_t zoneID = character->GetZoneID(); if (zoneID == LWOZONEID_INVALID) zoneID = 1000; //Send char to VE ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); if (character) { character->SetZoneID(zoneID); character->SetZoneInstance(zoneInstance); @@ -579,22 +520,24 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID return; }); } else { - Game::logger->Log("UserManager", "Unknown error occurred when logging in a character, either hasCharacter or character variable != true."); + LOG("Unknown error occurred when logging in a character, either hasCharacter or character variable != true."); } } uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { try { - std::string shirtQuery = "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) == \"character create shirt\" AND icc.color1 == "; - shirtQuery += std::to_string(shirtColor); - shirtQuery += " AND icc.decal == "; - shirtQuery = shirtQuery + std::to_string(shirtStyle); - auto tableData = CDClientDatabase::ExecuteQuery(shirtQuery); - auto shirtLOT = tableData.getIntField(0, -1); + auto stmt = CDClientDatabase::CreatePreppedStmt( + "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(shirtColor)); + stmt.bind(3, static_cast(shirtStyle)); + auto tableData = stmt.execQuery(); + auto shirtLOT = tableData.getIntField(0, 4069); tableData.finalize(); return shirtLOT; - } catch (const std::exception&) { - Game::logger->Log("Character Create", "Failed to execute query! Using backup..."); + } catch (const std::exception& ex) { + LOG("Could not look up shirt %i %i: %s", shirtColor, shirtStyle, ex.what()); // in case of no shirt found in CDServer, return problematic red vest. return 4069; } @@ -602,14 +545,17 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { uint32_t FindCharPantsID(uint32_t pantsColor) { try { - std::string pantsQuery = "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) == \"cc pants\" AND icc.color1 == "; - pantsQuery += std::to_string(pantsColor); - auto tableData = CDClientDatabase::ExecuteQuery(pantsQuery); - auto pantsLOT = tableData.getIntField(0, -1); + auto stmt = CDClientDatabase::CreatePreppedStmt( + "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(pantsColor)); + auto tableData = stmt.execQuery(); + auto pantsLOT = tableData.getIntField(0, 2508); tableData.finalize(); return pantsLOT; - } catch (const std::exception&) { - Game::logger->Log("Character Create", "Failed to execute query! Using backup..."); + } catch (const std::exception& ex) { + LOG("Could not look up pants %i: %s", pantsColor, ex.what()); // in case of no pants color found in CDServer, return red pants. return 2508; } diff --git a/dGame/UserManager.h b/dGame/UserManager.h index bf9985a1..94bf5e95 100644 --- a/dGame/UserManager.h +++ b/dGame/UserManager.h @@ -28,7 +28,6 @@ public: bool DeleteUser(const SystemAddress& sysAddr); //Returns true on succesful deletion void DeletePendingRemovals(); - bool IsNameAvailable(const std::string& requestedName); std::string GetPredefinedName(uint32_t firstNameIndex, uint32_t middleNameIndex, uint32_t lastNameIndex); bool IsNamePreapproved(const std::string& requestedName); diff --git a/dGame/dBehaviors/AirMovementBehavior.cpp b/dGame/dBehaviors/AirMovementBehavior.cpp index a6d749d3..8a3f894c 100644 --- a/dGame/dBehaviors/AirMovementBehavior.cpp +++ b/dGame/dBehaviors/AirMovementBehavior.cpp @@ -3,13 +3,13 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void AirMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { uint32_t handle{}; if (!bitStream->Read(handle)) { - Game::logger->Log("AirMovementBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; } @@ -26,14 +26,14 @@ void AirMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitS uint32_t behaviorId{}; if (!bitStream->Read(behaviorId)) { - Game::logger->Log("AirMovementBehavior", "Unable to read behaviorId from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read behaviorId from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); return; } LWOOBJID target{}; if (!bitStream->Read(target)) { - Game::logger->Log("AirMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); return; } diff --git a/dGame/dBehaviors/AndBehavior.cpp b/dGame/dBehaviors/AndBehavior.cpp index 67c88679..57493248 100644 --- a/dGame/dBehaviors/AndBehavior.cpp +++ b/dGame/dBehaviors/AndBehavior.cpp @@ -1,7 +1,7 @@ #include "AndBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void AndBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { for (auto* behavior : this->m_behaviors) { diff --git a/dGame/dBehaviors/ApplyBuffBehavior.cpp b/dGame/dBehaviors/ApplyBuffBehavior.cpp index c94762aa..26b3da84 100644 --- a/dGame/dBehaviors/ApplyBuffBehavior.cpp +++ b/dGame/dBehaviors/ApplyBuffBehavior.cpp @@ -15,7 +15,7 @@ void ApplyBuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS if (buffComponent == nullptr) return; buffComponent->ApplyBuff(m_BuffId, m_Duration, context->originator, addImmunity, cancelOnDamaged, cancelOnDeath, - cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); + cancelOnLogout, cancelonRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, m_ApplyOnTeammates); } void ApplyBuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch) { @@ -45,4 +45,5 @@ void ApplyBuffBehavior::Load() { cancelOnUi = GetBoolean("cancel_on_ui"); cancelOnUnequip = GetBoolean("cancel_on_unequip"); cancelOnZone = GetBoolean("cancel_on_zone"); + m_ApplyOnTeammates = GetBoolean("apply_on_teammates"); } diff --git a/dGame/dBehaviors/ApplyBuffBehavior.h b/dGame/dBehaviors/ApplyBuffBehavior.h index 139082df..e01a238e 100644 --- a/dGame/dBehaviors/ApplyBuffBehavior.h +++ b/dGame/dBehaviors/ApplyBuffBehavior.h @@ -31,4 +31,6 @@ public: void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; void Load() override; +private: + bool m_ApplyOnTeammates; }; diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.cpp b/dGame/dBehaviors/AreaOfEffectBehavior.cpp index e43d542d..2a7e9754 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp +++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp @@ -4,150 +4,130 @@ #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "BehaviorBranchContext.h" #include "BehaviorContext.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { uint32_t targetCount{}; if (!bitStream->Read(targetCount)) { - Game::logger->Log("AreaOfEffectBehavior", "Unable to read targetCount from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read targetCount from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + return; + } + + if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return; + + if (targetCount == 0){ + PlayFx(u"miss", context->originator); return; } if (targetCount > this->m_maxTargets) { + LOG("Serialized size is greater than max targets! Size: %i, Max: %i", targetCount, this->m_maxTargets); return; } - std::vector targets; + auto caster = context->caster; + if (this->m_useTargetAsCaster) context->caster = branch.target; + std::vector targets; targets.reserve(targetCount); for (auto i = 0u; i < targetCount; ++i) { LWOOBJID target{}; - if (!bitStream->Read(target)) { - Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i); - return; + LOG("failed to read in target %i from bitStream, aborting target Handle!", i); }; - targets.push_back(target); } for (auto target : targets) { branch.target = target; - this->m_action->Handle(context, bitStream, branch); } + context->caster = caster; + PlayFx(u"cast", context->originator); } void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { - auto* self = Game::entityManager->GetEntity(context->caster); - if (self == nullptr) { - Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator); + auto* caster = Game::entityManager->GetEntity(context->caster); + if (!caster) return; - return; + // determine the position we are casting the AOE from + auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition(); + if (this->m_useTargetPosition) { + if (branch.target == LWOOBJID_EMPTY) return; + auto branchTarget = Game::entityManager->GetEntity(branch.target); + if (branchTarget) reference = branchTarget->GetPosition(); } - auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition(); + reference += this->m_offset; - std::vector targets; - - auto* presetTarget = Game::entityManager->GetEntity(branch.target); - - if (presetTarget != nullptr) { - if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition())) { - targets.push_back(presetTarget); - } - } - - int32_t includeFaction = m_includeFaction; - - if (self->GetLOT() == 14466) // TODO: Fix edge case - { - includeFaction = 1; - } - - // Gets all of the valid targets, passing in if should target enemies and friends - for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) { - auto* entity = Game::entityManager->GetEntity(validTarget); - - if (entity == nullptr) { - Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator); - - continue; - } - - if (std::find(targets.begin(), targets.end(), entity) != targets.end()) { - continue; - } - - auto* destroyableComponent = entity->GetComponent(); - - if (destroyableComponent == nullptr) { - continue; - } - - if (destroyableComponent->HasFaction(m_ignoreFaction)) { - continue; - } - - const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition()); - - if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets)) { - targets.push_back(entity); - } - } + std::vector targets {}; + targets = Game::entityManager->GetEntitiesByProximity(reference, this->m_radius); + context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam); + // sort by distance std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) { - const auto aDistance = Vector3::DistanceSquared(a->GetPosition(), reference); - const auto bDistance = Vector3::DistanceSquared(b->GetPosition(), reference); + const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference); + const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference); + return aDistance < bDistance; + } + ); - return aDistance > bDistance; - }); + // resize if we have more than max targets allows + if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets); - const uint32_t size = targets.size(); + bitStream->Write(targets.size()); - bitStream->Write(size); - - if (size == 0) { + if (targets.size() == 0) { + PlayFx(u"miss", context->originator); return; - } + } else { + context->foundTarget = true; + // write all the targets to the bitstream + for (auto* target : targets) { + bitStream->Write(target->GetObjectID()); + } - context->foundTarget = true; - - for (auto* target : targets) { - bitStream->Write(target->GetObjectID()); - - PlayFx(u"cast", context->originator, target->GetObjectID()); - } - - for (auto* target : targets) { - branch.target = target->GetObjectID(); - - this->m_action->Calculate(context, bitStream, branch); + // then cast all the actions + for (auto* target : targets) { + branch.target = target->GetObjectID(); + this->m_action->Calculate(context, bitStream, branch); + } + PlayFx(u"cast", context->originator); } } void AreaOfEffectBehavior::Load() { - this->m_action = GetAction("action"); + this->m_action = GetAction("action"); // required + this->m_radius = GetFloat("radius", 0.0f); // required + this->m_maxTargets = GetInt("max targets", 100); + if (this->m_maxTargets == 0) this->m_maxTargets = 100; + this->m_useTargetPosition = GetBoolean("use_target_position", false); + this->m_useTargetAsCaster = GetBoolean("use_target_as_caster", false); + this->m_offset = NiPoint3( + GetFloat("offset_x", 0.0f), + GetFloat("offset_y", 0.0f), + GetFloat("offset_z", 0.0f) + ); - this->m_radius = GetFloat("radius"); - - this->m_maxTargets = GetInt("max targets"); - - this->m_ignoreFaction = GetInt("ignore_faction"); - - this->m_includeFaction = GetInt("include_faction"); - - this->m_TargetSelf = GetInt("target_self"); - - this->m_targetEnemy = GetInt("target_enemy"); - - this->m_targetFriend = GetInt("target_friend"); + // params after this are needed for filter targets + const auto parameters = GetParameterNames(); + for (const auto& parameter : parameters) { + if (parameter.first.rfind("include_faction", 0) == 0) { + this->m_includeFactionList.push_front(parameter.second); + } else if (parameter.first.rfind("ignore_faction", 0) == 0) { + this->m_ignoreFactionList.push_front(parameter.second); + } + } + this->m_targetSelf = GetBoolean("target_self", false); + this->m_targetEnemy = GetBoolean("target_enemy", false); + this->m_targetFriend = GetBoolean("target_friend", false); + this->m_targetTeam = GetBoolean("target_team", false); } diff --git a/dGame/dBehaviors/AreaOfEffectBehavior.h b/dGame/dBehaviors/AreaOfEffectBehavior.h index b5a48ddf..f0fbb18d 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.h +++ b/dGame/dBehaviors/AreaOfEffectBehavior.h @@ -1,34 +1,26 @@ #pragma once #include "Behavior.h" +#include class AreaOfEffectBehavior final : public Behavior { public: - Behavior* m_action; - - uint32_t m_maxTargets; - - float m_radius; - - int32_t m_ignoreFaction; - - int32_t m_includeFaction; - - int32_t m_TargetSelf; - - int32_t m_targetEnemy; - - int32_t m_targetFriend; - - /* - * Inherited - */ - explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) { - } - + explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {} void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; - void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; - void Load() override; +private: + Behavior* m_action; + uint32_t m_maxTargets; + float m_radius; + bool m_useTargetPosition; + bool m_useTargetAsCaster; + NiPoint3 m_offset; + + std::forward_list m_ignoreFactionList {}; + std::forward_list m_includeFactionList {}; + bool m_targetSelf; + bool m_targetEnemy; + bool m_targetFriend; + bool m_targetTeam; }; diff --git a/dGame/dBehaviors/AttackDelayBehavior.cpp b/dGame/dBehaviors/AttackDelayBehavior.cpp index 3f12f662..1bf1048a 100644 --- a/dGame/dBehaviors/AttackDelayBehavior.cpp +++ b/dGame/dBehaviors/AttackDelayBehavior.cpp @@ -2,13 +2,13 @@ #include "BehaviorBranchContext.h" #include "BehaviorContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void AttackDelayBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { uint32_t handle{}; if (!bitStream->Read(handle)) { - Game::logger->Log("AttackDelayBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; diff --git a/dGame/dBehaviors/BasicAttackBehavior.cpp b/dGame/dBehaviors/BasicAttackBehavior.cpp index 0a21383b..914e67a5 100644 --- a/dGame/dBehaviors/BasicAttackBehavior.cpp +++ b/dGame/dBehaviors/BasicAttackBehavior.cpp @@ -1,8 +1,10 @@ #include "BasicAttackBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" +#include "dZoneManager.h" +#include "WorldConfig.h" #include "DestroyableComponent.h" #include "BehaviorContext.h" #include "eBasicAttackSuccessTypes.h" @@ -13,8 +15,14 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi auto* destroyableComponent = entity->GetComponent(); if (destroyableComponent != nullptr) { - PlayFx(u"onhit", entity->GetObjectID()); + PlayFx(u"onhit", entity->GetObjectID()); //This damage animation doesn't seem to play consistently destroyableComponent->Damage(this->m_MaxDamage, context->originator, context->skillID); + + //Handle player damage cooldown + if (entity->IsPlayer() && !this->m_DontApplyImmune) { + const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime; + destroyableComponent->SetDamageCooldownTimer(immunityTime); + } } this->m_OnSuccess->Handle(context, bitStream, branch); @@ -26,10 +34,10 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi uint16_t allocatedBits{}; if (!bitStream->Read(allocatedBits) || allocatedBits == 0) { - Game::logger->LogDebug("BasicAttackBehavior", "No allocated bits"); + LOG_DEBUG("No allocated bits"); return; } - Game::logger->LogDebug("BasicAttackBehavior", "Number of allocated bits %i", allocatedBits); + LOG_DEBUG("Number of allocated bits %i", allocatedBits); const auto baseAddress = bitStream->GetReadOffset(); DoHandleBehavior(context, bitStream, branch); @@ -40,13 +48,13 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { auto* targetEntity = Game::entityManager->GetEntity(branch.target); if (!targetEntity) { - Game::logger->Log("BasicAttackBehavior", "Target targetEntity %llu not found.", branch.target); + LOG("Target targetEntity %llu not found.", branch.target); return; } auto* destroyableComponent = targetEntity->GetComponent(); if (!destroyableComponent) { - Game::logger->Log("BasicAttackBehavior", "No destroyable found on the obj/lot %llu/%i", branch.target, targetEntity->GetLOT()); + LOG("No destroyable found on the obj/lot %llu/%i", branch.target, targetEntity->GetLOT()); return; } @@ -55,7 +63,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit bool isSuccess{}; if (!bitStream->Read(isBlocked)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read isBlocked"); + LOG("Unable to read isBlocked"); return; } @@ -67,30 +75,31 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit } if (!bitStream->Read(isImmune)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read isImmune"); + LOG("Unable to read isImmune"); return; } if (isImmune) { + LOG_DEBUG("Target targetEntity %llu is immune!", branch.target); this->m_OnFailImmune->Handle(context, bitStream, branch); return; } if (!bitStream->Read(isSuccess)) { - Game::logger->Log("BasicAttackBehavior", "failed to read success from bitstream"); + LOG("failed to read success from bitstream"); return; } if (isSuccess) { uint32_t armorDamageDealt{}; if (!bitStream->Read(armorDamageDealt)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read armorDamageDealt"); + LOG("Unable to read armorDamageDealt"); return; } uint32_t healthDamageDealt{}; if (!bitStream->Read(healthDamageDealt)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read healthDamageDealt"); + LOG("Unable to read healthDamageDealt"); return; } @@ -103,7 +112,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit bool died{}; if (!bitStream->Read(died)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read died"); + LOG("Unable to read died"); return; } auto previousArmor = destroyableComponent->GetArmor(); @@ -114,7 +123,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit uint8_t successState{}; if (!bitStream->Read(successState)) { - Game::logger->Log("BasicAttackBehavior", "Unable to read success state"); + LOG("Unable to read success state"); return; } @@ -127,7 +136,7 @@ void BasicAttackBehavior::DoHandleBehavior(BehaviorContext* context, RakNet::Bit break; default: if (static_cast(successState) != eBasicAttackSuccessTypes::FAILIMMUNE) { - Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!", successState); + LOG("Unknown success state (%i)!", successState); return; } this->m_OnFailImmune->Handle(context, bitStream, branch); @@ -157,13 +166,13 @@ void BasicAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { auto* targetEntity = Game::entityManager->GetEntity(branch.target); if (!targetEntity) { - Game::logger->Log("BasicAttackBehavior", "Target entity %llu is null!", branch.target); + LOG("Target entity %llu is null!", branch.target); return; } auto* destroyableComponent = targetEntity->GetComponent(); if (!destroyableComponent || !destroyableComponent->GetParent()) { - Game::logger->Log("BasicAttackBehavior", "No destroyable component on %llu", branch.target); + LOG("No destroyable component on %llu", branch.target); return; } @@ -178,11 +187,11 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet return; } - const bool isImmune = destroyableComponent->IsImmune(); - + const bool isImmune = destroyableComponent->IsImmune() || destroyableComponent->IsCooldownImmune(); bitStream->Write(isImmune); if (isImmune) { + LOG_DEBUG("Target targetEntity %llu is immune!", branch.target); this->m_OnFailImmune->Calculate(context, bitStream, branch); return; } @@ -203,6 +212,11 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet bitStream->Write(isSuccess); + //Handle player damage cooldown + if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) { + destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig()->globalImmunityTime); + } + eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE; if (isSuccess) { if (healthDamageDealt >= 1) { @@ -227,7 +241,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet break; default: if (static_cast(successState) != eBasicAttackSuccessTypes::FAILIMMUNE) { - Game::logger->Log("BasicAttackBehavior", "Unknown success state (%i)!", successState); + LOG("Unknown success state (%i)!", successState); break; } this->m_OnFailImmune->Calculate(context, bitStream, branch); @@ -236,6 +250,8 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet } void BasicAttackBehavior::Load() { + this->m_DontApplyImmune = GetBoolean("dont_apply_immune"); + this->m_MinDamage = GetInt("min damage"); if (this->m_MinDamage == 0) this->m_MinDamage = 1; diff --git a/dGame/dBehaviors/BasicAttackBehavior.h b/dGame/dBehaviors/BasicAttackBehavior.h index f6e3fa28..6525c343 100644 --- a/dGame/dBehaviors/BasicAttackBehavior.h +++ b/dGame/dBehaviors/BasicAttackBehavior.h @@ -10,14 +10,14 @@ public: /** * @brief Reads a 16bit short from the bitStream and when the actual behavior handling finishes with all of its branches, the bitStream * is then offset to after the allocated bits for this stream. - * + * */ void DoHandleBehavior(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch); /** * @brief Handles a client initialized Basic Attack Behavior cast to be deserialized and verified on the server. - * - * @param context The Skill's Behavior context. All behaviors in the same tree share the same context + * + * @param context The Skill's Behavior context. All behaviors in the same tree share the same context * @param bitStream The bitStream to deserialize. BitStreams will always check their bounds before reading in a behavior * and will fail gracefully if an overread is detected. * @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down. @@ -27,13 +27,13 @@ public: /** * @brief Writes a 16bit short to the bitStream and when the actual behavior calculation finishes with all of its branches, the number * of bits used is then written to where the 16bit short initially was. - * + * */ void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; /** * @brief Calculates a server initialized Basic Attack Behavior cast to be serialized to the client - * + * * @param context The Skill's Behavior context. All behaviors in the same tree share the same context * @param bitStream The bitStream to serialize to. * @param branch The context of this specific branch of the Skill Behavior. Changes based on which branch you are going down. @@ -44,10 +44,12 @@ public: * @brief Loads this Behaviors parameters from the database. For this behavior specifically: * max and min damage will always be the same. If min is less than max, they are both set to max. * If an action is not in the database, then no action is taken for that result. - * + * */ void Load() override; private: + bool m_DontApplyImmune; + uint32_t m_MinDamage; uint32_t m_MaxDamage; diff --git a/dGame/dBehaviors/Behavior.cpp b/dGame/dBehaviors/Behavior.cpp index 6fe84a9f..64bb03f5 100644 --- a/dGame/dBehaviors/Behavior.cpp +++ b/dGame/dBehaviors/Behavior.cpp @@ -4,7 +4,7 @@ #include "Behavior.h" #include "CDActivitiesTable.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "BehaviorTemplates.h" #include "BehaviorBranchContext.h" #include @@ -64,6 +64,7 @@ #include "FallSpeedBehavior.h" #include "ChangeIdleFlagsBehavior.h" #include "DarkInspirationBehavior.h" +#include "ConsumeItemBehavior.h" //CDClient includes #include "CDBehaviorParameterTable.h" @@ -81,7 +82,7 @@ CDBehaviorParameterTable* Behavior::BehaviorParameterTable = nullptr; Behavior* Behavior::GetBehavior(const uint32_t behaviorId) { if (BehaviorParameterTable == nullptr) { - BehaviorParameterTable = CDClientManager::Instance().GetTable(); + BehaviorParameterTable = CDClientManager::GetTable(); } const auto pair = Cache.find(behaviorId); @@ -175,7 +176,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { case BehaviorTemplates::BEHAVIOR_SPEED: behavior = new SpeedBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: + case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: behavior = new DarkInspirationBehavior(behaviorId); break; case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: @@ -200,7 +201,9 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { case BehaviorTemplates::BEHAVIOR_SKILL_EVENT: behavior = new SkillEventBehavior(behaviorId); break; - case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: break; + case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: + behavior = new ConsumeItemBehavior(behaviorId); + break; case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED: behavior = new SkillCastFailedBehavior(behaviorId); break; @@ -278,12 +281,12 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { case BehaviorTemplates::BEHAVIOR_MOUNT: break; case BehaviorTemplates::BEHAVIOR_SKILL_SET: break; default: - //Game::logger->Log("Behavior", "Failed to load behavior with invalid template id (%i)!", templateId); + //LOG("Failed to load behavior with invalid template id (%i)!", templateId); break; } if (behavior == nullptr) { - //Game::logger->Log("Behavior", "Failed to load unimplemented template id (%i)!", templateId); + //LOG("Failed to load unimplemented template id (%i)!", templateId); behavior = new EmptyBehavior(behaviorId); } @@ -294,7 +297,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) { } BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) { - auto behaviorTemplateTable = CDClientManager::Instance().GetTable(); + auto behaviorTemplateTable = CDClientManager::GetTable(); BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY; // Find behavior template by its behavior id. Default to 0. @@ -306,7 +309,7 @@ BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) { } if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) { - Game::logger->Log("Behavior", "Failed to load behavior template with id (%i)!", behaviorId); + LOG("Failed to load behavior template with id (%i)!", behaviorId); } return templateID; @@ -365,11 +368,11 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID if (!type.empty()) { typeQuery.bind(1, typeString.c_str()); - typeQuery.bind(2, (int)effectId); + typeQuery.bind(2, static_cast(effectId)); result = typeQuery.execQuery(); } else { - idQuery.bind(1, (int)effectId); + idQuery.bind(1, static_cast(effectId)); result = idQuery.execQuery(); } @@ -402,7 +405,7 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID } Behavior::Behavior(const uint32_t behaviorId) { - auto behaviorTemplateTable = CDClientManager::Instance().GetTable(); + auto behaviorTemplateTable = CDClientManager::GetTable(); CDBehaviorTemplate templateInDatabase{}; @@ -426,7 +429,7 @@ Behavior::Behavior(const uint32_t behaviorId) { // Make sure we do not proceed if we are trying to load an invalid behavior if (templateInDatabase.behaviorID == 0) { - Game::logger->Log("Behavior", "Failed to load behavior with id (%i)!", behaviorId); + LOG("Failed to load behavior with id (%i)!", behaviorId); this->m_effectId = 0; this->m_effectHandle = nullptr; @@ -445,7 +448,7 @@ Behavior::Behavior(const uint32_t behaviorId) { float Behavior::GetFloat(const std::string& name, const float defaultValue) const { // Get the behavior parameter entry and return its value. - if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance().GetTable(); + if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::GetTable(); return BehaviorParameterTable->GetValue(this->m_behaviorId, name, defaultValue); } @@ -473,7 +476,7 @@ Behavior* Behavior::GetAction(float value) const { std::map Behavior::GetParameterNames() const { std::map templatesInDatabase; // Find behavior template by its behavior id. - if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::Instance().GetTable(); + if (!BehaviorParameterTable) BehaviorParameterTable = CDClientManager::GetTable(); if (BehaviorParameterTable) { templatesInDatabase = BehaviorParameterTable->GetParametersByBehaviorID(this->m_behaviorId); } diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index 43ba8457..e4f3011c 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -4,17 +4,18 @@ #include "EntityManager.h" #include "SkillComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "dServer.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include #include "DestroyableComponent.h" #include "EchoSyncSkill.h" #include "PhantomPhysicsComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "eReplicaComponentType.h" +#include "TeamManager.h" #include "eConnectionType.h" BehaviorSyncEntry::BehaviorSyncEntry() { @@ -30,7 +31,7 @@ uint32_t BehaviorContext::GetUniqueSkillId() const { auto* entity = Game::entityManager->GetEntity(this->originator); if (entity == nullptr) { - Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator); + LOG("Invalid entity for (%llu)!", this->originator); return 0; } @@ -38,7 +39,7 @@ uint32_t BehaviorContext::GetUniqueSkillId() const { auto* component = entity->GetComponent(); if (component == nullptr) { - Game::logger->Log("BehaviorContext", "No skill component attached to (%llu)!", this->originator);; + LOG("No skill component attached to (%llu)!", this->originator);; return 0; } @@ -125,7 +126,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit } if (!found) { - Game::logger->Log("BehaviorContext", "Failed to find behavior sync entry with sync id (%i)!", syncId); + LOG("Failed to find behavior sync entry with sync id (%i)!", syncId); return; } @@ -134,7 +135,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream* bit const auto branch = entry.branchContext; if (behavior == nullptr) { - Game::logger->Log("BehaviorContext", "Invalid behavior for sync id (%i)!", syncId); + LOG("Invalid behavior for sync id (%i)!", syncId); return; } @@ -248,12 +249,12 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) { entry.behavior->SyncCalculation(this, bitStream, entry.branchContext); if (!clientInitalized) { - echo.sBitStream.assign((char*)bitStream->GetData(), bitStream->GetNumberOfBytesUsed()); + echo.sBitStream.assign(reinterpret_cast(bitStream->GetData()), bitStream->GetNumberOfBytesUsed()); // Write message RakNet::BitStream message; - PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); + BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); message.Write(this->originator); echo.Serialize(&message); @@ -307,46 +308,123 @@ void BehaviorContext::Reset() { this->scheduledUpdates.clear(); } -std::vector BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const { - auto* entity = Game::entityManager->GetEntity(this->caster); +void BehaviorContext::FilterTargets(std::vector& targets, std::forward_list& ignoreFactionList, std::forward_list& includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const { - std::vector targets; - - if (entity == nullptr) { - Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator); - - return targets; + // if we aren't targeting anything, then clear the targets vector + if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) { + targets.clear(); + return; } - if (!ignoreFaction && !includeFaction) { - for (auto entry : entity->GetTargetsInPhantom()) { - auto* instance = Game::entityManager->GetEntity(entry); - - if (instance == nullptr) { - continue; - } - - targets.push_back(entry); - } + // if the caster is not there, return empty targets list + auto* caster = Game::entityManager->GetEntity(this->caster); + if (!caster) { + LOG_DEBUG("Invalid caster for (%llu)!", this->originator); + targets.clear(); + return; } - if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) { - DestroyableComponent* destroyableComponent; - if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) { - return targets; + auto index = targets.begin(); + while (index != targets.end()) { + auto candidate = *index; + + // make sure we don't have a nullptr + if (!candidate) { + index = targets.erase(index); + continue; } - auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS); - for (auto* candidate : entities) { - const auto id = candidate->GetObjectID(); + // handle targeting the caster + if (candidate == caster){ + // if we aren't targeting self, erase, otherise increment and continue + if (!targetSelf) index = targets.erase(index); + else index++; + continue; + } - if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) { - targets.push_back(id); + // make sure that the entity is targetable + if (!CheckTargetingRequirements(candidate)) { + index = targets.erase(index); + continue; + } + + // get factions to check against + // CheckTargetingRequirements checks for a destroyable component + // but we check again because bounds check are necessary + auto candidateDestroyableComponent = candidate->GetComponent(); + if (!candidateDestroyableComponent) { + index = targets.erase(index); + continue; + } + + // if they are dead, then earse and continue + 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)){ + index++; + continue; + } + + // check if they are a team member + if (targetTeam){ + auto* team = TeamManager::Instance()->GetTeam(this->caster); + 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()){ + index++; + continue; + } } } - } - return targets; + // if the caster doesn't have a destroyable component, return an empty targets list + auto* casterDestroyableComponent = caster->GetComponent(); + if (!casterDestroyableComponent) { + targets.clear(); + return; + } + + // if we arent targeting a friend, and they are a friend OR + // if we are not targeting enemies and they are an enemy OR. + // if we are ignoring their faction is explicitly ignored + // erase and continue + auto isEnemy = casterDestroyableComponent->IsEnemy(candidate); + if ((!targetFriend && !isEnemy) || + (!targetEnemy && isEnemy) || + CheckFactionList(ignoreFactionList, candidateFactions)) { + index = targets.erase(index); + continue; + } + + index++; + } + return; +} + +// some basic checks as well as the check that matters for this: if the quickbuild is complete +bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const { + // if the target is a nullptr, then it's not valid + if (!target) return false; + + // ignore quickbuilds that aren't completed + auto* targetQuickbuildComponent = target->GetComponent(); + if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eQuickBuildState::COMPLETED) return false; + + return true; +} + +// returns true if any of the object factions are in the faction list +bool BehaviorContext::CheckFactionList(std::forward_list& factionList, std::vector& 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; + } + return false; } diff --git a/dGame/dBehaviors/BehaviorContext.h b/dGame/dBehaviors/BehaviorContext.h index 117f328d..333dc9a8 100644 --- a/dGame/dBehaviors/BehaviorContext.h +++ b/dGame/dBehaviors/BehaviorContext.h @@ -6,6 +6,7 @@ #include "GameMessages.h" #include +#include class Behavior; @@ -106,7 +107,11 @@ struct BehaviorContext void Reset(); - std::vector GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false) const; + void FilterTargets(std::vector& targetsReference, std::forward_list& ignoreFaction, std::forward_list& includeFaction, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false, const bool targetTeam = false) const; + + bool CheckTargetingRequirements(const Entity* target) const; + + bool CheckFactionList(std::forward_list& factionList, std::vector& objectsFactions) const; explicit BehaviorContext(LWOOBJID originator, bool calculation = false); diff --git a/dGame/dBehaviors/BehaviorSlot.h b/dGame/dBehaviors/BehaviorSlot.h index 51219b80..5afddea7 100644 --- a/dGame/dBehaviors/BehaviorSlot.h +++ b/dGame/dBehaviors/BehaviorSlot.h @@ -2,9 +2,9 @@ #ifndef BEHAVIORSLOT_H #define BEHAVIORSLOT_H +#include -enum class BehaviorSlot -{ +enum class BehaviorSlot : int32_t { Invalid = -1, Primary, Offhand, diff --git a/dGame/dBehaviors/BlockBehavior.cpp b/dGame/dBehaviors/BlockBehavior.cpp index 19db0267..88c6bb0b 100644 --- a/dGame/dBehaviors/BlockBehavior.cpp +++ b/dGame/dBehaviors/BlockBehavior.cpp @@ -4,7 +4,7 @@ #include "BehaviorBranchContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { @@ -13,7 +13,7 @@ void BlockBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea auto* entity = Game::entityManager->GetEntity(target); if (entity == nullptr) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -43,7 +43,7 @@ void BlockBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branc auto* entity = Game::entityManager->GetEntity(target); if (entity == nullptr) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/BuffBehavior.cpp b/dGame/dBehaviors/BuffBehavior.cpp index 2c06dbd4..7ac8b352 100644 --- a/dGame/dBehaviors/BuffBehavior.cpp +++ b/dGame/dBehaviors/BuffBehavior.cpp @@ -4,7 +4,7 @@ #include "BehaviorBranchContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { @@ -13,7 +13,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream auto* entity = Game::entityManager->GetEntity(target); if (entity == nullptr) { - Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target); + LOG("Invalid target (%llu)!", target); return; } @@ -21,7 +21,7 @@ void BuffBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream auto* component = entity->GetComponent(); if (component == nullptr) { - Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!", target); + LOG("Invalid target, no destroyable component (%llu)!", target); return; } @@ -47,7 +47,7 @@ void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch auto* entity = Game::entityManager->GetEntity(target); if (entity == nullptr) { - Game::logger->Log("BuffBehavior", "Invalid target (%llu)!", target); + LOG("Invalid target (%llu)!", target); return; } @@ -55,7 +55,7 @@ void BuffBehavior::UnCast(BehaviorContext* context, BehaviorBranchContext branch auto* component = entity->GetComponent(); if (component == nullptr) { - Game::logger->Log("BuffBehavior", "Invalid target, no destroyable component (%llu)!", target); + LOG("Invalid target, no destroyable component (%llu)!", target); return; } diff --git a/dGame/dBehaviors/CMakeLists.txt b/dGame/dBehaviors/CMakeLists.txt index 8a9368b9..c8cb0be0 100644 --- a/dGame/dBehaviors/CMakeLists.txt +++ b/dGame/dBehaviors/CMakeLists.txt @@ -16,6 +16,7 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp" "ChangeOrientationBehavior.cpp" "ChargeUpBehavior.cpp" "ClearTargetBehavior.cpp" + "ConsumeItemBehavior.cpp" "DamageAbsorptionBehavior.cpp" "DamageReductionBehavior.cpp" "DarkInspirationBehavior.cpp" @@ -53,4 +54,9 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp" "TargetCasterBehavior.cpp" "TauntBehavior.cpp" "VentureVisionBehavior.cpp" - "VerifyBehavior.cpp" PARENT_SCOPE) + "VerifyBehavior.cpp") + +add_library(dBehaviors STATIC ${DGAME_DBEHAVIORS_SOURCES}) +target_link_libraries(dBehaviors PUBLIC dPhysics) +target_include_directories(dBehaviors PUBLIC ".") +target_precompile_headers(dBehaviors REUSE_FROM dGameBase) diff --git a/dGame/dBehaviors/CarBoostBehavior.cpp b/dGame/dBehaviors/CarBoostBehavior.cpp index e2929863..eb22cb09 100644 --- a/dGame/dBehaviors/CarBoostBehavior.cpp +++ b/dGame/dBehaviors/CarBoostBehavior.cpp @@ -5,7 +5,7 @@ #include "BehaviorContext.h" #include "CharacterComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "PossessableComponent.h" void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { @@ -17,7 +17,7 @@ void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt return; } - Game::logger->Log("Car boost", "Activating car boost!"); + LOG("Activating car boost!"); auto* possessableComponent = entity->GetComponent(); if (possessableComponent != nullptr) { @@ -27,7 +27,7 @@ void CarBoostBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt auto* characterComponent = possessor->GetComponent(); if (characterComponent != nullptr) { - Game::logger->Log("Car boost", "Tracking car boost!"); + LOG("Tracking car boost!"); characterComponent->UpdatePlayerStatistic(RacingCarBoostsActivated); } } diff --git a/dGame/dBehaviors/ChainBehavior.cpp b/dGame/dBehaviors/ChainBehavior.cpp index ec0f8969..4a20c151 100644 --- a/dGame/dBehaviors/ChainBehavior.cpp +++ b/dGame/dBehaviors/ChainBehavior.cpp @@ -1,13 +1,13 @@ #include "ChainBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { uint32_t chainIndex{}; if (!bitStream->Read(chainIndex)) { - Game::logger->Log("ChainBehavior", "Unable to read chainIndex from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read chainIndex from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; } @@ -16,7 +16,7 @@ void ChainBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea if (chainIndex < this->m_behaviors.size()) { this->m_behaviors.at(chainIndex)->Handle(context, bitStream, branch); } else { - Game::logger->Log("ChainBehavior", "chainIndex out of bounds, aborting handle of chain %i bits unread %i", chainIndex, bitStream->GetNumberOfUnreadBits()); + LOG("chainIndex out of bounds, aborting handle of chain %i bits unread %i", chainIndex, bitStream->GetNumberOfUnreadBits()); } } diff --git a/dGame/dBehaviors/ChargeUpBehavior.cpp b/dGame/dBehaviors/ChargeUpBehavior.cpp index 4c7c3dac..cf737e19 100644 --- a/dGame/dBehaviors/ChargeUpBehavior.cpp +++ b/dGame/dBehaviors/ChargeUpBehavior.cpp @@ -2,13 +2,13 @@ #include "BehaviorBranchContext.h" #include "BehaviorContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void ChargeUpBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { uint32_t handle{}; if (!bitStream->Read(handle)) { - Game::logger->Log("ChargeUpBehavior", "Unable to read handle from bitStream, aborting Handle! variable_type"); + LOG("Unable to read handle from bitStream, aborting Handle! variable_type"); return; }; diff --git a/dGame/dBehaviors/ConsumeItemBehavior.cpp b/dGame/dBehaviors/ConsumeItemBehavior.cpp new file mode 100644 index 00000000..c3b76fcb --- /dev/null +++ b/dGame/dBehaviors/ConsumeItemBehavior.cpp @@ -0,0 +1,31 @@ +#include "ConsumeItemBehavior.h" +#include "BehaviorContext.h" +#include "BehaviorBranchContext.h" +#include "InventoryComponent.h" + +void ConsumeItemBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + auto action_to_cast = m_ActionNotConsumed; + if (this->m_ConsumeLOT != -1) { + auto caster = Game::entityManager->GetEntity(context->caster); + if (!caster) return; + + auto inventoryComponent = caster->GetComponent(); + if (!inventoryComponent) return; + + if (inventoryComponent->RemoveItem(this->m_ConsumeLOT, this->m_NumToConsume, eInventoryType::INVALID, false, true)){ + action_to_cast = m_ActionConsumed; + } + } + if(action_to_cast) action_to_cast->Handle(context, bitStream, branch); +} + +void ConsumeItemBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { + Handle(context, bitStream, branch); +} + +void ConsumeItemBehavior::Load() { + this->m_ConsumeLOT = GetInt("consume_lot", -1); + this->m_NumToConsume = GetInt("num_to_consume", 1); + this->m_ActionNotConsumed = GetAction("action_not_consumed"); + this->m_ActionConsumed = GetAction("action_consumed"); +} diff --git a/dGame/dBehaviors/ConsumeItemBehavior.h b/dGame/dBehaviors/ConsumeItemBehavior.h new file mode 100644 index 00000000..f3eeb330 --- /dev/null +++ b/dGame/dBehaviors/ConsumeItemBehavior.h @@ -0,0 +1,17 @@ +#pragma once +#include "Behavior.h" + +class ConsumeItemBehavior final : public Behavior +{ +public: + explicit ConsumeItemBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {} + void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; + void Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; + void Load() override; + +private: + LOT m_ConsumeLOT; + uint32_t m_NumToConsume; + Behavior* m_ActionNotConsumed; + Behavior* m_ActionConsumed; +}; diff --git a/dGame/dBehaviors/DamageAbsorptionBehavior.cpp b/dGame/dBehaviors/DamageAbsorptionBehavior.cpp index f9936767..880550e3 100644 --- a/dGame/dBehaviors/DamageAbsorptionBehavior.cpp +++ b/dGame/dBehaviors/DamageAbsorptionBehavior.cpp @@ -4,14 +4,14 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -37,7 +37,7 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon auto* target = Game::entityManager->GetEntity(second); if (target == nullptr) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second); + LOG("Failed to find target (%llu)!", second); return; } diff --git a/dGame/dBehaviors/DamageReductionBehavior.cpp b/dGame/dBehaviors/DamageReductionBehavior.cpp index 7645ec7b..bf32360e 100644 --- a/dGame/dBehaviors/DamageReductionBehavior.cpp +++ b/dGame/dBehaviors/DamageReductionBehavior.cpp @@ -4,14 +4,14 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" void DamageReductionBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -35,7 +35,7 @@ void DamageReductionBehavior::Timer(BehaviorContext* context, BehaviorBranchCont auto* target = Game::entityManager->GetEntity(second); if (target == nullptr) { - Game::logger->Log("DamageReductionBehavior", "Failed to find target (%llu)!", second); + LOG("Failed to find target (%llu)!", second); return; } diff --git a/dGame/dBehaviors/DarkInspirationBehavior.cpp b/dGame/dBehaviors/DarkInspirationBehavior.cpp index 4e9890e3..27a7c634 100644 --- a/dGame/dBehaviors/DarkInspirationBehavior.cpp +++ b/dGame/dBehaviors/DarkInspirationBehavior.cpp @@ -10,7 +10,7 @@ void DarkInspirationBehavior::Handle(BehaviorContext* context, RakNet::BitStream auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target); + LOG_DEBUG("Failed to find target (%llu)!", branch.target); return; } @@ -29,7 +29,7 @@ void DarkInspirationBehavior::Calculate(BehaviorContext* context, RakNet::BitStr auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->LogDebug("DarkInspirationBehavior", "Failed to find target (%llu)!", branch.target); + LOG_DEBUG("Failed to find target (%llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/ForceMovementBehavior.cpp b/dGame/dBehaviors/ForceMovementBehavior.cpp index 97208236..2ba3f2e1 100644 --- a/dGame/dBehaviors/ForceMovementBehavior.cpp +++ b/dGame/dBehaviors/ForceMovementBehavior.cpp @@ -4,7 +4,7 @@ #include "ControllablePhysicsComponent.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { 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) { @@ -13,7 +13,7 @@ void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* uint32_t handle{}; if (!bitStream->Read(handle)) { - Game::logger->Log("ForceMovementBehavior", "Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read handle from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; } context->RegisterSyncBehavior(handle, this, branch, this->m_Duration); @@ -22,13 +22,13 @@ void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream* void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { uint32_t next{}; if (!bitStream->Read(next)) { - Game::logger->Log("ForceMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); return; } LWOOBJID target{}; if (!bitStream->Read(target)) { - Game::logger->Log("ForceMovementBehavior", "Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read target from bitStream, aborting Sync! %i", bitStream->GetNumberOfUnreadBits()); return; } diff --git a/dGame/dBehaviors/HealBehavior.cpp b/dGame/dBehaviors/HealBehavior.cpp index dae009d4..3eb4928c 100644 --- a/dGame/dBehaviors/HealBehavior.cpp +++ b/dGame/dBehaviors/HealBehavior.cpp @@ -1,7 +1,7 @@ #include "HealBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" #include "DestroyableComponent.h" #include "eReplicaComponentType.h" @@ -11,7 +11,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea auto* entity = Game::entityManager->GetEntity(branch.target); if (entity == nullptr) { - Game::logger->Log("HealBehavior", "Failed to find entity for (%llu)!", branch.target); + LOG("Failed to find entity for (%llu)!", branch.target); return; } @@ -19,7 +19,7 @@ void HealBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_strea auto* destroyable = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); if (destroyable == nullptr) { - Game::logger->Log("HealBehavior", "Failed to find destroyable component for %(llu)!", branch.target); + LOG("Failed to find destroyable component for %(llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/ImaginationBehavior.cpp b/dGame/dBehaviors/ImaginationBehavior.cpp index 50f7e046..c57fd0c7 100644 --- a/dGame/dBehaviors/ImaginationBehavior.cpp +++ b/dGame/dBehaviors/ImaginationBehavior.cpp @@ -3,7 +3,7 @@ #include "DestroyableComponent.h" #include "dpWorld.h" #include "EntityManager.h" -#include "dLogger.h" +#include "Logger.h" void ImaginationBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_stream, const BehaviorBranchContext branch) { diff --git a/dGame/dBehaviors/ImmunityBehavior.cpp b/dGame/dBehaviors/ImmunityBehavior.cpp index ba4a8b77..9824c479 100644 --- a/dGame/dBehaviors/ImmunityBehavior.cpp +++ b/dGame/dBehaviors/ImmunityBehavior.cpp @@ -4,7 +4,7 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" #include "ControllablePhysicsComponent.h" #include "eStateChangeType.h" @@ -13,7 +13,7 @@ void ImmunityBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitSt auto* target = Game::entityManager->GetEntity(branch.target); if (!target) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -59,7 +59,7 @@ void ImmunityBehavior::Timer(BehaviorContext* context, BehaviorBranchContext bra auto* target = Game::entityManager->GetEntity(second); if (!target) { - Game::logger->Log("DamageAbsorptionBehavior", "Failed to find target (%llu)!", second); + LOG("Failed to find target (%llu)!", second); return; } diff --git a/dGame/dBehaviors/InterruptBehavior.cpp b/dGame/dBehaviors/InterruptBehavior.cpp index ffe3bb8b..bd3f0c4a 100644 --- a/dGame/dBehaviors/InterruptBehavior.cpp +++ b/dGame/dBehaviors/InterruptBehavior.cpp @@ -2,7 +2,7 @@ #include "BehaviorBranchContext.h" #include "BehaviorContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" #include "SkillComponent.h" @@ -12,7 +12,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS bool unknown = false; if (!bitStream->Read(unknown)) { - Game::logger->Log("InterruptBehavior", "Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; @@ -23,7 +23,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS bool unknown = false; if (!bitStream->Read(unknown)) { - Game::logger->Log("InterruptBehavior", "Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; @@ -35,7 +35,7 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitS bool unknown = false; if (!bitStream->Read(unknown)) { - Game::logger->Log("InterruptBehavior", "Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; } diff --git a/dGame/dBehaviors/JetPackBehavior.cpp b/dGame/dBehaviors/JetPackBehavior.cpp index 134dd0fb..7fcd78b0 100644 --- a/dGame/dBehaviors/JetPackBehavior.cpp +++ b/dGame/dBehaviors/JetPackBehavior.cpp @@ -38,10 +38,12 @@ void JetPackBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bit } void JetPackBehavior::Load() { - this->m_WarningEffectID = GetInt("warning_effect_id"); - this->m_Airspeed = GetFloat("airspeed"); - this->m_MaxAirspeed = GetFloat("max_airspeed"); - this->m_VerticalVelocity = GetFloat("vertical_velocity"); - this->m_EnableHover = GetBoolean("enable_hover"); - this->m_BypassChecks = GetBoolean("bypass_checks", true); + this->m_WarningEffectID = GetInt("warning_effect_id", -1); + this->m_Airspeed = GetFloat("airspeed", 10); + this->m_MaxAirspeed = GetFloat("max_airspeed", 15); + this->m_VerticalVelocity = GetFloat("vertical_velocity", 1); + this->m_EnableHover = GetBoolean("enable_hover", false); + + // TODO: Implement proper jetpack checks, so we can set this default to false + this->m_BypassChecks = GetBoolean("bypass_checks", true); } diff --git a/dGame/dBehaviors/KnockbackBehavior.cpp b/dGame/dBehaviors/KnockbackBehavior.cpp index d67ebbc8..d0f86597 100644 --- a/dGame/dBehaviors/KnockbackBehavior.cpp +++ b/dGame/dBehaviors/KnockbackBehavior.cpp @@ -7,13 +7,13 @@ #include "GameMessages.h" #include "DestroyableComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void KnockbackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { bool unknown{}; if (!bitStream->Read(unknown)) { - Game::logger->Log("KnockbackBehavior", "Unable to read unknown from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read unknown from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; } diff --git a/dGame/dBehaviors/MovementSwitchBehavior.cpp b/dGame/dBehaviors/MovementSwitchBehavior.cpp index 0c11380f..cddeeea9 100644 --- a/dGame/dBehaviors/MovementSwitchBehavior.cpp +++ b/dGame/dBehaviors/MovementSwitchBehavior.cpp @@ -1,7 +1,7 @@ #include "MovementSwitchBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, const BehaviorBranchContext branch) { uint32_t movementType{}; @@ -15,7 +15,7 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* this->m_movingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { return; } - Game::logger->Log("MovementSwitchBehavior", "Unable to read movementType from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; diff --git a/dGame/dBehaviors/OverTimeBehavior.cpp b/dGame/dBehaviors/OverTimeBehavior.cpp index 49077f0f..f66a5253 100644 --- a/dGame/dBehaviors/OverTimeBehavior.cpp +++ b/dGame/dBehaviors/OverTimeBehavior.cpp @@ -2,7 +2,7 @@ #include "BehaviorBranchContext.h" #include "BehaviorContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" #include "SkillComponent.h" #include "DestroyableComponent.h" @@ -41,7 +41,7 @@ void OverTimeBehavior::Load() { m_Action = GetInt("action"); // Since m_Action is a skillID and not a behavior, get is correlated behaviorID. - CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable(); + CDSkillBehaviorTable* skillTable = CDClientManager::GetTable(); m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID; m_Delay = GetFloat("delay"); diff --git a/dGame/dBehaviors/ProjectileAttackBehavior.cpp b/dGame/dBehaviors/ProjectileAttackBehavior.cpp index eb435d7c..504afc69 100644 --- a/dGame/dBehaviors/ProjectileAttackBehavior.cpp +++ b/dGame/dBehaviors/ProjectileAttackBehavior.cpp @@ -3,23 +3,23 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "SkillComponent.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "eObjectBits.h" void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { LWOOBJID target{}; if (!bitStream->Read(target)) { - Game::logger->Log("ProjectileAttackBehavior", "Unable to read target from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read target from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; auto* entity = Game::entityManager->GetEntity(context->originator); if (entity == nullptr) { - Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator); + LOG("Failed to find originator (%llu)!", context->originator); return; } @@ -27,15 +27,15 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea auto* skillComponent = entity->GetComponent(); if (skillComponent == nullptr) { - Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!", -context->originator); + LOG("Failed to find skill component for (%llu)!", -context->originator); return; } if (m_useMouseposit && !branch.isSync) { - NiPoint3 targetPosition = NiPoint3::ZERO; + NiPoint3 targetPosition = NiPoint3Constant::ZERO; if (!bitStream->Read(targetPosition)) { - Game::logger->Log("ProjectileAttackBehavior", "Unable to read targetPosition from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read targetPosition from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; } @@ -46,7 +46,7 @@ void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStrea LWOOBJID projectileId{}; if (!bitStream->Read(projectileId)) { - Game::logger->Log("ProjectileAttackBehavior", "Unable to read projectileId from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read projectileId from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; @@ -64,7 +64,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt auto* entity = Game::entityManager->GetEntity(context->originator); if (entity == nullptr) { - Game::logger->Log("ProjectileAttackBehavior", "Failed to find originator (%llu)!", context->originator); + LOG("Failed to find originator (%llu)!", context->originator); return; } @@ -72,7 +72,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt auto* skillComponent = entity->GetComponent(); if (skillComponent == nullptr) { - Game::logger->Log("ProjectileAttackBehavior", "Failed to find skill component for (%llu)!", context->originator); + LOG("Failed to find skill component for (%llu)!", context->originator); return; @@ -81,7 +81,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt auto* other = Game::entityManager->GetEntity(branch.target); if (other == nullptr) { - Game::logger->Log("ProjectileAttackBehavior", "Invalid projectile target (%llu)!", branch.target); + LOG("Invalid projectile target (%llu)!", branch.target); return; } @@ -106,7 +106,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt const auto maxTime = this->m_maxDistance / this->m_projectileSpeed; for (auto i = 0u; i < this->m_projectileCount; ++i) { - auto id = static_cast(ObjectIDManager::Instance()->GenerateObjectID()); + auto id = static_cast(ObjectIDManager::GenerateObjectID()); GeneralUtils::SetBit(id, eObjectBits::SPAWNED); diff --git a/dGame/dBehaviors/PropertyTeleportBehavior.cpp b/dGame/dBehaviors/PropertyTeleportBehavior.cpp index 309fc929..939568f6 100644 --- a/dGame/dBehaviors/PropertyTeleportBehavior.cpp +++ b/dGame/dBehaviors/PropertyTeleportBehavior.cpp @@ -40,7 +40,7 @@ void PropertyTeleportBehavior::Handle(BehaviorContext* context, RakNet::BitStrea if (zoneClone != 0) ChatPackets::SendSystemMessage(sysAddr, u"Transfering to your property!"); else ChatPackets::SendSystemMessage(sysAddr, u"Transfering back to previous world!"); - Game::logger->Log("PropertyTeleportBehavior", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", sysAddr.ToString(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); if (entity->GetCharacter()) { entity->GetCharacter()->SetZoneID(zoneID); entity->GetCharacter()->SetZoneInstance(zoneInstance); diff --git a/dGame/dBehaviors/RepairBehavior.cpp b/dGame/dBehaviors/RepairBehavior.cpp index d2967921..d0ce3fe8 100644 --- a/dGame/dBehaviors/RepairBehavior.cpp +++ b/dGame/dBehaviors/RepairBehavior.cpp @@ -3,7 +3,7 @@ #include "DestroyableComponent.h" #include "dpWorld.h" #include "EntityManager.h" -#include "dLogger.h" +#include "Logger.h" #include "Game.h" #include "eReplicaComponentType.h" @@ -11,7 +11,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str auto* entity = Game::entityManager->GetEntity(branch.target); if (entity == nullptr) { - Game::logger->Log("RepairBehavior", "Failed to find entity for (%llu)!", branch.target); + LOG("Failed to find entity for (%llu)!", branch.target); return; } @@ -19,7 +19,7 @@ void RepairBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bit_str auto* destroyable = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); if (destroyable == nullptr) { - Game::logger->Log("RepairBehavior", "Failed to find destroyable component for %(llu)!", branch.target); + LOG("Failed to find destroyable component for %(llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/SpawnBehavior.cpp b/dGame/dBehaviors/SpawnBehavior.cpp index 2803251a..dd45fb1b 100644 --- a/dGame/dBehaviors/SpawnBehavior.cpp +++ b/dGame/dBehaviors/SpawnBehavior.cpp @@ -4,9 +4,9 @@ #include "BehaviorBranchContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Entity.h" #include "EntityInfo.h" #include "eReplicaComponentType.h" @@ -15,7 +15,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea auto* origin = Game::entityManager->GetEntity(context->originator); if (origin == nullptr) { - Game::logger->Log("SpawnBehavior", "Failed to find self entity (%llu)!", context->originator); + LOG("Failed to find self entity (%llu)!", context->originator); return; } @@ -45,7 +45,7 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea ); if (entity == nullptr) { - Game::logger->Log("SpawnBehavior", "Failed to spawn entity (%i)!", this->m_lot); + LOG("Failed to spawn entity (%i)!", this->m_lot); return; } @@ -53,10 +53,10 @@ void SpawnBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStrea entity->SetOwnerOverride(context->originator); // Unset the flag to reposition the player, this makes it harder to glitch out of the map - auto* rebuildComponent = entity->GetComponent(); + auto* quickBuildComponent = entity->GetComponent(); - if (rebuildComponent != nullptr) { - rebuildComponent->SetRepositionPlayer(false); + if (quickBuildComponent != nullptr) { + quickBuildComponent->SetRepositionPlayer(false); } Game::entityManager->ConstructEntity(entity); @@ -82,7 +82,7 @@ void SpawnBehavior::Timer(BehaviorContext* context, const BehaviorBranchContext auto* entity = Game::entityManager->GetEntity(second); if (entity == nullptr) { - Game::logger->Log("SpawnBehavior", "Failed to find spawned entity (%llu)!", second); + LOG("Failed to find spawned entity (%llu)!", second); return; } diff --git a/dGame/dBehaviors/SpeedBehavior.cpp b/dGame/dBehaviors/SpeedBehavior.cpp index 5dbad8ec..f21fda7e 100644 --- a/dGame/dBehaviors/SpeedBehavior.cpp +++ b/dGame/dBehaviors/SpeedBehavior.cpp @@ -3,7 +3,7 @@ #include "ControllablePhysicsComponent.h" #include "BehaviorContext.h" #include "BehaviorBranchContext.h" -#include "dLogger.h" +#include "Logger.h" void SpeedBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { diff --git a/dGame/dBehaviors/StunBehavior.cpp b/dGame/dBehaviors/StunBehavior.cpp index 8e160338..02144f69 100644 --- a/dGame/dBehaviors/StunBehavior.cpp +++ b/dGame/dBehaviors/StunBehavior.cpp @@ -5,7 +5,7 @@ #include "BehaviorContext.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" #include "eReplicaComponentType.h" @@ -17,14 +17,14 @@ void StunBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream bool blocked{}; if (!bitStream->Read(blocked)) { - Game::logger->Log("StunBehavior", "Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->Log("StunBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -47,7 +47,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr auto* self = Game::entityManager->GetEntity(context->originator); if (self == nullptr) { - Game::logger->Log("StunBehavior", "Invalid self entity (%llu)!", context->originator); + LOG("Invalid self entity (%llu)!", context->originator); return; } @@ -82,7 +82,7 @@ void StunBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStr bitStream->Write(blocked); if (target == nullptr) { - Game::logger->Log("StunBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/SwitchBehavior.cpp b/dGame/dBehaviors/SwitchBehavior.cpp index d65191d2..db04650d 100644 --- a/dGame/dBehaviors/SwitchBehavior.cpp +++ b/dGame/dBehaviors/SwitchBehavior.cpp @@ -1,7 +1,7 @@ #include "SwitchBehavior.h" #include "BehaviorBranchContext.h" #include "EntityManager.h" -#include "dLogger.h" +#include "Logger.h" #include "DestroyableComponent.h" #include "BehaviorContext.h" #include "BuffComponent.h" @@ -11,7 +11,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre if (this->m_imagination > 0 || !this->m_isEnemyFaction) { if (!bitStream->Read(state)) { - Game::logger->Log("SwitchBehavior", "Unable to read state from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read state from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; } @@ -28,7 +28,7 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre return; } - Game::logger->LogDebug("SwitchBehavior", "[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination()); + LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination()); if (state) { this->m_actionTrue->Handle(context, bitStream, branch); diff --git a/dGame/dBehaviors/SwitchMultipleBehavior.cpp b/dGame/dBehaviors/SwitchMultipleBehavior.cpp index 23411429..faaa0d23 100644 --- a/dGame/dBehaviors/SwitchMultipleBehavior.cpp +++ b/dGame/dBehaviors/SwitchMultipleBehavior.cpp @@ -5,7 +5,7 @@ #include "BehaviorBranchContext.h" #include "CDActivitiesTable.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" @@ -13,7 +13,7 @@ void SwitchMultipleBehavior::Handle(BehaviorContext* context, RakNet::BitStream* float value{}; if (!bitStream->Read(value)) { - Game::logger->Log("SwitchMultipleBehavior", "Unable to read value from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read value from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; @@ -42,7 +42,7 @@ void SwitchMultipleBehavior::Load() { "(select bP2.value FROM BehaviorParameter bP2 WHERE bP2.behaviorID = ?1 AND bP2.parameterID LIKE 'value %' " "AND replace(bP1.parameterID, 'behavior ', '') = replace(bP2.parameterID, 'value ', '')) as value " "FROM BehaviorParameter bP1 WHERE bP1.behaviorID = ?1 AND bP1.parameterID LIKE 'behavior %';"); - query.bind(1, (int)this->m_behaviorId); + query.bind(1, static_cast(this->m_behaviorId)); auto result = query.execQuery(); diff --git a/dGame/dBehaviors/TacArcBehavior.cpp b/dGame/dBehaviors/TacArcBehavior.cpp index b9d871f4..0cea8213 100644 --- a/dGame/dBehaviors/TacArcBehavior.cpp +++ b/dGame/dBehaviors/TacArcBehavior.cpp @@ -1,27 +1,35 @@ #include "TacArcBehavior.h" #include "BehaviorBranchContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "Entity.h" #include "BehaviorContext.h" #include "BaseCombatAIComponent.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { - if (this->m_targetEnemy && this->m_usePickedTarget && branch.target > 0) { - this->m_action->Handle(context, bitStream, branch); + std::vector targets = {}; - return; + if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) { + auto target = Game::entityManager->GetEntity(branch.target); + if (!target) LOG("target %llu is null", branch.target); + else { + targets.push_back(target); + context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam); + if (!targets.empty()) { + this->m_action->Handle(context, bitStream, branch); + return; + } + } } - bool hit = false; - - if (!bitStream->Read(hit)) { - Game::logger->Log("TacArcBehavior", "Unable to read hit from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + bool hasTargets = false; + if (!bitStream->Read(hasTargets)) { + LOG("Unable to read hasTargets from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; @@ -29,159 +37,113 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre bool blocked = false; if (!bitStream->Read(blocked)) { - Game::logger->Log("TacArcBehavior", "Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read blocked from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; if (blocked) { this->m_blockedAction->Handle(context, bitStream, branch); - return; } } - if (hit) { + if (hasTargets) { uint32_t count = 0; - if (!bitStream->Read(count)) { - Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; - if (count > m_maxTargets && m_maxTargets > 0) { - count = m_maxTargets; + if (count > m_maxTargets) { + LOG("Bitstream has too many targets Max:%i Recv:%i", this->m_maxTargets, count); + return; } - std::vector targets; - - for (auto i = 0u; i < count; ++i) { + for (auto i = 0u; i < count; i++) { LWOOBJID id{}; if (!bitStream->Read(id)) { - Game::logger->Log("TacArcBehavior", "Unable to read id from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); + LOG("Unable to read id from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits()); return; }; - targets.push_back(id); + if (id != LWOOBJID_EMPTY) { + auto* canidate = Game::entityManager->GetEntity(id); + if (canidate) targets.push_back(canidate); + } else { + LOG("Bitstream has LWOOBJID_EMPTY as a target!"); + } } for (auto target : targets) { - branch.target = target; - + branch.target = target->GetObjectID(); this->m_action->Handle(context, bitStream, branch); } - } else { - this->m_missAction->Handle(context, bitStream, branch); - } + } else this->m_missAction->Handle(context, bitStream, branch); } void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { auto* self = Game::entityManager->GetEntity(context->originator); if (self == nullptr) { - Game::logger->Log("TacArcBehavior", "Invalid self for (%llu)!", context->originator); + LOG("Invalid self for (%llu)!", context->originator); return; } - const auto* destroyableComponent = self->GetComponent(); - - if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) { - const auto* target = Game::entityManager->GetEntity(branch.target); - - if (target == nullptr) { + std::vector targets = {}; + if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) { + auto target = Game::entityManager->GetEntity(branch.target); + targets.push_back(target); + context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam); + if (!targets.empty()) { + this->m_action->Handle(context, bitStream, branch); return; } - - // If the game is specific about who to target, check that - if (destroyableComponent == nullptr || ((!m_targetFriend && !m_targetEnemy - || m_targetFriend && destroyableComponent->IsFriend(target) - || m_targetEnemy && destroyableComponent->IsEnemy(target)))) { - this->m_action->Calculate(context, bitStream, branch); - } - - return; } auto* combatAi = self->GetComponent(); const auto casterPosition = self->GetPosition(); - auto reference = self->GetPosition(); //+ m_offset; + auto reference = self->GetPosition() + m_offset; - std::vector targets; + targets.clear(); - std::vector validTargets; + std::vector validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange); - if (combatAi != nullptr) { - if (combatAi->GetTarget() != LWOOBJID_EMPTY) { - validTargets.push_back(combatAi->GetTarget()); - } - } - - // Find all valid targets, based on whether we target enemies or friends - for (const auto& contextTarget : context->GetValidTargets()) { - if (destroyableComponent != nullptr) { - const auto* targetEntity = Game::entityManager->GetEntity(contextTarget); - - if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity) - || m_targetFriend && destroyableComponent->IsFriend(targetEntity)) { - validTargets.push_back(contextTarget); - } - } else { - validTargets.push_back(contextTarget); - } - } + // filter all valid targets, based on whether we target enemies or friends + context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam); for (auto validTarget : validTargets) { - if (targets.size() >= this->m_maxTargets) { - break; - } + if (targets.size() >= this->m_maxTargets) break; + if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue; + if (validTarget->GetIsDead()) continue; - auto* entity = Game::entityManager->GetEntity(validTarget); - - if (entity == nullptr) { - Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator); + const auto targetPos = validTarget->GetPosition(); + // make sure we aren't too high or low in comparison to the targer + const auto heightDifference = std::abs(reference.y - targetPos.y); + if (targetPos.y > reference.y && heightDifference > this->m_upperBound || targetPos.y < reference.y && heightDifference > this->m_lowerBound) continue; - } - - if (std::find(targets.begin(), targets.end(), entity) != targets.end()) { - continue; - } - - if (entity->GetIsDead()) continue; - - const auto otherPosition = entity->GetPosition(); - - const auto heightDifference = std::abs(otherPosition.y - casterPosition.y); - - /*if (otherPosition.y > reference.y && heightDifference > this->m_upperBound || otherPosition.y < reference.y && heightDifference > this->m_lowerBound) - { - continue; - }*/ const auto forward = self->GetRotation().GetForwardVector(); // forward is a normalized vector of where the caster is facing. - // otherPosition is the position of the target. + // targetPos is the position of the target. // reference is the position of the caster. // If we cast a ray forward from the caster, does it come within m_farWidth of the target? - const auto distance = Vector3::Distance(reference, otherPosition); + const auto distance = Vector3::Distance(reference, targetPos); if (m_method == 2) { NiPoint3 rayPoint = casterPosition + forward * distance; - - if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, otherPosition) > this->m_farWidth * this->m_farWidth) { + if (m_farWidth > 0 && Vector3::DistanceSquared(rayPoint, targetPos) > this->m_farWidth * this->m_farWidth) continue; - } } - auto normalized = (reference - otherPosition) / distance; - + auto normalized = (reference - targetPos) / distance; const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180); - - if (distance >= this->m_minDistance && this->m_maxDistance >= distance && degreeAngle <= 2 * this->m_angle) { - targets.push_back(entity); + if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) { + targets.push_back(validTarget); } } @@ -193,33 +155,26 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS }); const auto hit = !targets.empty(); - bitStream->Write(hit); if (this->m_checkEnv) { const auto blocked = false; // TODO - bitStream->Write(blocked); } if (hit) { - if (combatAi != nullptr) { - combatAi->LookAt(targets[0]->GetPosition()); - } + if (combatAi) combatAi->LookAt(targets[0]->GetPosition()); context->foundTarget = true; // We want to continue with this behavior - const auto count = static_cast(targets.size()); bitStream->Write(count); - for (auto* target : targets) { bitStream->Write(target->GetObjectID()); } for (auto* target : targets) { branch.target = target->GetObjectID(); - this->m_action->Calculate(context, bitStream, branch); } } else { @@ -228,43 +183,48 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS } void TacArcBehavior::Load() { - this->m_usePickedTarget = GetBoolean("use_picked_target"); + this->m_maxRange = GetFloat("max range"); + this->m_height = GetFloat("height", 2.2f); + this->m_distanceWeight = GetFloat("distance_weight", 0.0f); + this->m_angleWeight = GetFloat("angle_weight", 0.0f); + this->m_angle = GetFloat("angle", 45.0f); + this->m_minRange = GetFloat("min range", 0.0f); + this->m_offset = NiPoint3( + GetFloat("offset_x", 0.0f), + GetFloat("offset_y", 0.0f), + GetFloat("offset_z", 0.0f) + ); + this->m_method = GetInt("method", 1); + this->m_upperBound = std::abs(GetFloat("upper_bound", 4.4f)); + this->m_lowerBound = std::abs(GetFloat("lower_bound", 0.4f)); + this->m_usePickedTarget = GetBoolean("use_picked_target", false); + this->m_useTargetPostion = GetBoolean("use_target_position", false); + this->m_checkEnv = GetBoolean("check_env", false); + this->m_useAttackPriority = GetBoolean("use_attack_priority", false); this->m_action = GetAction("action"); - this->m_missAction = GetAction("miss action"); - - this->m_checkEnv = GetBoolean("check_env"); - this->m_blockedAction = GetAction("blocked action"); - this->m_minDistance = GetFloat("min range"); + this->m_maxTargets = GetInt("max targets", 100); + if (this->m_maxTargets == 0) this->m_maxTargets = 100; - this->m_maxDistance = GetFloat("max range"); + this->m_farHeight = GetFloat("far_height", 5.0f); + this->m_farWidth = GetFloat("far_width", 5.0f); + this->m_nearHeight = GetFloat("near_height", 5.0f); + this->m_nearWidth = GetFloat("near_width", 5.0f); - this->m_maxTargets = GetInt("max targets"); - - this->m_targetEnemy = GetBoolean("target_enemy"); - - this->m_targetFriend = GetBoolean("target_friend"); - - this->m_targetTeam = GetBoolean("target_team"); - - this->m_angle = GetFloat("angle"); - - this->m_upperBound = GetFloat("upper_bound"); - - this->m_lowerBound = GetFloat("lower_bound"); - - this->m_farHeight = GetFloat("far_height"); - - this->m_farWidth = GetFloat("far_width"); - - this->m_method = GetInt("method"); - - this->m_offset = { - GetFloat("offset_x"), - GetFloat("offset_y"), - GetFloat("offset_z") - }; + // params after this are needed for filter targets + const auto parameters = GetParameterNames(); + for (const auto& parameter : parameters) { + if (parameter.first.rfind("include_faction", 0) == 0) { + this->m_includeFactionList.push_front(parameter.second); + } else if (parameter.first.rfind("ignore_faction", 0) == 0) { + this->m_ignoreFactionList.push_front(parameter.second); + } + } + this->m_targetSelf = GetBoolean("target_caster", false); + this->m_targetEnemy = GetBoolean("target_enemy", false); + this->m_targetFriend = GetBoolean("target_friend", false); + this->m_targetTeam = GetBoolean("target_team", false); } diff --git a/dGame/dBehaviors/TacArcBehavior.h b/dGame/dBehaviors/TacArcBehavior.h index 87a22051..d9345272 100644 --- a/dGame/dBehaviors/TacArcBehavior.h +++ b/dGame/dBehaviors/TacArcBehavior.h @@ -2,56 +2,42 @@ #include "Behavior.h" #include "dCommonVars.h" #include "NiPoint3.h" +#include -class TacArcBehavior final : public Behavior -{ +class TacArcBehavior final : public Behavior { public: - bool m_usePickedTarget; - - Behavior* m_action; - - bool m_checkEnv; - - Behavior* m_missAction; - - Behavior* m_blockedAction; - - float m_minDistance; - - float m_maxDistance; - - uint32_t m_maxTargets; - - bool m_targetEnemy; - - bool m_targetFriend; - - bool m_targetTeam; - - float m_angle; - - float m_upperBound; - - float m_lowerBound; - - float m_farHeight; - - float m_farWidth; - - uint32_t m_method; - - NiPoint3 m_offset; - - /* - * Inherited - */ - - explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) { - } - + explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {} void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; - void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override; - void Load() override; +private: + float m_maxRange; + float m_height; + float m_distanceWeight; + float m_angleWeight; + float m_angle; + float m_minRange; + NiPoint3 m_offset; + uint32_t m_method; + float m_upperBound; + float m_lowerBound; + bool m_usePickedTarget; + bool m_useTargetPostion; + bool m_checkEnv; + bool m_useAttackPriority; + Behavior* m_action; + Behavior* m_missAction; + Behavior* m_blockedAction; + uint32_t m_maxTargets; + float m_farHeight; + float m_farWidth; + float m_nearHeight; + float m_nearWidth; + + std::forward_list m_ignoreFactionList {}; + std::forward_list m_includeFactionList {}; + bool m_targetSelf; + bool m_targetEnemy; + bool m_targetFriend; + bool m_targetTeam; }; diff --git a/dGame/dBehaviors/TauntBehavior.cpp b/dGame/dBehaviors/TauntBehavior.cpp index b86cd7b5..2e9f2168 100644 --- a/dGame/dBehaviors/TauntBehavior.cpp +++ b/dGame/dBehaviors/TauntBehavior.cpp @@ -3,14 +3,14 @@ #include "BehaviorContext.h" #include "BaseCombatAIComponent.h" #include "EntityManager.h" -#include "dLogger.h" +#include "Logger.h" void TauntBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } @@ -26,7 +26,7 @@ void TauntBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitSt auto* target = Game::entityManager->GetEntity(branch.target); if (target == nullptr) { - Game::logger->Log("TauntBehavior", "Failed to find target (%llu)!", branch.target); + LOG("Failed to find target (%llu)!", branch.target); return; } diff --git a/dGame/dBehaviors/VerifyBehavior.cpp b/dGame/dBehaviors/VerifyBehavior.cpp index 6824a25f..2056f146 100644 --- a/dGame/dBehaviors/VerifyBehavior.cpp +++ b/dGame/dBehaviors/VerifyBehavior.cpp @@ -4,7 +4,7 @@ #include "NiPoint3.h" #include "BehaviorContext.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) { @@ -18,7 +18,7 @@ void VerifyBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS auto* self = Game::entityManager->GetEntity(context->originator); if (self == nullptr) { - Game::logger->Log("VerifyBehavior", "Invalid self for (%llu)", context->originator); + LOG("Invalid self for (%llu)", context->originator); return; } diff --git a/dGame/dComponents/ScriptedActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp similarity index 74% rename from dGame/dComponents/ScriptedActivityComponent.cpp rename to dGame/dComponents/ActivityComponent.cpp index 81b3e9a9..49ca4faf 100644 --- a/dGame/dComponents/ScriptedActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -1,4 +1,4 @@ -#include "ScriptedActivityComponent.h" +#include "ActivityComponent.h" #include "GameMessages.h" #include "CDClientManager.h" #include "MissionComponent.h" @@ -6,12 +6,11 @@ #include "dZoneManager.h" #include "ZoneInstanceManager.h" #include "Game.h" -#include "dLogger.h" -#include +#include "Logger.h" +#include "WorldPackets.h" #include "EntityManager.h" #include "ChatPackets.h" -#include "Player.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "dServer.h" #include "GeneralUtils.h" #include "dZoneManager.h" @@ -29,45 +28,37 @@ #include "CDActivitiesTable.h" #include "LeaderboardManager.h" -ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activityID) : Component(parent) { +ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Component(parent) { + /* + * This is precisely what the client does functionally + * Use the component id as the default activity id and load its data from the database + * if activityID is specified and if that column exists in the activities table, update the activity info with that data. + */ + m_ActivityID = activityID; - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); - std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); - - for (CDActivities activity : activities) { - m_ActivityInfo = activity; - if (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") { - m_ActivityInfo.minTeamSize = 1; - m_ActivityInfo.minTeams = 1; - } - - const auto& transferOverride = parent->GetVar(u"transferZoneID"); - if (!transferOverride.empty()) { - m_ActivityInfo.instanceMapID = std::stoi(GeneralUtils::UTF16ToWTF8(transferOverride)); - - // TODO: LU devs made me do it (for some reason cannon cove instancer is marked to go to GF survival) - // NOTE: 1301 is GF survival - if (m_ActivityInfo.instanceMapID == 1301) { - m_ActivityInfo.instanceMapID = 1302; - } - } + LoadActivityData(activityID); + if (m_Parent->HasVar(u"activityID")) { + m_ActivityID = parent->GetVar(u"activityID"); + LoadActivityData(m_ActivityID); } auto* destroyableComponent = m_Parent->GetComponent(); if (destroyableComponent) { - // check for LMIs and set the loot LMIs - CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().GetTable(); + // First lookup the loot matrix id for this component id. + CDActivityRewardsTable* activityRewardsTable = CDClientManager::GetTable(); std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) {return (entry.LootMatrixIndex == destroyableComponent->GetLootMatrixID()); }); uint32_t startingLMI = 0; + // If we have one, set the starting loot matrix id to that. if (activityRewards.size() > 0) { startingLMI = activityRewards[0].LootMatrixIndex; } if (startingLMI > 0) { - // now time for bodge :) + // We may have more than 1 loot matrix index to use depending ont the size of the team that is looting the activity. + // So this logic will get the rest of the loot matrix indices for this activity. std::vector objectTemplateActivities = activityRewardsTable->Query([=](CDActivityRewards entry) {return (activityRewards[0].objectTemplate == entry.objectTemplate); }); for (const auto& item : objectTemplateActivities) { @@ -78,31 +69,49 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit } } } +void ActivityComponent::LoadActivityData(const int32_t activityId) { + CDActivitiesTable* activitiesTable = CDClientManager::GetTable(); + std::vector activities = activitiesTable->Query([activityId](CDActivities entry) {return (entry.ActivityID == activityId); }); -ScriptedActivityComponent::~ScriptedActivityComponent() -= default; - -void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const { - outBitStream->Write(true); - outBitStream->Write(m_ActivityPlayers.size()); - - if (!m_ActivityPlayers.empty()) { - for (const auto& activityPlayer : m_ActivityPlayers) { - - outBitStream->Write(activityPlayer->playerID); - for (const auto& activityValue : activityPlayer->values) { - outBitStream->Write(activityValue); + bool soloRacing = Game::config->GetValue("solo_racing") == "1"; + for (CDActivities activity : activities) { + m_ActivityInfo = activity; + if (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && soloRacing) { + m_ActivityInfo.minTeamSize = 1; + m_ActivityInfo.minTeams = 1; + } + if (m_ActivityInfo.instanceMapID == -1) { + const auto& transferOverride = m_Parent->GetVarAsString(u"transferZoneID"); + if (!transferOverride.empty()) { + m_ActivityInfo.instanceMapID = + GeneralUtils::TryParse(transferOverride).value_or(m_ActivityInfo.instanceMapID); } } } } -void ScriptedActivityComponent::ReloadConfig() { - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); - std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); +void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + outBitStream->Write(m_DirtyActivityInfo); + if (m_DirtyActivityInfo) { + outBitStream->Write(m_ActivityPlayers.size()); + if (!m_ActivityPlayers.empty()) { + for (const auto& activityPlayer : m_ActivityPlayers) { + outBitStream->Write(activityPlayer->playerID); + for (const auto& activityValue : activityPlayer->values) { + outBitStream->Write(activityValue); + } + } + } + if (!bIsInitialUpdate) m_DirtyActivityInfo = false; + } +} + +void ActivityComponent::ReloadConfig() { + CDActivitiesTable* activitiesTable = CDClientManager::GetTable(); + std::vector activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); for (auto activity : activities) { auto mapID = m_ActivityInfo.instanceMapID; - if ((mapID == 1203 || mapID == 1261 || mapID == 1303 || mapID == 1403) && Game::config->GetValue("solo_racing") == "1") { + if (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") { m_ActivityInfo.minTeamSize = 1; m_ActivityInfo.minTeams = 1; } else { @@ -112,11 +121,7 @@ void ScriptedActivityComponent::ReloadConfig() { } } -void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) { - if (m_ActivityInfo.ActivityID == 103) { - return; - } - +void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::string& id) { if (id == "LobbyExit") { PlayerLeave(player->GetObjectID()); } else if (id == "PlayButton") { @@ -124,11 +129,8 @@ void ScriptedActivityComponent::HandleMessageBoxResponse(Entity* player, const s } } -void ScriptedActivityComponent::PlayerJoin(Entity* player) { - if (m_ActivityInfo.ActivityID == 103 || PlayerIsInQueue(player) || !IsValidActivity(player)) { - return; - } - +void ActivityComponent::PlayerJoin(Entity* player) { + if (PlayerIsInQueue(player)) return; // If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot if (HasLobby()) { PlayerJoinLobby(player); @@ -136,11 +138,9 @@ void ScriptedActivityComponent::PlayerJoin(Entity* player) { auto* instance = NewInstance(); instance->AddParticipant(player); } - - Game::entityManager->SerializeEntity(m_Parent); } -void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) { +void ActivityComponent::PlayerJoinLobby(Entity* player) { if (!m_Parent->HasComponent(eReplicaComponentType::QUICK_BUILD)) GameMessages::SendMatchResponse(player, player->GetSystemAddress(), 0); // tell the client they joined a lobby LobbyPlayer* newLobbyPlayer = new LobbyPlayer(); @@ -189,7 +189,7 @@ void ScriptedActivityComponent::PlayerJoinLobby(Entity* player) { } } -void ScriptedActivityComponent::PlayerLeave(LWOOBJID playerID) { +void ActivityComponent::PlayerLeave(LWOOBJID playerID) { // Removes the player from a lobby and notifies the others, not applicable for non-lobby instances for (Lobby* lobby : m_Queue) { @@ -214,7 +214,7 @@ void ScriptedActivityComponent::PlayerLeave(LWOOBJID playerID) { } } -void ScriptedActivityComponent::Update(float deltaTime) { +void ActivityComponent::Update(float deltaTime) { std::vector lobbiesToRemove{}; // Ticks all the lobbies, not applicable for non-instance activities for (Lobby* lobby : m_Queue) { @@ -273,7 +273,7 @@ void ScriptedActivityComponent::Update(float deltaTime) { // The timer has elapsed, start the instance if (lobby->timer <= 0.0f) { - Game::logger->Log("ScriptedActivityComponent", "Setting up instance."); + LOG("Setting up instance."); ActivityInstance* instance = NewInstance(); LoadPlayersIntoInstance(instance, lobby->players); instance->StartZone(); @@ -287,7 +287,7 @@ void ScriptedActivityComponent::Update(float deltaTime) { } } -void ScriptedActivityComponent::RemoveLobby(Lobby* lobby) { +void ActivityComponent::RemoveLobby(Lobby* lobby) { for (int i = 0; i < m_Queue.size(); ++i) { if (m_Queue[i] == lobby) { m_Queue.erase(m_Queue.begin() + i); @@ -296,29 +296,12 @@ void ScriptedActivityComponent::RemoveLobby(Lobby* lobby) { } } -bool ScriptedActivityComponent::HasLobby() const { +bool ActivityComponent::HasLobby() const { // If the player is not in the world he has to be, create a lobby for the transfer return m_ActivityInfo.instanceMapID != UINT_MAX && m_ActivityInfo.instanceMapID != Game::server->GetZoneID(); } -bool ScriptedActivityComponent::IsValidActivity(Entity* player) { - // Makes it so that scripted activities with an unimplemented map cannot be joined - /*if (player->GetGMLevel() < eGameMasterLevel::DEVELOPER && (m_ActivityInfo.instanceMapID == 1302 || m_ActivityInfo.instanceMapID == 1301)) { - if (m_Parent->GetLOT() == 4860) { - auto* missionComponent = player->GetComponent(); - missionComponent->CompleteMission(229); - } - - ChatPackets::SendSystemMessage(player->GetSystemAddress(), u"Sorry, this activity is not ready."); - static_cast(player)->SendToZone(Game::zoneManager->GetZone()->GetWorldID()); // Gets them out of this stuck state - - return false; - }*/ - - return true; -} - -bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) { +bool ActivityComponent::PlayerIsInQueue(Entity* player) { for (Lobby* lobby : m_Queue) { for (LobbyPlayer* lobbyPlayer : lobby->players) { if (player->GetObjectID() == lobbyPlayer->entityID) return true; @@ -328,7 +311,7 @@ bool ScriptedActivityComponent::PlayerIsInQueue(Entity* player) { return false; } -bool ScriptedActivityComponent::IsPlayedBy(Entity* player) const { +bool ActivityComponent::IsPlayedBy(Entity* player) const { for (const auto* instance : this->m_Instances) { for (const auto* instancePlayer : instance->GetParticipants()) { if (instancePlayer != nullptr && instancePlayer->GetObjectID() == player->GetObjectID()) @@ -339,7 +322,7 @@ bool ScriptedActivityComponent::IsPlayedBy(Entity* player) const { return false; } -bool ScriptedActivityComponent::IsPlayedBy(LWOOBJID playerID) const { +bool ActivityComponent::IsPlayedBy(LWOOBJID playerID) const { for (const auto* instance : this->m_Instances) { for (const auto* instancePlayer : instance->GetParticipants()) { if (instancePlayer != nullptr && instancePlayer->GetObjectID() == playerID) @@ -350,7 +333,7 @@ bool ScriptedActivityComponent::IsPlayedBy(LWOOBJID playerID) const { return false; } -bool ScriptedActivityComponent::TakeCost(Entity* player) const { +bool ActivityComponent::TakeCost(Entity* player) const { if (m_ActivityInfo.optionalCostLOT <= 0 || m_ActivityInfo.optionalCostCount <= 0) return true; @@ -366,7 +349,7 @@ bool ScriptedActivityComponent::TakeCost(Entity* player) const { return true; } -void ScriptedActivityComponent::PlayerReady(Entity* player, bool bReady) { +void ActivityComponent::PlayerReady(Entity* player, bool bReady) { for (Lobby* lobby : m_Queue) { for (LobbyPlayer* lobbyPlayer : lobby->players) { if (lobbyPlayer->entityID == player->GetObjectID()) { @@ -389,13 +372,13 @@ void ScriptedActivityComponent::PlayerReady(Entity* player, bool bReady) { } } -ActivityInstance* ScriptedActivityComponent::NewInstance() { +ActivityInstance* ActivityComponent::NewInstance() { auto* instance = new ActivityInstance(m_Parent, m_ActivityInfo); m_Instances.push_back(instance); return instance; } -void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const { +void ActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const { for (LobbyPlayer* player : lobby) { auto* entity = player->GetEntity(); if (entity == nullptr || !TakeCost(entity)) { @@ -406,11 +389,11 @@ void ScriptedActivityComponent::LoadPlayersIntoInstance(ActivityInstance* instan } } -const std::vector& ScriptedActivityComponent::GetInstances() const { +const std::vector& ActivityComponent::GetInstances() const { return m_Instances; } -ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID) { +ActivityInstance* ActivityComponent::GetInstance(const LWOOBJID playerID) { for (const auto* instance : GetInstances()) { for (const auto* participant : instance->GetParticipants()) { if (participant->GetObjectID() == playerID) @@ -421,14 +404,14 @@ ActivityInstance* ScriptedActivityComponent::GetInstance(const LWOOBJID playerID return nullptr; } -void ScriptedActivityComponent::ClearInstances() { +void ActivityComponent::ClearInstances() { for (ActivityInstance* instance : m_Instances) { delete instance; } m_Instances.clear(); } -ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID playerID) { +ActivityPlayer* ActivityComponent::GetActivityPlayerData(LWOOBJID playerID) { for (auto* activityData : m_ActivityPlayers) { if (activityData->playerID == playerID) { return activityData; @@ -438,13 +421,14 @@ ActivityPlayer* ScriptedActivityComponent::GetActivityPlayerData(LWOOBJID player return nullptr; } -void ScriptedActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) { +void ActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) { for (size_t i = 0; i < m_ActivityPlayers.size(); i++) { if (m_ActivityPlayers[i]->playerID == playerID) { delete m_ActivityPlayers[i]; m_ActivityPlayers[i] = nullptr; m_ActivityPlayers.erase(m_ActivityPlayers.begin() + i); + m_DirtyActivityInfo = true; Game::entityManager->SerializeEntity(m_Parent); return; @@ -452,38 +436,39 @@ void ScriptedActivityComponent::RemoveActivityPlayerData(LWOOBJID playerID) { } } -ActivityPlayer* ScriptedActivityComponent::AddActivityPlayerData(LWOOBJID playerID) { +ActivityPlayer* ActivityComponent::AddActivityPlayerData(LWOOBJID playerID) { auto* data = GetActivityPlayerData(playerID); if (data != nullptr) return data; m_ActivityPlayers.push_back(new ActivityPlayer{ playerID, {} }); + m_DirtyActivityInfo = true; Game::entityManager->SerializeEntity(m_Parent); return GetActivityPlayerData(playerID); } -float_t ScriptedActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) { +float_t ActivityComponent::GetActivityValue(LWOOBJID playerID, uint32_t index) { auto value = -1.0f; auto* data = GetActivityPlayerData(playerID); if (data != nullptr) { - value = data->values[std::min(index, (uint32_t)9)]; + value = data->values[std::min(index, static_cast(9))]; } return value; } -void ScriptedActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) { +void ActivityComponent::SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value) { auto* data = AddActivityPlayerData(playerID); if (data != nullptr) { - data->values[std::min(index, (uint32_t)9)] = value; + data->values[std::min(index, static_cast(9))] = value; } - + m_DirtyActivityInfo = true; Game::entityManager->SerializeEntity(m_Parent); } -void ScriptedActivityComponent::PlayerRemove(LWOOBJID playerID) { +void ActivityComponent::PlayerRemove(LWOOBJID playerID) { for (auto* instance : GetInstances()) { auto participants = instance->GetParticipants(); for (const auto* participant : participants) { @@ -516,7 +501,7 @@ void ActivityInstance::StartZone() { // only make a team if we have more than one participant if (participants.size() > 1) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::CREATE_TEAM); bitStream.Write(leader->GetObjectID()); bitStream.Write(m_Participants.size()); @@ -539,7 +524,7 @@ void ActivityInstance::StartZone() { if (player == nullptr) return; - Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", player->GetCharacter()->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); if (player->GetCharacter()) { player->GetCharacter()->SetZoneID(zoneID); player->GetCharacter()->SetZoneInstance(zoneInstance); @@ -561,14 +546,14 @@ void ActivityInstance::RewardParticipant(Entity* participant) { } // First, get the activity data - auto* activityRewardsTable = CDClientManager::Instance().GetTable(); - std::vector activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); }); + auto* activityRewardsTable = CDClientManager::GetTable(); + std::vector activityRewards = activityRewardsTable->Query([this](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); }); if (!activityRewards.empty()) { uint32_t minCoins = 0; uint32_t maxCoins = 0; - auto* currencyTableTable = CDClientManager::Instance().GetTable(); + auto* currencyTableTable = CDClientManager::GetTable(); std::vector currencyTable = currencyTableTable->Query([=](CDCurrencyTable entry) { return (entry.currencyIndex == activityRewards[0].CurrencyIndex && entry.npcminlevel == 1); }); if (!currencyTable.empty()) { @@ -576,7 +561,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) { maxCoins = currencyTable[0].maxvalue; } - LootGenerator::Instance().DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins); + Loot::DropLoot(participant, m_Parent, activityRewards[0].LootMatrixIndex, minCoins, maxCoins); } } diff --git a/dGame/dComponents/ActivityComponent.h b/dGame/dComponents/ActivityComponent.h new file mode 100644 index 00000000..de63b343 --- /dev/null +++ b/dGame/dComponents/ActivityComponent.h @@ -0,0 +1,373 @@ +#ifndef ACTIVITYCOMPONENT_H +#define ACTIVITYCOMPONENT_H + +#include "CDClientManager.h" +#include "BitStream.h" +#include "Entity.h" +#include "Component.h" +#include "eReplicaComponentType.h" + +#include "CDActivitiesTable.h" + + /** + * Represents an instance of an activity, having participants and score + */ +class ActivityInstance { +public: + ActivityInstance(Entity* parent, CDActivities activityInfo) { m_Parent = parent; m_ActivityInfo = activityInfo; }; + //~ActivityInstance(); + + /** + * Adds an entity to this activity + * @param participant the entity to add + */ + void AddParticipant(Entity* participant); + + /** + * Removes all the participants from this activity + */ + void ClearParticipants() { m_Participants.clear(); }; + + /** + * Starts the instance world for this activity and sends all participants there + */ + void StartZone(); + + /** + * Gives the rewards for completing this activity to some participant + * @param participant the participant to give rewards to + */ + void RewardParticipant(Entity* participant); + + /** + * Removes a participant from this activity + * @param participant the participant to remove + */ + void RemoveParticipant(const Entity* participant); + + /** + * Returns all the participants of this activity + * @return all the participants of this activity + */ + std::vector GetParticipants() const; + + /** + * Currently unused + */ + uint32_t GetScore() const; + + /** + * Currently unused + */ + void SetScore(uint32_t score); +private: + + /** + * Currently unused + */ + uint32_t score = 0; + + /** + * The instance ID of this activity + */ + uint32_t m_NextZoneCloneID = 0; + + /** + * The database information for this activity + */ + CDActivities m_ActivityInfo; + + /** + * The entity that owns this activity (the entity that has the ScriptedActivityComponent) + */ + Entity* m_Parent; + + /** + * All the participants of this activity + */ + std::vector m_Participants; +}; + +/** + * Represents an entity in a lobby + */ +struct LobbyPlayer { + + /** + * The ID of the entity that is in the lobby + */ + LWOOBJID entityID; + + /** + * Whether or not the entity is ready + */ + bool ready = false; + + /** + * Returns the entity that is in the lobby + * @return the entity that is in the lobby + */ + Entity* GetEntity() const; +}; + +/** + * Represents a lobby of players with a timer until it should start the activity + */ +struct Lobby { + + /** + * The lobby of players + */ + std::vector players; + + /** + * The timer that determines when the activity should start + */ + float timer; +}; + +/** + * Represents the score for the player in an activity, one index might represent score, another one time, etc. + */ +struct ActivityPlayer { + + /** + * The entity that the score is tracked for + */ + LWOOBJID playerID; + + /** + * The list of score for this entity + */ + float values[10]; +}; + +/** + * Welcome to the absolute behemoth that is the scripted activity component. I have now clue how this was managed in + * live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that + * can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing + * and lobbying. + */ +class ActivityComponent : public Component { +public: + ActivityComponent(Entity* parent, int32_t activityID); + + void LoadActivityData(const int32_t activityId); + + void Update(float deltaTime) override; + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + + /** + * Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby + * @param player the entity to join the game + */ + void PlayerJoin(Entity* player); + + /** + * Makes an entity join the lobby for this minigame, if it exists + * @param player the entity to join + */ + void PlayerJoinLobby(Entity* player); + + /** + * Makes the player leave the lobby + * @param playerID the entity to leave the lobby + */ + void PlayerLeave(LWOOBJID playerID); + + /** + * Removes the entity from the minigame (and its score) + * @param playerID the entity to remove from the minigame + */ + void PlayerRemove(LWOOBJID playerID); + + /** + * Adds all the players to an instance of some activity + * @param instance the instance to load the players into + * @param lobby the players to load into the instance + */ + void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const; + + /** + * Removes a lobby from the activity manager + * @param lobby the lobby to remove + */ + void RemoveLobby(Lobby* lobby); + + /** + * Marks a player as (un)ready in a lobby + * @param player the entity to mark + * @param bReady true if the entity is ready, false otherwise + */ + void PlayerReady(Entity* player, bool bReady); + + /** + * Returns the ID of this activity + * @return the ID of this activity + */ + int GetActivityID() { return m_ActivityInfo.ActivityID; } + + /** + * Returns if this activity has a lobby, e.g. if it needs to instance players to some other map + * @return true if this activity has a lobby, false otherwise + */ + bool HasLobby() const; + + /** + * Checks if a player is currently waiting in a lobby + * @param player the entity to check for + * @return true if the entity is waiting in a lobby, false otherwise + */ + bool PlayerIsInQueue(Entity* player); + + /** + * Checks if an entity is currently playing this activity + * @param player the entity to check + * @return true if the entity is playing this lobby, false otherwise + */ + bool IsPlayedBy(Entity* player) const; + + /** + * Checks if an entity is currently playing this activity + * @param playerID the entity to check + * @return true if the entity is playing this lobby, false otherwise + */ + bool IsPlayedBy(LWOOBJID playerID) const; + + /** + * Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity + * @param player the entity to take cost for + * @return true if the cost was successfully deducted, false otherwise + */ + bool TakeCost(Entity* player) const; + + /** + * Handles any response from a player clicking on a lobby / instance menu + * @param player the entity that clicked + * @param id the message that was passed + */ + void HandleMessageBoxResponse(Entity* player, const std::string& id); + + /** + * Creates a new instance for this activity + * @return a new instance for this activity + */ + ActivityInstance* NewInstance(); + + /** + * Returns all the currently active instances of this activity + * @return all the currently active instances of this activity + */ + const std::vector& GetInstances() const; + + /** + * Returns the instance that some entity is currently playing in + * @param playerID the entity to check for + * @return if any, the instance that the entity is currently in + */ + ActivityInstance* GetInstance(const LWOOBJID playerID); + + /** + * @brief Reloads the config settings for this component + * + */ + void ReloadConfig(); + + /** + * Removes all the instances + */ + void ClearInstances(); + + /** + * Returns all the score for the players that are currently playing this activity + * @return + */ + std::vector GetActivityPlayers() { return m_ActivityPlayers; }; + + /** + * Returns activity data for a specific entity (e.g. score and such). + * @param playerID the entity to get data for + * @return the activity data (score) for the passed player in this activity, if it exists + */ + ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID); + + /** + * Sets some score value for an entity + * @param playerID the entity to set score for + * @param index the score index to set + * @param value the value to set in for that index + */ + void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value); + + /** + * Returns activity score for the passed parameters + * @param playerID the entity to get score for + * @param index the index to get score for + * @return activity score for the passed parameters + */ + float_t GetActivityValue(LWOOBJID playerID, uint32_t index); + + /** + * Removes activity score tracking for some entity + * @param playerID the entity to remove score for + */ + void RemoveActivityPlayerData(LWOOBJID playerID); + + /** + * Adds activity score tracking for some entity + * @param playerID the entity to add the activity score for + * @return the created entry + */ + ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID); + + /** + * Sets the mapID that this activity points to + * @param mapID the map ID to set + */ + void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; }; + + /** + * Returns the LMI that this activity points to for a team size + * @param teamSize the team size to get the LMI for + * @return the LMI that this activity points to for a team size + */ + uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; } +private: + + /** + * The database information for this activity + */ + CDActivities m_ActivityInfo; + + /** + * All the active instances of this activity + */ + std::vector m_Instances; + + /** + * The current lobbies for this activity + */ + std::vector m_Queue; + + /** + * All the activity score for the players in this activity + */ + std::vector m_ActivityPlayers; + + /** + * LMIs for team sizes + */ + std::unordered_map m_ActivityLootMatrices; + + /** + * The activity id + */ + int32_t m_ActivityID; + + /** + * If the Activity info is dirty + */ + bool m_DirtyActivityInfo = true; +}; + +#endif // ACTIVITYCOMPONENT_H diff --git a/dGame/dComponents/BaseCombatAIComponent.cpp b/dGame/dComponents/BaseCombatAIComponent.cpp index 5df43854..17183409 100644 --- a/dGame/dComponents/BaseCombatAIComponent.cpp +++ b/dGame/dComponents/BaseCombatAIComponent.cpp @@ -1,5 +1,5 @@ #include "BaseCombatAIComponent.h" -#include +#include "BitStream.h" #include "Entity.h" #include "EntityManager.h" @@ -20,11 +20,12 @@ #include #include "SkillComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include "Metrics.hpp" #include "CDComponentsRegistryTable.h" #include "CDPhysicsComponentTable.h" +#include "dNavMesh.h" BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { m_Target = LWOOBJID_EMPTY; @@ -34,13 +35,12 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_MovementAI = nullptr; m_Disabled = false; m_SkillEntries = {}; - m_MovementAI = nullptr; m_SoftTimer = 5.0f; //Grab the aggro information from BaseCombatAI: auto componentQuery = CDClientDatabase::CreatePreppedStmt( "SELECT aggroRadius, tetherSpeed, pursuitSpeed, softTetherRadius, hardTetherRadius FROM BaseCombatAIComponent WHERE id = ?;"); - componentQuery.bind(1, (int)id); + componentQuery.bind(1, static_cast(id)); auto componentResult = componentQuery.execQuery(); @@ -77,7 +77,7 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): */ auto skillQuery = CDClientDatabase::CreatePreppedStmt( "SELECT skillID, cooldown, behaviorID FROM SkillBehavior WHERE skillID IN (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);"); - skillQuery.bind(1, (int)parent->GetLOT()); + skillQuery.bind(1, static_cast(parent->GetLOT())); auto result = skillQuery.execQuery(); @@ -107,10 +107,10 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): int32_t collisionGroup = (COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_ENEMY); - CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable(); auto componentID = componentRegistryTable->GetByIDAndType(parent->GetLOT(), eReplicaComponentType::CONTROLLABLE_PHYSICS); - CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable(); + CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable(); if (physicsComponentTable != nullptr) { auto* info = physicsComponentTable->GetByID(componentID); @@ -129,17 +129,17 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): m_dpEntity->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); - dpWorld::Instance().AddEntity(m_dpEntity); - dpWorld::Instance().AddEntity(m_dpEntityEnemy); + dpWorld::AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntityEnemy); } BaseCombatAIComponent::~BaseCombatAIComponent() { if (m_dpEntity) - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); if (m_dpEntityEnemy) - dpWorld::Instance().RemoveEntity(m_dpEntityEnemy); + dpWorld::RemoveEntity(m_dpEntityEnemy); } void BaseCombatAIComponent::Update(const float deltaTime) { @@ -185,7 +185,7 @@ void BaseCombatAIComponent::Update(const float deltaTime) { bool stunnedThisFrame = m_Stunned; CalculateCombat(deltaTime); // Putting this here for now - if (m_StartPosition == NiPoint3::ZERO) { + if (m_StartPosition == NiPoint3Constant::ZERO) { m_StartPosition = m_Parent->GetPosition(); } @@ -243,12 +243,12 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) { bool hadRemainingDowntime = m_SkillTime > 0.0f; if (m_SkillTime > 0.0f) m_SkillTime -= deltaTime; - auto* rebuild = m_Parent->GetComponent(); + auto* rebuild = m_Parent->GetComponent(); if (rebuild != nullptr) { const auto state = rebuild->GetState(); - if (state != eRebuildState::COMPLETED) { + if (state != eQuickBuildState::COMPLETED) { return; } } @@ -520,10 +520,10 @@ bool BaseCombatAIComponent::IsMech() { } -void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate); if (m_DirtyStateOrTarget || bIsInitialUpdate) { - outBitStream->Write(uint32_t(m_State)); + outBitStream->Write(m_State); outBitStream->Write(m_Target); m_DirtyStateOrTarget = false; } @@ -540,7 +540,7 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const { auto* entity = Game::entityManager->GetEntity(target); if (entity == nullptr) { - Game::logger->Log("BaseCombatAIComponent", "Invalid entity for checking validity (%llu)!", target); + LOG("Invalid entity for checking validity (%llu)!", target); return false; } @@ -554,17 +554,17 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const { auto* referenceDestroyable = m_Parent->GetComponent(); if (referenceDestroyable == nullptr) { - Game::logger->Log("BaseCombatAIComponent", "Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID()); + LOG("Invalid reference destroyable component on (%llu)!", m_Parent->GetObjectID()); return false; } - auto* quickbuild = entity->GetComponent(); + auto* quickbuild = entity->GetComponent(); if (quickbuild != nullptr) { const auto state = quickbuild->GetState(); - if (state != eRebuildState::COMPLETED) { + if (state != eQuickBuildState::COMPLETED) { return false; } } @@ -654,8 +654,8 @@ void BaseCombatAIComponent::Wander() { auto destination = m_StartPosition + delta; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) { diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index 8bf6140a..f00910e7 100644 --- a/dGame/dComponents/BaseCombatAIComponent.h +++ b/dGame/dComponents/BaseCombatAIComponent.h @@ -19,7 +19,7 @@ class Entity; /** * The current state of the AI */ -enum class AiState : int { +enum class AiState : uint32_t { idle = 0, // Doing nothing aggro, // Waiting for an enemy to cross / running back to spawn tether, // Chasing an enemy @@ -45,15 +45,15 @@ struct AiSkillEntry /** * Handles the AI of entities, making them wander, tether and attack their enemies */ -class BaseCombatAIComponent : public Component { +class BaseCombatAIComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BASE_COMBAT_AI; BaseCombatAIComponent(Entity* parentEntity, uint32_t id); ~BaseCombatAIComponent() override; void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Get the current behavioral state of the enemy diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index bbf928dc..78ee3637 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -4,9 +4,9 @@ #include "dZoneManager.h" #include "SwitchComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "GameMessages.h" -#include +#include "BitStream.h" #include "eTriggerEventType.h" BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) { @@ -22,7 +22,7 @@ BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) { BouncerComponent::~BouncerComponent() { } -void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_PetEnabled); if (m_PetEnabled) { outBitStream->Write(m_PetBouncerEnabled); @@ -81,13 +81,13 @@ void BouncerComponent::LookupPetSwitch() { Game::entityManager->SerializeEntity(m_Parent); - Game::logger->Log("BouncerComponent", "Loaded pet bouncer"); + LOG("Loaded pet bouncer"); } } } if (!m_PetSwitchLoaded) { - Game::logger->Log("BouncerComponent", "Failed to load pet bouncer"); + LOG("Failed to load pet bouncer"); m_Parent->AddCallbackTimer(0.5f, [this]() { LookupPetSwitch(); diff --git a/dGame/dComponents/BouncerComponent.h b/dGame/dComponents/BouncerComponent.h index 15665cc1..b41881c6 100644 --- a/dGame/dComponents/BouncerComponent.h +++ b/dGame/dComponents/BouncerComponent.h @@ -10,14 +10,14 @@ /** * Attached to bouncer entities, allowing other entities to bounce off of it */ -class BouncerComponent : public Component { +class BouncerComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BOUNCER; BouncerComponent(Entity* parentEntity); ~BouncerComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; Entity* GetParentEntity() const; diff --git a/dGame/dComponents/BuffComponent.cpp b/dGame/dComponents/BuffComponent.cpp index 68b5182c..cdf1d5bc 100644 --- a/dGame/dComponents/BuffComponent.cpp +++ b/dGame/dComponents/BuffComponent.cpp @@ -1,53 +1,71 @@ #include "BuffComponent.h" -#include +#include "BitStream.h" #include "CDClientDatabase.h" #include #include "DestroyableComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "GameMessages.h" #include "SkillComponent.h" #include "ControllablePhysicsComponent.h" #include "EntityManager.h" #include "CDClientManager.h" #include "CDSkillBehaviorTable.h" +#include "TeamManager.h" std::unordered_map> BuffComponent::m_Cache{}; +namespace { + std::map BuffFx = { + { "overtime", "OTB_" }, + { "max_health", "HEALTH_" }, + { "max_imagination", "IMAGINATION_" }, + { "max_armor", "ARMOR_" }, + { "speed", "SPEED_" }, + { "loot", "LOOT_" } + }; +} + BuffComponent::BuffComponent(Entity* parent) : Component(parent) { } BuffComponent::~BuffComponent() { } -void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (!bIsInitialUpdate) return; - if (m_Buffs.empty()) { - outBitStream->Write0(); - } else { - outBitStream->Write1(); + outBitStream->Write(!m_Buffs.empty()); + if (!m_Buffs.empty()) { outBitStream->Write(m_Buffs.size()); - for (const auto& buff : m_Buffs) { - outBitStream->Write(buff.first); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); + for (const auto& [id, buff] : m_Buffs) { + outBitStream->Write(id); + outBitStream->Write(buff.time != 0.0f); + if (buff.time != 0.0f) outBitStream->Write(buff.time * 1000.0f); + outBitStream->Write(buff.cancelOnDeath); + outBitStream->Write(buff.cancelOnZone); + outBitStream->Write(buff.cancelOnDamaged); + outBitStream->Write(buff.cancelOnRemoveBuff); + outBitStream->Write(buff.cancelOnUi); + outBitStream->Write(buff.cancelOnLogout); + outBitStream->Write(buff.cancelOnUnequip); + outBitStream->Write0(); // Cancel on Damage Absorb Ran Out. Generally false from what I can tell - outBitStream->Write0(); - outBitStream->Write0(); + auto* team = TeamManager::Instance()->GetTeam(buff.source); + bool addedByTeammate = false; + if (team) { + addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0; + } - outBitStream->Write(0); + outBitStream->Write(addedByTeammate); // Added by teammate. If source is in the same team as the target, this is true. Otherwise, false. + outBitStream->Write(buff.applyOnTeammates); + if (addedByTeammate) outBitStream->Write(buff.source); + + outBitStream->Write(buff.refCount); } } - outBitStream->Write0(); + outBitStream->Write0(); // something to do with immunity buffs? } void BuffComponent::Update(float deltaTime) { @@ -77,23 +95,67 @@ void BuffComponent::Update(float deltaTime) { if (buff.second.time <= 0.0f) { RemoveBuff(buff.first); - - break; } } + + if (m_BuffsToRemove.empty()) return; + + for (const auto& buff : m_BuffsToRemove) { + m_Buffs.erase(buff); + } + + m_BuffsToRemove.clear(); +} + +const std::string& GetFxName(const std::string& buffname) { + const auto& toReturn = BuffFx[buffname]; + if (toReturn.empty()) { + LOG_DEBUG("No fx name for %s", buffname.c_str()); + } + return toReturn; +} + +void BuffComponent::ApplyBuffFx(uint32_t buffId, const BuffParameter& buff) { + std::string fxToPlay; + const auto& buffName = GetFxName(buff.name); + + if (buffName.empty()) return; + + fxToPlay += std::to_string(buffId); + LOG_DEBUG("Playing %s %i", fxToPlay.c_str(), buff.effectId); + GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), buff.effectId, u"cast", fxToPlay, LWOOBJID_EMPTY, 1.07f, 1.0f, false); +} + +void BuffComponent::RemoveBuffFx(uint32_t buffId, const BuffParameter& buff) { + std::string fxToPlay; + const auto& buffName = GetFxName(buff.name); + + if (buffName.empty()) return; + + fxToPlay += std::to_string(buffId); + LOG_DEBUG("Stopping %s", fxToPlay.c_str()); + GameMessages::SendStopFXEffect(m_Parent, false, fxToPlay); } void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOOBJID source, bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, bool cancelOnRemoveBuff, - bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone) { + bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool applyOnTeammates) { // Prevent buffs from stacking. if (HasBuff(id)) { + m_Buffs[id].refCount++; + m_Buffs[id].time = duration; return; } - GameMessages::SendAddBuff(const_cast(m_Parent->GetObjectID()), source, (uint32_t)id, - (uint32_t)duration * 1000, addImmunity, cancelOnDamaged, cancelOnDeath, - cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone); + auto* team = TeamManager::Instance()->GetTeam(source); + bool addedByTeammate = false; + if (team) { + addedByTeammate = std::count(team->members.begin(), team->members.end(), m_Parent->GetObjectID()) > 0; + } + + GameMessages::SendAddBuff(const_cast(m_Parent->GetObjectID()), source, static_cast(id), + static_cast(duration) * 1000, addImmunity, cancelOnDamaged, cancelOnDeath, + cancelOnLogout, cancelOnRemoveBuff, cancelOnUi, cancelOnUnequip, cancelOnZone, addedByTeammate, applyOnTeammates); float tick = 0; float stacks = 0; @@ -102,7 +164,7 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { if (parameter.name == "overtime") { - auto* behaviorTemplateTable = CDClientManager::Instance().GetTable(); + auto* behaviorTemplateTable = CDClientManager::GetTable(); behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID; stacks = static_cast(parameter.values[1]); @@ -121,20 +183,46 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO buff.stacks = stacks; buff.source = source; buff.behaviorID = behaviorID; + buff.cancelOnDamaged = cancelOnDamaged; + buff.cancelOnDeath = cancelOnDeath; + buff.cancelOnLogout = cancelOnLogout; + buff.cancelOnRemoveBuff = cancelOnRemoveBuff; + buff.cancelOnUi = cancelOnUi; + buff.cancelOnUnequip = cancelOnUnequip; + buff.cancelOnZone = cancelOnZone; + buff.refCount = 1; m_Buffs.emplace(id, buff); + + auto* parent = GetParent(); + if (!cancelOnDeath) return; + + m_Parent->AddDieCallback([parent, id]() { + LOG_DEBUG("Removing buff %i because parent died", id); + if (!parent) return; + auto* buffComponent = parent->GetComponent(); + if (buffComponent) buffComponent->RemoveBuff(id, false, false, true); + }); } -void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity) { +void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity, bool ignoreRefCount) { const auto& iter = m_Buffs.find(id); if (iter == m_Buffs.end()) { return; } + if (!ignoreRefCount && !iter->second.cancelOnRemoveBuff) { + iter->second.refCount--; + LOG_DEBUG("refCount for buff %i is now %i", id, iter->second.refCount); + if (iter->second.refCount > 0) { + return; + } + } + GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); - m_Buffs.erase(iter); + m_BuffsToRemove.push_back(id); RemoveBuffEffect(id); } @@ -146,6 +234,7 @@ bool BuffComponent::HasBuff(int32_t id) { void BuffComponent::ApplyBuffEffect(int32_t id) { const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { + ApplyBuffFx(id, parameter); if (parameter.name == "max_health") { const auto maxHealth = parameter.value; @@ -182,6 +271,7 @@ void BuffComponent::ApplyBuffEffect(int32_t id) { void BuffComponent::RemoveBuffEffect(int32_t id) { const auto& parameters = GetBuffParameters(id); for (const auto& parameter : parameters) { + RemoveBuffFx(id, parameter); if (parameter.name == "max_health") { const auto maxHealth = parameter.value; @@ -251,13 +341,25 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { auto* buffEntry = buffElement->FirstChildElement("b"); - while (buffEntry != nullptr) { + while (buffEntry) { int32_t id = buffEntry->IntAttribute("id"); float t = buffEntry->FloatAttribute("t"); float tk = buffEntry->FloatAttribute("tk"); + float tt = buffEntry->FloatAttribute("tt"); int32_t s = buffEntry->FloatAttribute("s"); LWOOBJID sr = buffEntry->Int64Attribute("sr"); int32_t b = buffEntry->IntAttribute("b"); + int32_t refCount = buffEntry->IntAttribute("refCount"); + + bool cancelOnDamaged = buffEntry->BoolAttribute("cancelOnDamaged"); + bool cancelOnDeath = buffEntry->BoolAttribute("cancelOnDeath"); + bool cancelOnLogout = buffEntry->BoolAttribute("cancelOnLogout"); + bool cancelOnRemoveBuff = buffEntry->BoolAttribute("cancelOnRemoveBuff"); + bool cancelOnUi = buffEntry->BoolAttribute("cancelOnUi"); + bool cancelOnUnequip = buffEntry->BoolAttribute("cancelOnUnequip"); + bool cancelOnZone = buffEntry->BoolAttribute("cancelOnZone"); + bool applyOnTeammates = buffEntry->BoolAttribute("applyOnTeammates"); + Buff buff; buff.id = id; @@ -266,6 +368,18 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { buff.stacks = s; buff.source = sr; buff.behaviorID = b; + buff.refCount = refCount; + buff.tickTime = tt; + + buff.cancelOnDamaged = cancelOnDamaged; + buff.cancelOnDeath = cancelOnDeath; + buff.cancelOnLogout = cancelOnLogout; + buff.cancelOnRemoveBuff = cancelOnRemoveBuff; + buff.cancelOnUi = cancelOnUi; + buff.cancelOnUnequip = cancelOnUnequip; + buff.cancelOnZone = cancelOnZone; + buff.applyOnTeammates = applyOnTeammates; + m_Buffs.emplace(id, buff); @@ -288,15 +402,28 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) { buffElement->DeleteChildren(); } - for (const auto& buff : m_Buffs) { + for (const auto& [id, buff] : m_Buffs) { 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; - buffEntry->SetAttribute("id", buff.first); - buffEntry->SetAttribute("t", buff.second.time); - buffEntry->SetAttribute("tk", buff.second.tick); - buffEntry->SetAttribute("s", buff.second.stacks); - buffEntry->SetAttribute("sr", buff.second.source); - buffEntry->SetAttribute("b", buff.second.behaviorID); + buffEntry->SetAttribute("id", id); + buffEntry->SetAttribute("t", buff.time); + buffEntry->SetAttribute("tk", buff.tick); + buffEntry->SetAttribute("tt", buff.tickTime); + buffEntry->SetAttribute("s", buff.stacks); + buffEntry->SetAttribute("sr", buff.source); + buffEntry->SetAttribute("b", buff.behaviorID); + buffEntry->SetAttribute("refCount", buff.refCount); + + buffEntry->SetAttribute("cancelOnDamaged", buff.cancelOnDamaged); + buffEntry->SetAttribute("cancelOnDeath", buff.cancelOnDeath); + buffEntry->SetAttribute("cancelOnLogout", buff.cancelOnLogout); + buffEntry->SetAttribute("cancelOnRemoveBuff", buff.cancelOnRemoveBuff); + buffEntry->SetAttribute("cancelOnUi", buff.cancelOnUi); + buffEntry->SetAttribute("cancelOnUnequip", buff.cancelOnUnequip); + buffEntry->SetAttribute("cancelOnZone", buff.cancelOnZone); + buffEntry->SetAttribute("applyOnTeammates", buff.applyOnTeammates); buffElement->LinkEndChild(buffEntry); } @@ -309,9 +436,8 @@ const std::vector& BuffComponent::GetBuffParameters(int32_t buffI return pair->second; } - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT * FROM BuffParameters WHERE BuffID = ?;"); - query.bind(1, (int)buffId); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM BuffParameters WHERE BuffID = ?;"); + query.bind(1, static_cast(buffId)); auto result = query.execQuery(); @@ -321,11 +447,12 @@ const std::vector& BuffComponent::GetBuffParameters(int32_t buffI BuffParameter param; param.buffId = buffId; - param.name = result.getStringField(1); - param.value = result.getFloatField(2); + param.name = result.getStringField("ParameterName"); + param.value = result.getFloatField("NumberValue"); + param.effectId = result.getIntField("EffectID"); if (!result.fieldIsNull(3)) { - std::istringstream stream(result.getStringField(3)); + std::istringstream stream(result.getStringField("StringValue")); std::string token; while (std::getline(stream, token, ',')) { @@ -334,7 +461,7 @@ const std::vector& BuffComponent::GetBuffParameters(int32_t buffI param.values.push_back(value); } catch (std::invalid_argument& exception) { - Game::logger->Log("BuffComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); + LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); } } } diff --git a/dGame/dComponents/BuffComponent.h b/dGame/dComponents/BuffComponent.h index d9175883..18aa7a42 100644 --- a/dGame/dComponents/BuffComponent.h +++ b/dGame/dComponents/BuffComponent.h @@ -14,8 +14,7 @@ class Entity; /** * Extra information on effects to apply after applying a buff, for example whether to buff armor, imag or health and by how much */ -struct BuffParameter -{ +struct BuffParameter { int32_t buffId; std::string name; float value; @@ -26,8 +25,7 @@ struct BuffParameter /** * Meta information about a buff that can be applied, e.g. how long it's applied, who applied it, etc. */ -struct Buff -{ +struct Buff { int32_t id = 0; float time = 0; float tick = 0; @@ -35,14 +33,23 @@ struct Buff int32_t stacks = 0; LWOOBJID source = 0; int32_t behaviorID = 0; + bool cancelOnDamaged = false; + bool cancelOnDeath = false; + bool cancelOnLogout = false; + bool cancelOnRemoveBuff = false; + bool cancelOnUi = false; + bool cancelOnUnequip = false; + bool cancelOnZone = false; + bool applyOnTeammates = false; + uint32_t refCount = 0; }; /** * Allows for the application of buffs to the parent entity, altering health, armor and imagination. */ -class BuffComponent : public Component { +class BuffComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::BUFF; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUFF; explicit BuffComponent(Entity* parent); @@ -54,7 +61,7 @@ public: void UpdateXml(tinyxml2::XMLDocument* doc) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; @@ -74,14 +81,17 @@ public: */ void ApplyBuff(int32_t id, float duration, LWOOBJID source, bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, - bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false); + bool cancelOnUi = false, bool cancelOnUnequip = false, bool cancelOnZone = false, bool applyOnTeammates = false); + + void ApplyBuffFx(uint32_t buffId, const BuffParameter& buffName); + void RemoveBuffFx(uint32_t buffId, const BuffParameter& buffName); /** * Removes a buff from the parent entity, reversing its effects * @param id the id of the buff to remove * @param removeImmunity whether or not to remove immunity on removing the buff */ - void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false); + void RemoveBuff(int32_t id, bool fromUnEquip = false, bool removeImmunity = false, bool ignoreRefCount = false); /** * Returns whether or not the entity has a buff identified by `id` @@ -130,6 +140,9 @@ private: */ std::map m_Buffs; + // Buffs to remove at the end of the update frame. + std::vector m_BuffsToRemove; + /** * Parameters (=effects) for each buff */ diff --git a/dGame/dComponents/BuildBorderComponent.cpp b/dGame/dComponents/BuildBorderComponent.cpp index af31f939..066a8c74 100644 --- a/dGame/dComponents/BuildBorderComponent.cpp +++ b/dGame/dComponents/BuildBorderComponent.cpp @@ -4,7 +4,7 @@ #include "GameMessages.h" #include "Entity.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "InventoryComponent.h" #include "Item.h" #include "PropertyManagementComponent.h" @@ -24,7 +24,7 @@ void BuildBorderComponent::OnUse(Entity* originator) { if (!entities.empty()) { buildArea = entities[0]->GetObjectID(); - Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque"); + LOG("Using PropertyPlaque"); } auto* inventoryComponent = originator->GetComponent(); @@ -41,7 +41,7 @@ void BuildBorderComponent::OnUse(Entity* originator) { inventoryComponent->PushEquippedItems(); - Game::logger->Log("BuildBorderComponent", "Starting with %llu", buildArea); + LOG("Starting with %llu", buildArea); if (PropertyManagementComponent::Instance() != nullptr) { GameMessages::SendStartArrangingWithItem( @@ -56,7 +56,7 @@ void BuildBorderComponent::OnUse(Entity* originator) { 4, 0, -1, - NiPoint3::ZERO, + NiPoint3Constant::ZERO, 0 ); } else { diff --git a/dGame/dComponents/BuildBorderComponent.h b/dGame/dComponents/BuildBorderComponent.h index dc5afc8a..a59ac363 100644 --- a/dGame/dComponents/BuildBorderComponent.h +++ b/dGame/dComponents/BuildBorderComponent.h @@ -14,9 +14,9 @@ /** * Component for the build border, allowing the user to start building when interacting with it */ -class BuildBorderComponent : public Component { +class BuildBorderComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::BUILD_BORDER; BuildBorderComponent(Entity* parent); ~BuildBorderComponent() override; diff --git a/dGame/dComponents/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index 67f4ece5..ac509e11 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -1,13 +1,18 @@ -set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" +set(DGAME_DCOMPONENTS_SOURCES + "ActivityComponent.cpp" + "BaseCombatAIComponent.cpp" "BouncerComponent.cpp" "BuffComponent.cpp" "BuildBorderComponent.cpp" "CharacterComponent.cpp" + "CollectibleComponent.cpp" "Component.cpp" "ControllablePhysicsComponent.cpp" "DestroyableComponent.cpp" "DonationVendorComponent.cpp" + "GhostComponent.cpp" "InventoryComponent.cpp" + "ItemComponent.cpp" "LevelProgressionComponent.cpp" "LUPExhibitComponent.cpp" "MissionComponent.cpp" @@ -18,27 +23,35 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "MovingPlatformComponent.cpp" "PetComponent.cpp" "PhantomPhysicsComponent.cpp" + "PhysicsComponent.cpp" "PlayerForcedMovementComponent.cpp" "PossessableComponent.cpp" "PossessorComponent.cpp" - "PropertyComponent.cpp" "PropertyEntranceComponent.cpp" "PropertyManagementComponent.cpp" "PropertyVendorComponent.cpp" "ProximityMonitorComponent.cpp" "RacingControlComponent.cpp" "RailActivatorComponent.cpp" - "RebuildComponent.cpp" + "QuickBuildComponent.cpp" "RenderComponent.cpp" "RigidbodyPhantomPhysicsComponent.cpp" - "RocketLaunchLupComponent.cpp" + "MultiZoneEntranceComponent.cpp" "RocketLaunchpadControlComponent.cpp" - "ScriptedActivityComponent.cpp" "ShootingGalleryComponent.cpp" "SimplePhysicsComponent.cpp" "SkillComponent.cpp" "SoundTriggerComponent.cpp" "SwitchComponent.cpp" "TriggerComponent.cpp" - "VehiclePhysicsComponent.cpp" - "VendorComponent.cpp" PARENT_SCOPE) + "HavokVehiclePhysicsComponent.cpp" + "VendorComponent.cpp" + "MiniGameControlComponent.cpp" +) + +add_library(dComponents STATIC ${DGAME_DCOMPONENTS_SOURCES}) +target_include_directories(dComponents PRIVATE ${PROJECT_SOURCE_DIR}/dScripts/02_server/Map/General) # PetDigServer.h +target_precompile_headers(dComponents REUSE_FROM dGameBase) +target_link_libraries(dComponents + PUBLIC dPhysics dDatabase + INTERFACE dUtilities dCommon dBehaviors dChatFilter dMission dInventory) diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 10a4e9db..e4e2ba4b 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -1,8 +1,8 @@ #include "CharacterComponent.h" -#include +#include "BitStream.h" #include "tinyxml2.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "GeneralUtils.h" #include "dServer.h" #include "dZoneManager.h" @@ -10,14 +10,21 @@ #include "InventoryComponent.h" #include "ControllablePhysicsComponent.h" #include "EntityManager.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "GameMessages.h" #include "Item.h" #include "Amf3.h" #include "eGameMasterLevel.h" #include "eGameActivity.h" +#include "User.h" +#include "Database.h" +#include "CDRewardCodesTable.h" +#include "Mail.h" +#include "ZoneInstanceManager.h" +#include "WorldPackets.h" +#include -CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) { +CharacterComponent::CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress) : Component(parent) { m_Character = character; m_IsRacing = false; @@ -39,6 +46,7 @@ CharacterComponent::CharacterComponent(Entity* parent, Character* character) : C m_CurrentActivity = eGameActivity::NONE; m_CountryCode = 0; m_LastUpdateTimestamp = std::time(nullptr); + m_SystemAddress = systemAddress; } bool CharacterComponent::LandingAnimDisabled(int zoneID) { @@ -70,13 +78,17 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) { CharacterComponent::~CharacterComponent() { } -void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) { - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); + outBitStream->Write(m_ClaimCodes[0] != 0); + if (m_ClaimCodes[0] != 0) outBitStream->Write(m_ClaimCodes[0]); + outBitStream->Write(m_ClaimCodes[1] != 0); + if (m_ClaimCodes[1] != 0) outBitStream->Write(m_ClaimCodes[1]); + outBitStream->Write(m_ClaimCodes[2] != 0); + if (m_ClaimCodes[2] != 0) outBitStream->Write(m_ClaimCodes[2]); + outBitStream->Write(m_ClaimCodes[3] != 0); + if (m_ClaimCodes[3] != 0) outBitStream->Write(m_ClaimCodes[3]); outBitStream->Write(m_Character->GetHairColor()); outBitStream->Write(m_Character->GetHairStyle()); @@ -126,7 +138,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit outBitStream->Write0(); outBitStream->Write(m_IsLanding); if (m_IsLanding) { - outBitStream->Write(uint16_t(m_LastRocketConfig.size())); + outBitStream->Write(m_LastRocketConfig.size()); for (uint16_t character : m_LastRocketConfig) { outBitStream->Write(character); } @@ -148,7 +160,7 @@ void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInit outBitStream->Write(m_DirtySocialInfo); if (m_DirtySocialInfo) { outBitStream->Write(m_GuildID); - outBitStream->Write(static_cast(m_GuildName.size())); + outBitStream->Write(m_GuildName.size()); if (!m_GuildName.empty()) outBitStream->WriteBits(reinterpret_cast(m_GuildName.c_str()), static_cast(m_GuildName.size()) * sizeof(wchar_t) * 8); @@ -178,13 +190,20 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); if (!character) { - Game::logger->Log("CharacterComponent", "Failed to find char tag while loading XML!"); + LOG("Failed to find char tag while loading XML!"); return; } if (character->QueryAttribute("rpt", &m_Reputation) == tinyxml2::XML_NO_ATTRIBUTE) { SetReputation(0); } + character->QueryUnsigned64Attribute("co", &m_ClaimCodes[0]); + character->QueryUnsigned64Attribute("co1", &m_ClaimCodes[1]); + character->QueryUnsigned64Attribute("co2", &m_ClaimCodes[2]); + character->QueryUnsigned64Attribute("co3", &m_ClaimCodes[3]); + + AwardClaimCodes(); + character->QueryInt64Attribute("ls", &m_Uscore); // Load the statistics @@ -213,7 +232,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { uint32_t mapID; child->QueryAttribute("map", &mapID); - m_ZoneStatistics.insert({ (LWOMAPID)mapID, statistics }); + m_ZoneStatistics.insert({ static_cast(mapID), statistics }); child = child->NextSiblingElement(); } @@ -283,7 +302,7 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf"); if (!minifig) { - Game::logger->Log("CharacterComponent", "Failed to find mf tag while updating XML!"); + LOG("Failed to find mf tag while updating XML!"); return; } @@ -303,10 +322,15 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); if (!character) { - Game::logger->Log("CharacterComponent", "Failed to find char tag while updating XML!"); + LOG("Failed to find char tag while updating XML!"); return; } + if (m_ClaimCodes[0] != 0) character->SetAttribute("co", m_ClaimCodes[0]); + if (m_ClaimCodes[1] != 0) character->SetAttribute("co1", m_ClaimCodes[1]); + if (m_ClaimCodes[2] != 0) character->SetAttribute("co2", m_ClaimCodes[2]); + if (m_ClaimCodes[3] != 0) character->SetAttribute("co3", m_ClaimCodes[3]); + character->SetAttribute("ls", m_Uscore); // Custom attribute to keep track of reputation. character->SetAttribute("rpt", GetReputation()); @@ -351,7 +375,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { // auto newUpdateTimestamp = std::time(nullptr); - Game::logger->Log("TotalTimePlayed", "Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp); + LOG("Time since last save: %d", newUpdateTimestamp - m_LastUpdateTimestamp); m_TotalTimePlayed += newUpdateTimestamp - m_LastUpdateTimestamp; character->SetAttribute("time", m_TotalTimePlayed); @@ -381,7 +405,7 @@ Item* CharacterComponent::GetRocket(Entity* player) { } if (!rocket) { - Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!"); + LOG("Unable to find rocket to equip!"); return rocket; } return rocket; @@ -477,7 +501,7 @@ void CharacterComponent::TrackArmorDelta(int32_t armor) { } } -void CharacterComponent::TrackRebuildComplete() { +void CharacterComponent::TrackQuickBuildComplete() { UpdatePlayerStatistic(QuickBuildsCompleted); const auto mapID = Game::zoneManager->GetZoneID().GetMapID(); @@ -494,9 +518,9 @@ void CharacterComponent::TrackPositionUpdate(const NiPoint3& newPosition) { const auto distance = NiPoint3::Distance(newPosition, m_Parent->GetPosition()); if (m_IsRacing) { - UpdatePlayerStatistic(DistanceDriven, (uint64_t)distance); + UpdatePlayerStatistic(DistanceDriven, static_cast(distance)); } else { - UpdatePlayerStatistic(MetersTraveled, (uint64_t)distance); + UpdatePlayerStatistic(MetersTraveled, static_cast(distance)); } } @@ -699,7 +723,7 @@ std::string CharacterComponent::StatisticsToString() const { } uint64_t CharacterComponent::GetStatisticFromSplit(std::vector split, uint32_t index) { - return split.size() > index ? std::stoul(split.at(index)) : 0; + return split.size() > index ? std::stoull(split.at(index)) : 0; } ZoneStatistics& CharacterComponent::GetZoneStatisticsForMap(LWOMAPID mapID) { @@ -737,3 +761,77 @@ void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventu arrayToSend.Insert(ventureVisionType, showFaction); GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend); } + +void CharacterComponent::AwardClaimCodes() { + if (!m_Parent || !m_Parent->GetCharacter()) return; + auto* user = m_Parent->GetCharacter()->GetParentUser(); + if (!user) return; + + auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID()); + if (rewardCodes.empty()) return; + + auto* cdrewardCodes = CDClientManager::GetTable(); + for (auto const rewardCode : rewardCodes) { + LOG_DEBUG("Processing RewardCode %i", rewardCode); + const uint32_t rewardCodeIndex = rewardCode >> 6; + const uint32_t bitIndex = rewardCode % 64; + if (GeneralUtils::CheckBit(m_ClaimCodes[rewardCodeIndex], bitIndex)) continue; + m_ClaimCodes[rewardCodeIndex] = GeneralUtils::SetBit(m_ClaimCodes[rewardCodeIndex], bitIndex); + + // Don't send it on this one since it's default and the mail doesn't make sense + if (rewardCode == 30) continue; + + auto attachmentLOT = cdrewardCodes->GetAttachmentLOT(rewardCode); + std::ostringstream subject; + subject << "%[RewardCodes_" << rewardCode << "_subjectText]"; + std::ostringstream body; + body << "%[RewardCodes_" << rewardCode << "_bodyText]"; + Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1); + } +} + +void CharacterComponent::SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId) const { + const auto objid = m_Parent->GetObjectID(); + + ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneId, cloneId, false, [objid](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { + auto* entity = Game::entityManager->GetEntity(objid); + + if (!entity) return; + + const auto sysAddr = entity->GetSystemAddress(); + + auto* character = entity->GetCharacter(); + auto* characterComponent = entity->GetComponent(); + + if (character && characterComponent) { + character->SetZoneID(zoneID); + character->SetZoneInstance(zoneInstance); + character->SetZoneClone(zoneClone); + + characterComponent->SetLastRocketConfig(u""); + + character->SaveXMLToDatabase(); + } + + WorldPackets::SendTransferToWorld(sysAddr, serverIP, serverPort, mythranShift); + + Game::entityManager->DestructEntity(entity); + }); +} + +const SystemAddress& CharacterComponent::GetSystemAddress() const { + return m_SystemAddress; +} + +void CharacterComponent::SetRespawnPos(const NiPoint3& position) { + if (!m_Character) return; + + m_respawnPos = position; + + m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position); + +} + +void CharacterComponent::SetRespawnRot(const NiQuaternion& rotation) { + m_respawnRot = rotation; +} diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index c367aefc..01c26f9a 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -10,6 +10,8 @@ #include "CDMissionsTable.h" #include "tinyxml2.h" #include "eReplicaComponentType.h" +#include +#include "Loot.h" enum class eGameActivity : uint32_t; @@ -60,17 +62,17 @@ enum StatisticID { /** * Represents a character, including their rockets and stats */ -class CharacterComponent : public Component { +class CharacterComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CHARACTER; - CharacterComponent(Entity* parent, Character* character); + CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress); ~CharacterComponent() override; void LoadFromXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Updates the rocket configuration using a LOT string separated by commas @@ -232,7 +234,7 @@ public: /** * Handles completing a rebuild by updating the statistics */ - void TrackRebuildComplete(); + void TrackQuickBuildComplete(); /** * Tracks a player completing the race, also updates stats @@ -280,6 +282,30 @@ public: LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;}; + /** + * Sends a player to another zone with an optional clone ID + * + * @param zoneId zoneID for the new instance. + * @param cloneId cloneID for the new instance. + */ + void SendToZone(LWOMAPID zoneId, LWOCLONEID cloneId = 0) const; + + const SystemAddress& GetSystemAddress() const; + + const NiPoint3& GetRespawnPosition() const { return m_respawnPos; }; + + void SetRespawnPos(const NiPoint3& position); + + const NiQuaternion& GetRespawnRotation() const { return m_respawnRot; }; + + void SetRespawnRot(const NiQuaternion& rotation); + + std::map& GetDroppedLoot() { return m_DroppedLoot; }; + + uint64_t GetDroppedCoins() const { return m_DroppedCoins; }; + + void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; }; + /** * Character info regarding this character, including clothing styles, etc. */ @@ -566,6 +592,20 @@ private: LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY; + + std::array m_ClaimCodes{}; + + void AwardClaimCodes(); + + SystemAddress m_SystemAddress; + + NiPoint3 m_respawnPos; + + NiQuaternion m_respawnRot; + + std::map m_DroppedLoot; + + uint64_t m_DroppedCoins = 0; }; #endif // CHARACTERCOMPONENT_H diff --git a/dGame/dComponents/CollectibleComponent.cpp b/dGame/dComponents/CollectibleComponent.cpp new file mode 100644 index 00000000..99fcc681 --- /dev/null +++ b/dGame/dComponents/CollectibleComponent.cpp @@ -0,0 +1,5 @@ +#include "CollectibleComponent.h" + +void CollectibleComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { + outBitStream->Write(GetCollectibleId()); +} diff --git a/dGame/dComponents/CollectibleComponent.h b/dGame/dComponents/CollectibleComponent.h new file mode 100644 index 00000000..10a23293 --- /dev/null +++ b/dGame/dComponents/CollectibleComponent.h @@ -0,0 +1,18 @@ +#ifndef __COLLECTIBLECOMPONENT__H__ +#define __COLLECTIBLECOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" + +class CollectibleComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::COLLECTIBLE; + CollectibleComponent(Entity* parentEntity, int32_t collectibleId) : Component(parentEntity), m_CollectibleId(collectibleId) {} + + int16_t GetCollectibleId() const { return m_CollectibleId; } + void Serialize(RakNet::BitStream* outBitStream, bool isConstruction) override; +private: + int16_t m_CollectibleId = 0; +}; + +#endif //!__COLLECTIBLECOMPONENT__H__ diff --git a/dGame/dComponents/Component.cpp b/dGame/dComponents/Component.cpp index ca018c29..1136456c 100644 --- a/dGame/dComponents/Component.cpp +++ b/dGame/dComponents/Component.cpp @@ -28,3 +28,7 @@ void Component::UpdateXml(tinyxml2::XMLDocument* doc) { void Component::LoadFromXml(tinyxml2::XMLDocument* doc) { } + +void Component::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { + +} diff --git a/dGame/dComponents/Component.h b/dGame/dComponents/Component.h index 9b0df9fd..70f30f76 100644 --- a/dGame/dComponents/Component.h +++ b/dGame/dComponents/Component.h @@ -1,14 +1,13 @@ #pragma once -#include "../thirdparty/tinyxml2/tinyxml2.h" +#include "tinyxml2.h" class Entity; /** * Component base class, provides methods for game loop updates, usage events and loading and saving to XML. */ -class Component -{ +class Component { public: Component(Entity* parent); virtual ~Component(); @@ -43,6 +42,8 @@ public: */ virtual void LoadFromXml(tinyxml2::XMLDocument* doc); + virtual void Serialize(RakNet::BitStream* outBitStream, bool isConstruction); + protected: /** diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index a658ccd7..be5227a0 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -1,7 +1,7 @@ #include "ControllablePhysicsComponent.h" #include "Entity.h" #include "BitStream.h" -#include "dLogger.h" +#include "Logger.h" #include "Game.h" #include "dpWorld.h" @@ -15,15 +15,12 @@ #include "LevelProgressionComponent.h" #include "eStateChangeType.h" -ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Component(entity) { - m_Position = {}; - m_Rotation = NiQuaternion::IDENTITY; +ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) { m_Velocity = {}; m_AngularVelocity = {}; m_InJetpackMode = false; m_IsOnGround = true; m_IsOnRail = false; - m_DirtyPosition = true; m_DirtyVelocity = true; m_DirtyAngularVelocity = true; m_dpEntity = nullptr; @@ -55,18 +52,18 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Com return; if (entity->GetLOT() == 1) { - Game::logger->Log("ControllablePhysicsComponent", "Using patch to load minifig physics"); + LOG("Using patch to load minifig physics"); float radius = 1.5f; m_dpEntity = new dpEntity(m_Parent->GetObjectID(), radius, false); m_dpEntity->SetCollisionGroup(COLLISION_GROUP_DYNAMIC | COLLISION_GROUP_FRIENDLY); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } } ControllablePhysicsComponent::~ControllablePhysicsComponent() { if (m_dpEntity) { - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); } } @@ -74,7 +71,7 @@ void ControllablePhysicsComponent::Update(float deltaTime) { } -void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { //If this is a creation, then we assume the position is dirty, even when it isn't. //This is because new clients will still need to receive the position. //if (bIsInitialUpdate) m_DirtyPosition = true; @@ -164,7 +161,7 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bo void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); if (!character) { - Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag!"); + LOG("Failed to find char tag!"); return; } @@ -181,22 +178,16 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { m_DirtyPosition = true; } -void ControllablePhysicsComponent::ResetFlags() { - m_DirtyAngularVelocity = false; - m_DirtyPosition = false; - m_DirtyVelocity = false; -} - void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); if (!character) { - Game::logger->Log("ControllablePhysicsComponent", "Failed to find char tag while updating XML!"); + LOG("Failed to find char tag while updating XML!"); return; } auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); - if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0) { + if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { character->SetAttribute("lzx", m_Position.x); character->SetAttribute("lzy", m_Position.y); character->SetAttribute("lzz", m_Position.z); @@ -208,26 +199,14 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) { } void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { - if (m_Static) { - return; - } - - m_Position.x = pos.x; - m_Position.y = pos.y; - m_Position.z = pos.z; - m_DirtyPosition = true; - + if (m_Static) return; + PhysicsComponent::SetPosition(pos); if (m_dpEntity) m_dpEntity->SetPosition(pos); } void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) { - if (m_Static) { - return; - } - - m_Rotation = rot; - m_DirtyPosition = true; - + if (m_Static) return; + PhysicsComponent::SetRotation(rot); if (m_dpEntity) m_dpEntity->SetRotation(rot); } @@ -289,7 +268,7 @@ void ControllablePhysicsComponent::RemovePickupRadiusScale(float value) { if (pos != m_ActivePickupRadiusScales.end()) { m_ActivePickupRadiusScales.erase(pos); } else { - Game::logger->LogDebug("ControllablePhysicsComponent", "Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.", value, m_ActivePickupRadiusScales.size()); + LOG_DEBUG("Warning: Could not find pickup radius %f in list of active radii. List has %i active radii.", value, m_ActivePickupRadiusScales.size()); return; } @@ -314,7 +293,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) { if (pos != m_ActiveSpeedBoosts.end()) { m_ActiveSpeedBoosts.erase(pos); } else { - Game::logger->LogDebug("ControllablePhysicsComponent", "Warning: Could not find speedboost %f in list of active speedboosts. List has %i active speedboosts.", value, m_ActiveSpeedBoosts.size()); + LOG_DEBUG("Warning: Could not find speedboost %f in list of active speedboosts. List has %i active speedboosts.", value, m_ActiveSpeedBoosts.size()); return; } @@ -332,7 +311,7 @@ void ControllablePhysicsComponent::RemoveSpeedboost(float value) { void ControllablePhysicsComponent::ActivateBubbleBuff(eBubbleType bubbleType, bool specialAnims){ if (m_IsInBubble) { - Game::logger->Log("ControllablePhysicsComponent", "Already in bubble"); + LOG("Already in bubble"); return; } m_BubbleType = bubbleType; diff --git a/dGame/dComponents/ControllablePhysicsComponent.h b/dGame/dComponents/ControllablePhysicsComponent.h index 470a7af4..e850cfeb 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.h +++ b/dGame/dComponents/ControllablePhysicsComponent.h @@ -6,7 +6,7 @@ #include "NiPoint3.h" #include "NiQuaternion.h" #include "tinyxml2.h" -#include "Component.h" +#include "PhysicsComponent.h" #include "dpCollisionChecks.h" #include "PhantomPhysicsComponent.h" #include "eBubbleType.h" @@ -19,17 +19,16 @@ enum class eStateChangeType : uint32_t; /** * Handles the movement of controllable Entities, e.g. enemies and players */ -class ControllablePhysicsComponent : public Component { +class ControllablePhysicsComponent : public PhysicsComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS; ControllablePhysicsComponent(Entity* entity); ~ControllablePhysicsComponent() override; void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override; - void ResetFlags(); void UpdateXml(tinyxml2::XMLDocument* doc) override; /** @@ -37,26 +36,14 @@ public: * If the entity is static, this is a no-op. * @param pos The position to set */ - void SetPosition(const NiPoint3& pos); - - /** - * Returns the current position of the entity - * @return The current position of the entity - */ - const NiPoint3& GetPosition() const { return m_Position; } + void SetPosition(const NiPoint3& pos) override; /** * Sets the rotation of this entity, ensures this change is serialized next tick. If the entity is static, this is * a no-op. * @param rot the rotation to set */ - void SetRotation(const NiQuaternion& rot); - - /** - * Returns the current rotation of this entity - * @return the current rotation of this entity - */ - const NiQuaternion& GetRotation() const { return m_Rotation; } + void SetRotation(const NiQuaternion& rot) override; /** * Sets the current velocity of this entity, ensures that this change is serialized next tick. If the entity is @@ -323,21 +310,6 @@ private: */ dpEntity* m_dpEntity; - /** - * Whether or not the position is dirty, forcing a serialization update of the position - */ - bool m_DirtyPosition; - - /** - * The current position of the entity - */ - NiPoint3 m_Position; - - /** - * The current rotation of the entity - */ - NiQuaternion m_Rotation; - /** * Whether or not the velocity is dirty, forcing a serialization of the velocity */ diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index 4c726bad..68271f26 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -1,6 +1,6 @@ #include "DestroyableComponent.h" -#include -#include "dLogger.h" +#include "BitStream.h" +#include "Logger.h" #include "Game.h" #include "dConfig.h" @@ -11,7 +11,7 @@ #include "CDClientManager.h" #include "CDDestructibleComponentTable.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "CppScripts.h" #include "Loot.h" #include "Character.h" @@ -72,24 +72,27 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) { m_ImmuneToImaginationLossCount = 0; m_ImmuneToQuickbuildInterruptCount = 0; m_ImmuneToPullToPointCount = 0; + m_DeathBehavior = -1; + + m_DamageCooldownTimer = 0.0f; } DestroyableComponent::~DestroyableComponent() { } void DestroyableComponent::Reinitialize(LOT templateID) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); int32_t buffComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::BUFF); int32_t collectibleComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::COLLECTIBLE); - int32_t rebuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD); + int32_t quickBuildComponentID = compRegistryTable->GetByIDAndType(templateID, eReplicaComponentType::QUICK_BUILD); int32_t componentID = 0; if (collectibleComponentID > 0) componentID = collectibleComponentID; - if (rebuildComponentID > 0) componentID = rebuildComponentID; + if (quickBuildComponentID > 0) componentID = quickBuildComponentID; if (buffComponentID > 0) componentID = buffComponentID; - CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable(); + CDDestructibleComponentTable* destCompTable = CDClientManager::GetTable(); std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); if (componentID > 0) { @@ -119,7 +122,7 @@ void DestroyableComponent::Reinitialize(LOT templateID) { } } -void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) { +void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) { outBitStream->Write1(); // always write these on construction outBitStream->Write(m_ImmuneToBasicAttackCount); @@ -151,7 +154,7 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn outBitStream->Write(m_fMaxArmor); outBitStream->Write(m_fMaxImagination); - outBitStream->Write(uint32_t(m_FactionIDs.size())); + outBitStream->Write(m_FactionIDs.size()); for (size_t i = 0; i < m_FactionIDs.size(); ++i) { outBitStream->Write(m_FactionIDs[i]); } @@ -178,10 +181,14 @@ void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn } } +void DestroyableComponent::Update(float deltaTime) { + m_DamageCooldownTimer -= deltaTime; +} + void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); if (!dest) { - Game::logger->Log("DestroyableComponent", "Failed to find dest tag!"); + LOG("Failed to find dest tag!"); return; } @@ -203,7 +210,7 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); if (!dest) { - Game::logger->Log("DestroyableComponent", "Failed to find dest tag!"); + LOG("Failed to find dest tag!"); return; } @@ -244,13 +251,14 @@ void DestroyableComponent::SetMaxHealth(float value, bool playAnim) { if (playAnim) { // Now update the player bar - if (!m_Parent->GetParentUser()) return; + auto* characterComponent = m_Parent->GetComponent(); + if (!characterComponent) return; AMFArrayValue args; args.Insert("amount", std::to_string(difference)); args.Insert("type", "health"); - GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); + GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args); } Game::entityManager->SerializeEntity(m_Parent); @@ -285,13 +293,14 @@ void DestroyableComponent::SetMaxArmor(float value, bool playAnim) { if (playAnim) { // Now update the player bar - if (!m_Parent->GetParentUser()) return; + auto* characterComponent = m_Parent->GetComponent(); + if (!characterComponent) return; AMFArrayValue args; args.Insert("amount", std::to_string(value)); args.Insert("type", "armor"); - GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); + GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args); } Game::entityManager->SerializeEntity(m_Parent); @@ -325,13 +334,14 @@ void DestroyableComponent::SetMaxImagination(float value, bool playAnim) { if (playAnim) { // Now update the player bar - if (!m_Parent->GetParentUser()) return; + auto* characterComponent = m_Parent->GetComponent(); + if (!characterComponent) return; AMFArrayValue args; args.Insert("amount", std::to_string(difference)); args.Insert("type", "imagination"); - GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent->GetParentUser()->GetSystemAddress(), "MaxPlayerBarUpdate", args); + GameMessages::SendUIMessageServerToSingleClient(m_Parent, characterComponent->GetSystemAddress(), "MaxPlayerBarUpdate", args); } Game::entityManager->SerializeEntity(m_Parent); } @@ -363,16 +373,17 @@ void DestroyableComponent::SetIsShielded(bool value) { void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignoreChecks) { // Ignore factionID -1 - if (factionID == -1 && !ignoreChecks) { - return; - } + if (factionID == -1 && !ignoreChecks) return; + + // if we already have that faction, don't add it again + if (std::find(m_FactionIDs.begin(), m_FactionIDs.end(), factionID) != m_FactionIDs.end()) return; m_FactionIDs.push_back(factionID); m_DirtyHealth = true; auto query = CDClientDatabase::CreatePreppedStmt( "SELECT enemyList FROM Factions WHERE faction = ?;"); - query.bind(1, (int)factionID); + query.bind(1, static_cast(factionID)); auto result = query.execQuery(); @@ -407,6 +418,14 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore } bool DestroyableComponent::IsEnemy(const Entity* other) const { + if (m_Parent->IsPlayer() && other->IsPlayer()) { + auto* thisCharacterComponent = m_Parent->GetComponent(); + if (!thisCharacterComponent) return false; + auto* otherCharacterComponent = other->GetComponent(); + if (!otherCharacterComponent) return false; + if (thisCharacterComponent->GetPvpEnabled() && otherCharacterComponent->GetPvpEnabled()) return true; + return false; + } const auto* otherDestroyableComponent = other->GetComponent(); if (otherDestroyableComponent != nullptr) { for (const auto enemyFaction : m_EnemyFactionIDs) { @@ -454,6 +473,10 @@ bool DestroyableComponent::IsImmune() const { return m_IsGMImmune || m_ImmuneToBasicAttackCount > 0; } +bool DestroyableComponent::IsCooldownImmune() const { + return m_DamageCooldownTimer > 0.0f; +} + bool DestroyableComponent::IsKnockbackImmune() const { auto* characterComponent = m_Parent->GetComponent(); auto* inventoryComponent = m_Parent->GetComponent(); @@ -485,43 +508,6 @@ Entity* DestroyableComponent::GetKiller() const { return Game::entityManager->GetEntity(m_KillerID); } -bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignoreFactions, const bool targetEnemy, const bool targetFriend) const { - auto* targetEntity = Game::entityManager->GetEntity(target); - - if (targetEntity == nullptr) { - Game::logger->Log("DestroyableComponent", "Invalid entity for checking validity (%llu)!", target); - return false; - } - - auto* targetDestroyable = targetEntity->GetComponent(); - - if (targetDestroyable == nullptr) { - return false; - } - - auto* targetQuickbuild = targetEntity->GetComponent(); - - if (targetQuickbuild != nullptr) { - const auto state = targetQuickbuild->GetState(); - - if (state != eRebuildState::COMPLETED) { - return false; - } - } - - if (ignoreFactions) { - return true; - } - - // Get if the target entity is an enemy and friend - bool isEnemy = IsEnemy(targetEntity); - bool isFriend = IsFriend(targetEntity); - - // Return true if the target type matches what we are targeting - return (isEnemy && targetEnemy) || (isFriend && targetFriend); -} - - void DestroyableComponent::Heal(const uint32_t health) { auto current = static_cast(GetHealth()); const auto max = static_cast(GetMaxHealth()); @@ -573,7 +559,8 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 return; } - if (IsImmune()) { + if (IsImmune() || IsCooldownImmune()) { + LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); return; } @@ -661,28 +648,28 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 } //check if hardcore mode is enabled - if (Game::entityManager->GetHardcoreMode()) { + if (Game::entityManager->GetHardcoreMode()) { DoHardcoreModeDrops(source); - } + } Smash(source, eKillType::VIOLENT, u"", skillID); } void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) { m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd)); - Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID()); - Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size()); + LOG_DEBUG("Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID()); + LOG_DEBUG("Number of subscribed scripts %i", m_SubscribedScripts.size()); } void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) { auto foundScript = m_SubscribedScripts.find(scriptObjId); if (foundScript != m_SubscribedScripts.end()) { m_SubscribedScripts.erase(foundScript); - Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID()); + LOG_DEBUG("Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID()); } else { - Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId); + LOG_DEBUG("Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId); } - Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size()); + LOG_DEBUG("Number of subscribed scripts %i", m_SubscribedScripts.size()); } void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) { @@ -763,18 +750,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType auto* member = Game::entityManager->GetEntity(specificOwner); - if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); + if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); } else { for (const auto memberId : team->members) { // Free for all auto* member = Game::entityManager->GetEntity(memberId); if (member == nullptr) continue; - LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); + Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins()); } } } else { // drop loot for non team user - LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins()); + Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins()); } } } else { @@ -792,7 +779,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType coinsTotal -= coinsToLose; - LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose); + Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose); character->SetCoins(coinsTotal, eLootSourceType::PICKUP); } } @@ -812,7 +799,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType } } - m_Parent->Kill(owner); + m_Parent->Kill(owner, killType); } void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) { @@ -823,16 +810,16 @@ void DestroyableComponent::SetFaction(int32_t factionID, bool ignoreChecks) { } void DestroyableComponent::SetStatusImmunity( - const eStateChangeType state, - const bool bImmuneToBasicAttack, - const bool bImmuneToDamageOverTime, - const bool bImmuneToKnockback, - const bool bImmuneToInterrupt, - const bool bImmuneToSpeed, - const bool bImmuneToImaginationGain, - const bool bImmuneToImaginationLoss, - const bool bImmuneToQuickbuildInterrupt, - const bool bImmuneToPullToPoint) { + const eStateChangeType state, + const bool bImmuneToBasicAttack, + const bool bImmuneToDamageOverTime, + const bool bImmuneToKnockback, + const bool bImmuneToInterrupt, + const bool bImmuneToSpeed, + const bool bImmuneToImaginationGain, + const bool bImmuneToImaginationLoss, + const bool bImmuneToQuickbuildInterrupt, + const bool bImmuneToPullToPoint) { if (state == eStateChangeType::POP) { if (bImmuneToBasicAttack && m_ImmuneToBasicAttackCount > 0) m_ImmuneToBasicAttackCount -= 1; @@ -845,7 +832,7 @@ void DestroyableComponent::SetStatusImmunity( if (bImmuneToQuickbuildInterrupt && m_ImmuneToQuickbuildInterruptCount > 0) m_ImmuneToQuickbuildInterruptCount -= 1; if (bImmuneToPullToPoint && m_ImmuneToPullToPointCount > 0) m_ImmuneToPullToPointCount -= 1; - } else if (state == eStateChangeType::PUSH){ + } else if (state == eStateChangeType::PUSH) { if (bImmuneToBasicAttack) m_ImmuneToBasicAttackCount += 1; if (bImmuneToDamageOverTime) m_ImmuneToDamageOverTimeCount += 1; if (bImmuneToKnockback) m_ImmuneToKnockbackCount += 1; @@ -972,7 +959,7 @@ void DestroyableComponent::AddOnHitCallback(const std::function& m_OnHitCallbacks.push_back(callback); } -void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){ +void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source) { //check if this is a player: if (m_Parent->IsPlayer()) { //remove hardcore_lose_uscore_on_death_percent from the player's uscore: @@ -990,9 +977,9 @@ void DestroyableComponent::DoHardcoreModeDrops(const LWOOBJID source){ if (inventory) { //get the items inventory: auto items = inventory->GetInventory(eInventoryType::ITEMS); - if (items){ + if (items) { auto itemMap = items->GetItems(); - if (!itemMap.empty()){ + if (!itemMap.empty()) { for (const auto& item : itemMap) { //drop the item: if (!item.second) continue; diff --git a/dGame/dComponents/DestroyableComponent.h b/dGame/dComponents/DestroyableComponent.h index 5e5133b7..1f45b43e 100644 --- a/dGame/dComponents/DestroyableComponent.h +++ b/dGame/dComponents/DestroyableComponent.h @@ -17,14 +17,15 @@ enum class eStateChangeType : uint32_t; * Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which * indicate which enemies this entity has. */ -class DestroyableComponent : public Component { +class DestroyableComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DESTROYABLE; DestroyableComponent(Entity* parentEntity); ~DestroyableComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); + void Update(float deltaTime) override; + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void LoadFromXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument* doc) override; @@ -166,6 +167,11 @@ public: */ bool IsImmune() const; + /** + * @return whether this entity is currently immune to attacks due to a damage cooldown period + */ + bool IsCooldownImmune() const; + /** * Sets if this entity has GM immunity, making it not killable * @param value the GM immunity of this entity @@ -371,14 +377,6 @@ public: */ Entity* GetKiller() const; - /** - * Checks if the target ID is a valid enemy of this entity - * @param target the target ID to check for - * @param ignoreFactions whether or not check for the factions, e.g. just return true if the entity cannot be smashed - * @return if the target ID is a valid enemy - */ - bool CheckValidity(LWOOBJID target, bool ignoreFactions = false, bool targetEnemy = true, bool targetFriend = false) const; - /** * Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks. * @param damage the damage to attempt to apply @@ -414,15 +412,23 @@ public: ); // Getters for status immunities - const bool GetImmuneToBasicAttack() {return m_ImmuneToBasicAttackCount > 0;}; - const bool GetImmuneToDamageOverTime() {return m_ImmuneToDamageOverTimeCount > 0;}; - const bool GetImmuneToKnockback() {return m_ImmuneToKnockbackCount > 0;}; - const bool GetImmuneToInterrupt() {return m_ImmuneToInterruptCount > 0;}; - const bool GetImmuneToSpeed() {return m_ImmuneToSpeedCount > 0;}; - const bool GetImmuneToImaginationGain() {return m_ImmuneToImaginationGainCount > 0;}; - const bool GetImmuneToImaginationLoss() {return m_ImmuneToImaginationLossCount > 0;}; - const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;}; - const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;}; + const bool GetImmuneToBasicAttack() { return m_ImmuneToBasicAttackCount > 0; }; + const bool GetImmuneToDamageOverTime() { return m_ImmuneToDamageOverTimeCount > 0; }; + const bool GetImmuneToKnockback() { return m_ImmuneToKnockbackCount > 0; }; + const bool GetImmuneToInterrupt() { return m_ImmuneToInterruptCount > 0; }; + const bool GetImmuneToSpeed() { return m_ImmuneToSpeedCount > 0; }; + const bool GetImmuneToImaginationGain() { return m_ImmuneToImaginationGainCount > 0; }; + const bool GetImmuneToImaginationLoss() { return m_ImmuneToImaginationLossCount > 0; }; + const bool GetImmuneToQuickbuildInterrupt() { return m_ImmuneToQuickbuildInterruptCount > 0; }; + const bool GetImmuneToPullToPoint() { return m_ImmuneToPullToPointCount > 0; }; + + // Damage cooldown setters/getters + void SetDamageCooldownTimer(float value) { m_DamageCooldownTimer = value; } + float GetDamageCooldownTimer() { return m_DamageCooldownTimer; } + + // Death behavior setters/getters + void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; } + int32_t GetDeathBehavior() const { return m_DeathBehavior; } /** * Utility to reset all stats to the default stats based on items and completed missions @@ -605,6 +611,16 @@ private: uint32_t m_ImmuneToImaginationLossCount; uint32_t m_ImmuneToQuickbuildInterruptCount; uint32_t m_ImmuneToPullToPointCount; + + /** + * Death behavior type. If 0, the client plays a death animation as opposed to a smash animation. + */ + int32_t m_DeathBehavior; + + /** + * Damage immunity cooldown timer. Set to a value that then counts down to create a damage cooldown for players + */ + float m_DamageCooldownTimer; }; #endif // DESTROYABLECOMPONENT_H diff --git a/dGame/dComponents/DonationVendorComponent.cpp b/dGame/dComponents/DonationVendorComponent.cpp index f19ba9b7..6abc959a 100644 --- a/dGame/dComponents/DonationVendorComponent.cpp +++ b/dGame/dComponents/DonationVendorComponent.cpp @@ -22,10 +22,8 @@ DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorCompone return; } - std::unique_ptr query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;")); - query->setInt(1, m_ActivityId); - std::unique_ptr donation_total(query->executeQuery()); - if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total"); + auto donationTotal = Database::Get()->GetDonationTotal(m_ActivityId); + if (donationTotal) m_TotalDonated = donationTotal.value(); m_TotalRemaining = m_Goal - m_TotalDonated; m_PercentComplete = m_TotalDonated/static_cast(m_Goal); } @@ -38,8 +36,8 @@ void DonationVendorComponent::SubmitDonation(uint32_t count) { m_DirtyDonationVendor = true; } -void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - VendorComponent::Serialize(outBitStream, bIsInitialUpdate, flags); +void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + VendorComponent::Serialize(outBitStream, bIsInitialUpdate); outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor); if (bIsInitialUpdate || m_DirtyDonationVendor) { outBitStream->Write(m_PercentComplete); diff --git a/dGame/dComponents/DonationVendorComponent.h b/dGame/dComponents/DonationVendorComponent.h index 6c706bf9..7eb60849 100644 --- a/dGame/dComponents/DonationVendorComponent.h +++ b/dGame/dComponents/DonationVendorComponent.h @@ -8,9 +8,9 @@ class Entity; class DonationVendorComponent final : public VendorComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR; DonationVendorComponent(Entity* parent); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; uint32_t GetActivityID() {return m_ActivityId;}; void SubmitDonation(uint32_t count); diff --git a/dGame/dComponents/GhostComponent.cpp b/dGame/dComponents/GhostComponent.cpp new file mode 100644 index 00000000..db12ce4d --- /dev/null +++ b/dGame/dComponents/GhostComponent.cpp @@ -0,0 +1,57 @@ +#include "GhostComponent.h" + +GhostComponent::GhostComponent(Entity* parent) : Component(parent) { + m_GhostReferencePoint = NiPoint3Constant::ZERO; + m_GhostOverridePoint = NiPoint3Constant::ZERO; + m_GhostOverride = false; +} + +GhostComponent::~GhostComponent() { + for (auto& observedEntity : m_ObservedEntities) { + if (observedEntity == 0) continue; + + auto* entity = Game::entityManager->GetGhostCandidate(observedEntity); + if (!entity) continue; + + entity->SetObservers(entity->GetObservers() - 1); + } +} + +void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) { + m_GhostReferencePoint = value; +} + +void GhostComponent::SetGhostOverridePoint(const NiPoint3& value) { + m_GhostOverridePoint = value; +} + +void GhostComponent::AddLimboConstruction(LWOOBJID objectId) { + m_LimboConstructions.insert(objectId); +} + +void GhostComponent::RemoveLimboConstruction(LWOOBJID objectId) { + m_LimboConstructions.erase(objectId); +} + +void GhostComponent::ConstructLimboEntities() { + for (const auto& objectId : m_LimboConstructions) { + auto* entity = Game::entityManager->GetEntity(objectId); + if (!entity) continue; + + Game::entityManager->ConstructEntity(entity, m_Parent->GetSystemAddress()); + } + + m_LimboConstructions.clear(); +} + +void GhostComponent::ObserveEntity(int32_t id) { + m_ObservedEntities.insert(id); +} + +bool GhostComponent::IsObserved(int32_t id) { + return m_ObservedEntities.contains(id); +} + +void GhostComponent::GhostEntity(int32_t id) { + m_ObservedEntities.erase(id); +} diff --git a/dGame/dComponents/GhostComponent.h b/dGame/dComponents/GhostComponent.h new file mode 100644 index 00000000..bc4f158d --- /dev/null +++ b/dGame/dComponents/GhostComponent.h @@ -0,0 +1,54 @@ +#ifndef __GHOSTCOMPONENT__H__ +#define __GHOSTCOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" +#include + +class NiPoint3; + +class GhostComponent final : public Component { +public: + static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST; + GhostComponent(Entity* parent); + ~GhostComponent() override; + + void SetGhostOverride(bool value) { m_GhostOverride = value; }; + + const NiPoint3& GetGhostReferencePoint() const { return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; }; + + const NiPoint3& GetOriginGhostReferencePoint() const { return m_GhostReferencePoint; }; + + const NiPoint3& GetGhostOverridePoint() const { return m_GhostOverridePoint; }; + + bool GetGhostOverride() const { return m_GhostOverride; }; + + void SetGhostReferencePoint(const NiPoint3& value); + + void SetGhostOverridePoint(const NiPoint3& value); + + void AddLimboConstruction(const LWOOBJID objectId); + + void RemoveLimboConstruction(const LWOOBJID objectId); + + void ConstructLimboEntities(); + + void ObserveEntity(const int32_t id); + + bool IsObserved(const int32_t id); + + void GhostEntity(const int32_t id); + +private: + NiPoint3 m_GhostReferencePoint; + + NiPoint3 m_GhostOverridePoint; + + std::unordered_set m_ObservedEntities; + + std::unordered_set m_LimboConstructions; + + bool m_GhostOverride; +}; + +#endif //!__GHOSTCOMPONENT__H__ diff --git a/dGame/dComponents/VehiclePhysicsComponent.cpp b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp similarity index 67% rename from dGame/dComponents/VehiclePhysicsComponent.cpp rename to dGame/dComponents/HavokVehiclePhysicsComponent.cpp index 684b135b..8c8e14fe 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.cpp +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp @@ -1,11 +1,9 @@ -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "EntityManager.h" -VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(parent) { - m_Position = NiPoint3::ZERO; - m_Rotation = NiQuaternion::IDENTITY; - m_Velocity = NiPoint3::ZERO; - m_AngularVelocity = NiPoint3::ZERO; +HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) { + m_Velocity = NiPoint3Constant::ZERO; + m_AngularVelocity = NiPoint3Constant::ZERO; m_IsOnGround = true; m_IsOnRail = false; m_DirtyPosition = true; @@ -14,65 +12,45 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : Component(par m_EndBehavior = GeneralUtils::GenerateRandomNumber(0, 7); } -VehiclePhysicsComponent::~VehiclePhysicsComponent() { - -} - -void VehiclePhysicsComponent::SetPosition(const NiPoint3& pos) { - if (pos == m_Position) return; - m_DirtyPosition = true; - m_Position = pos; -} - -void VehiclePhysicsComponent::SetRotation(const NiQuaternion& rot) { - if (rot == m_Rotation) return; - m_DirtyPosition = true; - m_Rotation = rot; -} - -void VehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { +void HavokVehiclePhysicsComponent::SetVelocity(const NiPoint3& vel) { if (vel == m_Velocity) return; m_DirtyPosition = true; m_Velocity = vel; } -void VehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { +void HavokVehiclePhysicsComponent::SetAngularVelocity(const NiPoint3& vel) { if (vel == m_AngularVelocity) return; m_DirtyPosition = true; m_AngularVelocity = vel; } -void VehiclePhysicsComponent::SetIsOnGround(bool val) { +void HavokVehiclePhysicsComponent::SetIsOnGround(bool val) { if (val == m_IsOnGround) return; m_DirtyPosition = true; m_IsOnGround = val; } -void VehiclePhysicsComponent::SetIsOnRail(bool val) { +void HavokVehiclePhysicsComponent::SetIsOnRail(bool val) { if (val == m_IsOnRail) return; m_DirtyPosition = true; m_IsOnRail = val; } -void VehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { +void HavokVehiclePhysicsComponent::SetRemoteInputInfo(const RemoteInputInfo& remoteInputInfo) { if (m_RemoteInputInfo == remoteInputInfo) return; this->m_RemoteInputInfo = remoteInputInfo; m_DirtyRemoteInput = true; } -void VehiclePhysicsComponent::SetDirtyPosition(bool val) { - m_DirtyPosition = val; -} - -void VehiclePhysicsComponent::SetDirtyVelocity(bool val) { +void HavokVehiclePhysicsComponent::SetDirtyVelocity(bool val) { m_DirtyVelocity = val; } -void VehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) { +void HavokVehiclePhysicsComponent::SetDirtyAngularVelocity(bool val) { m_DirtyAngularVelocity = val; } -void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); if (bIsInitialUpdate || m_DirtyPosition) { @@ -133,7 +111,7 @@ void VehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI outBitStream->Write0(); } -void VehiclePhysicsComponent::Update(float deltaTime) { +void HavokVehiclePhysicsComponent::Update(float deltaTime) { if (m_SoftUpdate > 5) { Game::entityManager->SerializeEntity(m_Parent); m_SoftUpdate = 0; diff --git a/dGame/dComponents/VehiclePhysicsComponent.h b/dGame/dComponents/HavokVehiclePhysicsComponent.h similarity index 56% rename from dGame/dComponents/VehiclePhysicsComponent.h rename to dGame/dComponents/HavokVehiclePhysicsComponent.h index e314bef1..85a0e279 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -2,65 +2,23 @@ #include "BitStream.h" #include "Entity.h" -#include "Component.h" +#include "PhysicsComponent.h" #include "eReplicaComponentType.h" - -struct RemoteInputInfo { - void operator=(const RemoteInputInfo& other) { - m_RemoteInputX = other.m_RemoteInputX; - m_RemoteInputY = other.m_RemoteInputY; - m_IsPowersliding = other.m_IsPowersliding; - m_IsModified = other.m_IsModified; - } - - bool operator==(const RemoteInputInfo& other) { - return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified; - } - - float m_RemoteInputX; - float m_RemoteInputY; - bool m_IsPowersliding; - bool m_IsModified; -}; +#include "PositionUpdate.h" /** * Physics component for vehicles. */ -class VehiclePhysicsComponent : public Component { +class HavokVehiclePhysicsComponent : public PhysicsComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS; - VehiclePhysicsComponent(Entity* parentEntity); - ~VehiclePhysicsComponent() override; + HavokVehiclePhysicsComponent(Entity* parentEntity); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; - /** - * Sets the position - * @param pos the new position - */ - void SetPosition(const NiPoint3& pos); - - /** - * Gets the position - * @return the position - */ - const NiPoint3& GetPosition() const { return m_Position; } - - /** - * Sets the rotation - * @param rot the new rotation - */ - void SetRotation(const NiQuaternion& rot); - - /** - * Gets the rotation - * @return the rotation - */ - const NiQuaternion& GetRotation() const { return m_Rotation; } - /** * Sets the velocity * @param vel the new velocity @@ -115,10 +73,6 @@ public: void SetRemoteInputInfo(const RemoteInputInfo&); private: - bool m_DirtyPosition; - NiPoint3 m_Position; - NiQuaternion m_Rotation; - bool m_DirtyVelocity; NiPoint3 m_Velocity; diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 8689c0bc..591e0505 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -5,21 +5,20 @@ #include "Entity.h" #include "Item.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "CDClientManager.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "MissionComponent.h" #include "GameMessages.h" #include "SkillComponent.h" #include "Character.h" #include "EntityManager.h" #include "ItemSet.h" -#include "Player.h" #include "PetComponent.h" #include "PossessorComponent.h" #include "PossessableComponent.h" #include "ModuleAssemblyComponent.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "CharacterComponent.h" #include "dZoneManager.h" #include "PropertyManagementComponent.h" @@ -31,6 +30,7 @@ #include "eMissionTaskType.h" #include "eStateChangeType.h" #include "eUseItemResponse.h" +#include "Mail.h" #include "CDComponentsRegistryTable.h" #include "CDInventoryComponentTable.h" @@ -55,10 +55,10 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do return; } - auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); const auto componentId = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::INVENTORY); - auto* inventoryComponentTable = CDClientManager::Instance().GetTable(); + auto* inventoryComponentTable = CDClientManager::GetTable(); auto items = inventoryComponentTable->Query([=](const CDInventoryComponent entry) { return entry.id == componentId; }); auto slot = 0u; @@ -68,7 +68,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do continue; } - const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID(); + const LWOOBJID id = ObjectIDManager::GenerateObjectID(); const auto& info = Inventory::FindItemComponent(item.itemid); @@ -86,7 +86,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do const auto proxyLOT = static_cast(std::stoi(proxyLotAsString)); const auto& proxyInfo = Inventory::FindItemComponent(proxyLOT); - const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID(); + const LWOOBJID proxyId = ObjectIDManager::GenerateObjectID(); // Use item.count since we equip item.count number of the item this is a requested proxy of UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ }); @@ -175,14 +175,14 @@ void InventoryComponent::AddItem( const bool bound, int32_t preferredSlot) { if (count == 0) { - Game::logger->Log("InventoryComponent", "Attempted to add 0 of item (%i) to the inventory!", lot); + LOG("Attempted to add 0 of item (%i) to the inventory!", lot); return; } if (!Inventory::IsValidItem(lot)) { if (lot > 0) { - Game::logger->Log("InventoryComponent", "Attempted to add invalid item (%i) to the inventory!", lot); + LOG("Attempted to add invalid item (%i) to the inventory!", lot); } return; @@ -200,7 +200,7 @@ void InventoryComponent::AddItem( const auto slot = preferredSlot != -1 && inventory->IsSlotEmpty(preferredSlot) ? preferredSlot : inventory->FindEmptySlot(); if (slot == -1) { - Game::logger->Log("InventoryComponent", "Failed to find empty slot for inventory (%i)!", inventoryType); + LOG("Failed to find empty slot for inventory (%i)!", inventoryType); return; } @@ -264,17 +264,11 @@ void InventoryComponent::AddItem( } if (slot == -1) { - auto* player = dynamic_cast(GetParent()); - - if (player == nullptr) { - return; - } - outOfSpace += size; switch (sourceType) { case 0: - player->SendMail(LWOOBJID_EMPTY, "Darkflame Universe", "Lost Reward", "You received an item and didn't have room for it.", lot, size); + Mail::SendMail(LWOOBJID_EMPTY, "Darkflame Universe", m_Parent, "Lost Reward", "You received an item and didn't have room for it.", lot, size); break; case 1: @@ -300,38 +294,26 @@ void InventoryComponent::AddItem( } } -void InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound) { +bool InventoryComponent::RemoveItem(const LOT lot, const uint32_t count, eInventoryType inventoryType, const bool ignoreBound, const bool silent) { if (count == 0) { - Game::logger->Log("InventoryComponent", "Attempted to remove 0 of item (%i) from the inventory!", lot); - - return; + LOG("Attempted to remove 0 of item (%i) from the inventory!", lot); + return false; } - - if (inventoryType == INVALID) { - inventoryType = Inventory::FindInventoryTypeForLot(lot); - } - + if (inventoryType == INVALID) inventoryType = Inventory::FindInventoryTypeForLot(lot); auto* inventory = GetInventory(inventoryType); - - if (inventory == nullptr) { - return; - } + if (!inventory) return false; auto left = std::min(count, inventory->GetLotCount(lot)); + if (left != count) return false; while (left > 0) { auto* item = FindItemByLot(lot, inventoryType, false, ignoreBound); - - if (item == nullptr) { - break; - } - + if (!item) break; const auto delta = std::min(left, item->GetCount()); - - item->SetCount(item->GetCount() - delta); - + item->SetCount(item->GetCount() - delta, silent); left -= delta; } + return true; } void InventoryComponent::MoveItemToInventory(Item* item, const eInventoryType inventory, const uint32_t count, const bool showFlyingLot, bool isModMoveAndEquip, const bool ignoreEquipped, const int32_t preferredSlot) { @@ -496,7 +478,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); if (inventoryElement == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'inv' xml element!"); + LOG("Failed to find 'inv' xml element!"); return; } @@ -504,7 +486,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* bags = inventoryElement->FirstChildElement("bag"); if (bags == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!"); + LOG("Failed to find 'bags' xml element!"); return; } @@ -530,7 +512,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* items = inventoryElement->FirstChildElement("items"); if (items == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!"); + LOG("Failed to find 'items' xml element!"); return; } @@ -545,7 +527,7 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { auto* inventory = GetInventory(static_cast(type)); if (inventory == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find inventory (%i)!", type); + LOG("Failed to find inventory (%i)!", type); return; } @@ -618,7 +600,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); if (inventoryElement == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'inv' xml element!"); + LOG("Failed to find 'inv' xml element!"); return; } @@ -641,7 +623,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { auto* bags = inventoryElement->FirstChildElement("bag"); if (bags == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'bags' xml element!"); + LOG("Failed to find 'bags' xml element!"); return; } @@ -660,7 +642,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { auto* items = inventoryElement->FirstChildElement("items"); if (items == nullptr) { - Game::logger->Log("InventoryComponent", "Failed to find 'items' xml element!"); + LOG("Failed to find 'items' xml element!"); return; } @@ -712,7 +694,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { } } -void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate, unsigned& flags) { +void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate) { if (bIsInitialUpdate || m_Dirty) { outBitStream->Write(true); @@ -770,10 +752,6 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b outBitStream->Write(false); } -void InventoryComponent::ResetFlags() { - m_Dirty = false; -} - void InventoryComponent::Update(float deltaTime) { for (auto* set : m_Itemsets) { set->Update(deltaTime); @@ -931,30 +909,30 @@ void InventoryComponent::UnEquipItem(Item* item) { void InventoryComponent::EquipScripts(Item* equippedItem) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); if (!compRegistryTable) return; int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); if (scriptComponentID > -1) { - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); + CDScriptComponentTable* scriptCompTable = CDClientManager::GetTable(); CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); if (!itemScript) { - Game::logger->Log("InventoryComponent", "null script?"); + LOG("null script?"); } itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId()); } } void InventoryComponent::UnequipScripts(Item* unequippedItem) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); if (!compRegistryTable) return; int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1); if (scriptComponentID > -1) { - CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable(); + CDScriptComponentTable* scriptCompTable = CDClientManager::GetTable(); CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID); auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name); if (!itemScript) { - Game::logger->Log("InventoryComponent", "null script?"); + LOG("null script?"); } itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId()); } @@ -997,7 +975,7 @@ void InventoryComponent::HandlePossession(Item* item) { auto* mount = Game::entityManager->CreateEntity(info, nullptr, m_Parent); // Check to see if the mount is a vehicle, if so, flip it - auto* vehicleComponent = mount->GetComponent(); + auto* vehicleComponent = mount->GetComponent(); if (vehicleComponent) characterComponent->SetIsRacing(true); // Setup the destroyable stats @@ -1162,19 +1140,7 @@ void InventoryComponent::AddItemSkills(const LOT lot) { const auto skill = FindSkill(lot); - if (skill == 0) { - return; - } - - if (index != m_Skills.end()) { - const auto old = index->second; - - GameMessages::SendRemoveSkill(m_Parent, old); - } - - GameMessages::SendAddSkill(m_Parent, skill, static_cast(slot)); - - m_Skills.insert_or_assign(slot, skill); + SetSkill(slot, skill); } void InventoryComponent::RemoveItemSkills(const LOT lot) { @@ -1201,7 +1167,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) { if (slot == BehaviorSlot::Primary) { m_Skills.insert_or_assign(BehaviorSlot::Primary, 1); - GameMessages::SendAddSkill(m_Parent, 1, static_cast(BehaviorSlot::Primary)); + GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary); } } @@ -1256,7 +1222,7 @@ void InventoryComponent::SpawnPet(Item* item) { EntityInfo info{}; info.lot = item->GetLot(); info.pos = m_Parent->GetPosition(); - info.rot = NiQuaternion::IDENTITY; + info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = m_Parent->GetObjectID(); auto* pet = Game::entityManager->CreateEntity(info); @@ -1314,7 +1280,7 @@ bool InventoryComponent::IsTransferInventory(eInventoryType type) { } uint32_t InventoryComponent::FindSkill(const LOT lot) { - auto* table = CDClientManager::Instance().GetTable(); + auto* table = CDClientManager::GetTable(); const auto results = table->Query([=](const CDObjectSkills& entry) { return entry.objectTemplate == static_cast(lot); @@ -1332,8 +1298,8 @@ uint32_t InventoryComponent::FindSkill(const LOT lot) { std::vector InventoryComponent::FindBuffs(Item* item, bool castOnEquip) const { std::vector buffs; if (item == nullptr) return buffs; - auto* table = CDClientManager::Instance().GetTable(); - auto* behaviors = CDClientManager::Instance().GetTable(); + auto* table = CDClientManager::GetTable(); + auto* behaviors = CDClientManager::GetTable(); const auto results = table->Query([=](const CDObjectSkills& entry) { return entry.objectTemplate == static_cast(item->GetLot()); @@ -1346,7 +1312,7 @@ std::vector InventoryComponent::FindBuffs(Item* item, bool castOnEquip const auto entry = behaviors->GetSkillByID(result.skillID); if (entry.skillID == 0) { - Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID); + LOG("Failed to find buff behavior for skill (%i)!", result.skillID); continue; } @@ -1369,7 +1335,7 @@ void InventoryComponent::SetNPCItems(const std::vector& items) { auto slot = 0u; for (const auto& item : items) { - const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID(); + const LWOOBJID id = ObjectIDManager::GenerateObjectID(); const auto& info = Inventory::FindItemComponent(item); @@ -1413,7 +1379,7 @@ std::vector InventoryComponent::GenerateProxies(Item* parent) { try { lots.push_back(std::stoi(segment)); } catch (std::invalid_argument& exception) { - Game::logger->Log("InventoryComponent", "Failed to parse proxy (%s): (%s)!", segment.c_str(), exception.what()); + LOG("Failed to parse proxy (%s): (%s)!", segment.c_str(), exception.what()); } } @@ -1631,3 +1597,29 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) { petInventoryElement->LinkEndChild(petElement); } } + + +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; + else return false; + return SetSkill(behaviorSlot, 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()) { + const auto old = index->second; + GameMessages::SendRemoveSkill(m_Parent, old); + } + + GameMessages::SendAddSkill(m_Parent, skillId, slot); + m_Skills.insert_or_assign(slot, skillId); + return true; +} + diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index 801f9f51..e47e6a59 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -35,17 +35,15 @@ enum class eItemType : int32_t; * of different types, each type representing a different group of items, see `eInventoryType` for a list of * inventories. */ -class InventoryComponent : public Component -{ +class InventoryComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void LoadXml(tinyxml2::XMLDocument* document); void UpdateXml(tinyxml2::XMLDocument* document) override; - void ResetFlags(); /** * Returns an inventory of the specified type, if it exists @@ -119,8 +117,9 @@ public: * @param count the number of items to remove * @param inventoryType optional inventory type to remove the item from * @param ignoreBound ignores bound items + * @param silent silently remove the item */ - void RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false); + bool RemoveItem(LOT lot, uint32_t count, eInventoryType inventoryType = INVALID, bool ignoreBound = false, bool silent = false); /** * Moves an existing item to an inventory of the entity @@ -368,6 +367,11 @@ public: */ void UnequipScripts(Item* unequippedItem); + std::map GetSkills(){ return m_Skills; }; + + bool SetSkill(int slot, uint32_t skillId); + bool SetSkill(BehaviorSlot slot, uint32_t skillId); + ~InventoryComponent() override; private: diff --git a/dGame/dComponents/ItemComponent.cpp b/dGame/dComponents/ItemComponent.cpp new file mode 100644 index 00000000..dc413b17 --- /dev/null +++ b/dGame/dComponents/ItemComponent.cpp @@ -0,0 +1,5 @@ +#include "ItemComponent.h" + +void ItemComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { + outBitStream->Write0(); +} diff --git a/dGame/dComponents/ItemComponent.h b/dGame/dComponents/ItemComponent.h new file mode 100644 index 00000000..875ef0a5 --- /dev/null +++ b/dGame/dComponents/ItemComponent.h @@ -0,0 +1,16 @@ +#ifndef __ITEMCOMPONENT__H__ +#define __ITEMCOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" + +class ItemComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ITEM; + + ItemComponent(Entity* entity) : Component(entity) {} + + void Serialize(RakNet::BitStream* bitStream, bool isConstruction) override; +}; + +#endif //!__ITEMCOMPONENT__H__ diff --git a/dGame/dComponents/LUPExhibitComponent.cpp b/dGame/dComponents/LUPExhibitComponent.cpp index deb3cc8c..a312617d 100644 --- a/dGame/dComponents/LUPExhibitComponent.cpp +++ b/dGame/dComponents/LUPExhibitComponent.cpp @@ -1,44 +1,24 @@ #include "LUPExhibitComponent.h" - #include "EntityManager.h" -LUPExhibitComponent::LUPExhibitComponent(Entity* parent) : Component(parent) { - m_Exhibits = { 11121, 11295, 11423, 11979 }; - - m_ExhibitIndex = 0; - - m_Exhibit = m_Exhibits[m_ExhibitIndex]; - - -} - -LUPExhibitComponent::~LUPExhibitComponent() { - -} - void LUPExhibitComponent::Update(float deltaTime) { m_UpdateTimer += deltaTime; - if (m_UpdateTimer > 20.0f) { - NextExhibit(); - + NextLUPExhibit(); m_UpdateTimer = 0.0f; } } -void LUPExhibitComponent::NextExhibit() { - m_ExhibitIndex++; - - if (m_ExhibitIndex >= m_Exhibits.size()) { - m_ExhibitIndex = 0; - } - - m_Exhibit = m_Exhibits[m_ExhibitIndex]; - +void LUPExhibitComponent::NextLUPExhibit() { + m_LUPExhibitIndex++; + m_DirtyLUPExhibit = true; Game::entityManager->SerializeEntity(m_Parent); } -void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) { - outBitStream->Write1(); // Dirty flag? - outBitStream->Write(m_Exhibit); +void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + outBitStream->Write(m_DirtyLUPExhibit); + if (m_DirtyLUPExhibit) { + outBitStream->Write(m_LUPExhibits[m_LUPExhibitIndex % m_LUPExhibits.size()]); + if (!bIsInitialUpdate) m_DirtyLUPExhibit = false; + } } diff --git a/dGame/dComponents/LUPExhibitComponent.h b/dGame/dComponents/LUPExhibitComponent.h index 587d1b2f..e6653868 100644 --- a/dGame/dComponents/LUPExhibitComponent.h +++ b/dGame/dComponents/LUPExhibitComponent.h @@ -3,43 +3,26 @@ #include "Component.h" #include "Entity.h" #include "eReplicaComponentType.h" +#include +#include +#include "dCommonVars.h" /** * Component that handles the LOT that is shown in the LUP exhibit in the LUP world. Works by setting a timer and * switching the LOTs around that we'd like to display. */ -class LUPExhibitComponent : public Component +class LUPExhibitComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LUP_EXHIBIT; - LUPExhibitComponent(Entity* parent); - ~LUPExhibitComponent(); + LUPExhibitComponent(Entity* parent) : Component(parent) {}; void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags); - - /** - * After the timer runs out, this changes the currently exhibited LOT to the next one - */ - void NextExhibit(); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + void NextLUPExhibit(); private: - /** - * The LOT that's currently on exhibit - */ - LOT m_Exhibit; - - /** - * The time since we've last updated the exhibit - */ - float m_UpdateTimer; - - /** - * The list of possible exhibits to show - */ - std::vector m_Exhibits; - - /** - * The current index in the exhibit list - */ - size_t m_ExhibitIndex; + float m_UpdateTimer = 0.0f; + std::array m_LUPExhibits = { 11121, 11295, 11423, 11979 }; + uint8_t m_LUPExhibitIndex = 0; + bool m_DirtyLUPExhibit = true; }; diff --git a/dGame/dComponents/LevelProgressionComponent.cpp b/dGame/dComponents/LevelProgressionComponent.cpp index 8163e736..d271f97b 100644 --- a/dGame/dComponents/LevelProgressionComponent.cpp +++ b/dGame/dComponents/LevelProgressionComponent.cpp @@ -16,7 +16,7 @@ LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); if (!level) { - Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while updating XML!"); + LOG("Failed to find lvl tag while updating XML!"); return; } level->SetAttribute("l", m_Level); @@ -27,7 +27,7 @@ void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); if (!level) { - Game::logger->Log("LevelProgressionComponent", "Failed to find lvl tag while loading XML!"); + LOG("Failed to find lvl tag while loading XML!"); return; } level->QueryAttribute("l", &m_Level); @@ -37,14 +37,14 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { m_CharacterVersion = static_cast(characterVersion); } -void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo); if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level); m_DirtyLevelInfo = false; } void LevelProgressionComponent::HandleLevelUp() { - auto* rewardsTable = CDClientManager::Instance().GetTable(); + auto* rewardsTable = CDClientManager::GetTable(); const auto& rewards = rewardsTable->GetByLevelID(m_Level); bool rewardingItem = rewards.size() > 0; @@ -56,19 +56,19 @@ void LevelProgressionComponent::HandleLevelUp() { // Tell the client we beginning to send level rewards. if (rewardingItem) GameMessages::NotifyLevelRewards(m_Parent->GetObjectID(), m_Parent->GetSystemAddress(), m_Level, rewardingItem); - for (auto* reward : rewards) { - switch (reward->rewardType) { + for (const auto& reward : rewards) { + switch (reward.rewardType) { case 0: - inventoryComponent->AddItem(reward->value, reward->count, eLootSourceType::LEVEL_REWARD); + inventoryComponent->AddItem(reward.value, reward.count, eLootSourceType::LEVEL_REWARD); break; case 4: { auto* items = inventoryComponent->GetInventory(eInventoryType::ITEMS); - items->SetSize(items->GetSize() + reward->value); + items->SetSize(items->GetSize() + reward.value); } break; case 9: - SetSpeedBase(static_cast(reward->value) ); + SetSpeedBase(static_cast(reward.value) ); controllablePhysicsComponent->SetSpeedMultiplier(GetSpeedBase() / 500.0f); break; case 11: diff --git a/dGame/dComponents/LevelProgressionComponent.h b/dGame/dComponents/LevelProgressionComponent.h index 06908a81..6083738c 100644 --- a/dGame/dComponents/LevelProgressionComponent.h +++ b/dGame/dComponents/LevelProgressionComponent.h @@ -11,9 +11,9 @@ * */ -class LevelProgressionComponent : public Component { +class LevelProgressionComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::LEVEL_PROGRESSION; /** * Constructor for this component @@ -21,7 +21,7 @@ public: */ LevelProgressionComponent(Entity* parent); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Save data from this componennt to character XML diff --git a/dGame/dComponents/MiniGameControlComponent.cpp b/dGame/dComponents/MiniGameControlComponent.cpp new file mode 100644 index 00000000..fdd56a2c --- /dev/null +++ b/dGame/dComponents/MiniGameControlComponent.cpp @@ -0,0 +1,5 @@ +#include "MiniGameControlComponent.h" + +void MiniGameControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { + outBitStream->Write(0x40000000); +} diff --git a/dGame/dComponents/MiniGameControlComponent.h b/dGame/dComponents/MiniGameControlComponent.h new file mode 100644 index 00000000..e2581b2d --- /dev/null +++ b/dGame/dComponents/MiniGameControlComponent.h @@ -0,0 +1,15 @@ +#ifndef __MINIGAMECONTROLCOMPONENT__H__ +#define __MINIGAMECONTROLCOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" + +class MiniGameControlComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MINI_GAME_CONTROL; + + MiniGameControlComponent(Entity* parent) : Component(parent) {} + void Serialize(RakNet::BitStream* outBitStream, bool isConstruction); +}; + +#endif //!__MINIGAMECONTROLCOMPONENT__H__ diff --git a/dGame/dComponents/MinigameComponent.cpp b/dGame/dComponents/MinigameComponent.cpp new file mode 100644 index 00000000..6bcb985e --- /dev/null +++ b/dGame/dComponents/MinigameComponent.cpp @@ -0,0 +1,5 @@ +#include "MinigameComponent.h" + +void MinigameComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { + outBitStream->Write(0x40000000); +} diff --git a/dGame/dComponents/MissionComponent.cpp b/dGame/dComponents/MissionComponent.cpp index 40411902..151fcf2f 100644 --- a/dGame/dComponents/MissionComponent.cpp +++ b/dGame/dComponents/MissionComponent.cpp @@ -7,7 +7,7 @@ #include #include "MissionComponent.h" -#include "dLogger.h" +#include "Logger.h" #include "CDClientManager.h" #include "CDMissionTasksTable.h" #include "InventoryComponent.h" @@ -145,8 +145,13 @@ void MissionComponent::RemoveMission(uint32_t missionId) { } void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID associate, const std::string& targets, int32_t count, bool ignoreAchievements) { - for (const auto& pair : m_Missions) { - auto* mission = pair.second; + std::vector acceptedAchievements; + if (count > 0 && !ignoreAchievements) { + acceptedAchievements = LookForAchievements(type, value, true, associate, targets, count); + } + + for (const auto& [id, mission] : m_Missions) { + if (!mission || std::find(acceptedAchievements.begin(), acceptedAchievements.end(), mission->GetMissionId()) != acceptedAchievements.end()) continue; if (mission->IsAchievement() && ignoreAchievements) continue; @@ -154,10 +159,6 @@ void MissionComponent::Progress(eMissionTaskType type, int32_t value, LWOOBJID a mission->Progress(type, value, associate, targets, count); } - - if (count > 0 && !ignoreAchievements) { - LookForAchievements(type, value, true, associate, targets, count); - } } void MissionComponent::ForceProgress(const uint32_t missionId, const uint32_t taskId, const int32_t value, const bool acceptMission) { @@ -265,7 +266,7 @@ void MissionComponent::ForceProgressValue(uint32_t missionId, uint32_t taskType, } bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) { - auto* missionsTable = CDClientManager::Instance().GetTable(); + auto* missionsTable = CDClientManager::GetTable(); const auto missions = missionsTable->Query([=](const CDMissions& entry) { return entry.id == static_cast(missionId); @@ -282,12 +283,12 @@ bool MissionComponent::GetMissionInfo(uint32_t missionId, CDMissions& result) { #define MISSION_NEW_METHOD -bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) { +const std::vector MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, bool progress, LWOOBJID associate, const std::string& targets, int32_t count) { #ifdef MISSION_NEW_METHOD // Query for achievments, using the cache const auto& result = QueryAchievements(type, value, targets); - bool any = false; + std::vector acceptedAchievements; for (const uint32_t missionID : result) { // Check if we already have this achievement @@ -309,7 +310,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, instance->Accept(); - any = true; + acceptedAchievements.push_back(missionID); if (progress) { // Progress mission to bring it up to speed @@ -317,16 +318,16 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, } } - return any; + return acceptedAchievements; #else - auto* missionTasksTable = CDClientManager::Instance().GetTable(); - auto* missionsTable = CDClientManager::Instance().GetTable(); + auto* missionTasksTable = CDClientManager::GetTable(); + auto* missionsTable = CDClientManager::GetTable(); auto tasks = missionTasksTable->Query([=](const CDMissionTasks& entry) { return entry.taskType == static_cast(type); }); - auto any = false; + std::vector acceptedAchievements; for (const auto& task : tasks) { if (GetMission(task.id) != nullptr) { @@ -363,7 +364,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, break; } } catch (std::invalid_argument& exception) { - Game::logger->Log("MissionComponent", "Failed to parse target (%s): (%s)!", token.c_str(), exception.what()); + LOG("Failed to parse target (%s): (%s)!", token.c_str(), exception.what()); } } @@ -380,14 +381,14 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, instance->Accept(); - any = true; + acceptedAchievements.push_back(mission.id); if (progress) { instance->Progress(type, value, associate, targets, count); } } - return any; + return acceptedAchievements; #endif } @@ -406,8 +407,8 @@ const std::vector& MissionComponent::QueryAchievements(eMissionTaskTyp } // Find relevent tables - auto* missionTasksTable = CDClientManager::Instance().GetTable(); - auto* missionsTable = CDClientManager::Instance().GetTable(); + auto* missionTasksTable = CDClientManager::GetTable(); + auto* missionsTable = CDClientManager::GetTable(); std::vector result; @@ -457,7 +458,7 @@ const std::vector& MissionComponent::QueryAchievements(eMissionTaskTyp bool MissionComponent::RequiresItem(const LOT lot) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT type FROM Objects WHERE id = ?;"); - query.bind(1, (int)lot); + query.bind(1, static_cast(lot)); auto result = query.execQuery(); @@ -499,7 +500,7 @@ bool MissionComponent::RequiresItem(const LOT lot) { const auto required = LookForAchievements(eMissionTaskType::GATHER, lot, false); - return required; + return !required.empty(); } @@ -620,3 +621,12 @@ bool MissionComponent::HasCollectible(int32_t collectibleID) { bool MissionComponent::HasMission(uint32_t missionId) { return GetMission(missionId) != nullptr; } + +void MissionComponent::ResetMission(const int32_t missionId) { + auto* mission = GetMission(missionId); + + if (!mission) return; + + m_Missions.erase(missionId); + GameMessages::SendResetMissions(m_Parent, m_Parent->GetSystemAddress(), missionId); +} diff --git a/dGame/dComponents/MissionComponent.h b/dGame/dComponents/MissionComponent.h index eeaaa726..42c4df08 100644 --- a/dGame/dComponents/MissionComponent.h +++ b/dGame/dComponents/MissionComponent.h @@ -24,10 +24,9 @@ class AchievementCacheKey; * The mission inventory of an entity. Tracks mission state for each mission that can be accepted and allows for * progression of each of the mission task types (see eMissionTaskType). */ -class MissionComponent : public Component -{ +class MissionComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION; explicit MissionComponent(Entity* parent); ~MissionComponent() override; @@ -141,7 +140,7 @@ public: * @param count the number of values to progress by (differs by task type) * @return true if a achievement was accepted, false otherwise */ - bool LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1); + const std::vector LookForAchievements(eMissionTaskType type, int32_t value, bool progress = true, LWOOBJID associate = LWOOBJID_EMPTY, const std::string& targets = "", int32_t count = 1); /** * Checks if there's a mission active that requires the collection of the specified LOT @@ -170,6 +169,7 @@ public: */ bool HasMission(uint32_t missionId); + void ResetMission(const int32_t missionId); private: /** * All the missions owned by this entity, mapped by mission ID diff --git a/dGame/dComponents/MissionOfferComponent.cpp b/dGame/dComponents/MissionOfferComponent.cpp index e4c94ebd..7f26ed72 100644 --- a/dGame/dComponents/MissionOfferComponent.cpp +++ b/dGame/dComponents/MissionOfferComponent.cpp @@ -11,7 +11,7 @@ #include "GameMessages.h" #include "Entity.h" #include "MissionComponent.h" -#include "dLogger.h" +#include "Logger.h" #include "Game.h" #include "MissionPrerequisites.h" #include "eMissionState.h" @@ -29,18 +29,18 @@ uint32_t OfferedMission::GetMissionId() const { return this->missionId; } -bool OfferedMission::GetOfferMission() const { +bool OfferedMission::GetOffersMission() const { return this->offersMission; } -bool OfferedMission::GetAcceptMission() const { +bool OfferedMission::GetAcceptsMission() const { return this->acceptsMission; } //------------------------ MissionOfferComponent below ------------------------ MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot) : Component(parent) { - auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); auto value = compRegistryTable->GetByIDAndType(parentLot, eReplicaComponentType::MISSION_OFFER, -1); @@ -48,61 +48,46 @@ MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot const uint32_t componentId = value; // Now lookup the missions in the MissionNPCComponent table - auto* missionNpcComponentTable = CDClientManager::Instance().GetTable(); + auto* missionNpcComponentTable = CDClientManager::GetTable(); auto missions = missionNpcComponentTable->Query([=](const CDMissionNPCComponent& entry) { return entry.id == static_cast(componentId); }); for (auto& mission : missions) { - auto* offeredMission = new OfferedMission(mission.missionID, mission.offersMission, mission.acceptsMission); - this->offeredMissions.push_back(offeredMission); + this->offeredMissions.emplace_back(mission.missionID, mission.offersMission, mission.acceptsMission); } } } - -MissionOfferComponent::~MissionOfferComponent() { - for (auto* mission : this->offeredMissions) { - if (mission) { - delete mission; - mission = nullptr; - } - } - - offeredMissions.clear(); -} - void MissionOfferComponent::OnUse(Entity* originator) { OfferMissions(originator); } void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifiedMissionId) { // First, get the entity's MissionComponent. If there is not one, then we cannot offer missions to this entity. - auto* missionComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); + auto* missionComponent = entity->GetComponent(); if (!missionComponent) { - Game::logger->Log("MissionOfferComponent", "Unable to get mission component for Entity %llu", entity->GetObjectID()); + LOG("Unable to get mission component for Entity %llu", entity->GetObjectID()); return; } - std::vector offered{}; - CDMissions info{}; if (specifiedMissionId > 0 && !Mission::IsValidMission(specifiedMissionId, info)) { return; } - for (auto* offeredMission : this->offeredMissions) { + for (const auto offeredMission : this->offeredMissions) { if (specifiedMissionId > 0) { - if (offeredMission->GetMissionId() != specifiedMissionId && !info.isRandom) { + if (offeredMission.GetMissionId() != specifiedMissionId && !info.isRandom) { continue; } } // First, check if we already have the mission - const auto missionId = offeredMission->GetMissionId(); + const auto missionId = offeredMission.GetMissionId(); auto* mission = missionComponent->GetMission(missionId); @@ -118,8 +103,6 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi if (mission->IsActive() || mission->IsReadyToComplete()) { GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID()); - offered.push_back(missionId); - continue; } } @@ -127,20 +110,13 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi const auto canAccept = MissionPrerequisites::CanAccept(missionId, missionComponent->GetMissions()); // Mission has not yet been accepted - check the prereqs - if (!canAccept) - continue; - - if (!Mission::IsValidMission(missionId, info)) { - continue; - } + if (!canAccept || !Mission::IsValidMission(missionId, info)) continue; const auto& randomPool = info.randomPool; const auto isRandom = info.isRandom; - if (isRandom && randomPool.empty()) // This means the mission is part of a random pool of missions. - { - continue; - } + // This means the mission is part of a random pool of missions. + if (isRandom && randomPool.empty()) continue; if (isRandom && !randomPool.empty()) { std::istringstream stream(randomPool); @@ -154,7 +130,7 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi randomMissionPool.push_back(value); } catch (std::invalid_argument& exception) { - Game::logger->Log("MissionOfferComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); + LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); } } @@ -180,9 +156,7 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi sample == specifiedMissionId) { mission = missionComponent->GetMission(sample); - if (mission == nullptr || mission->IsAchievement()) { - continue; - } + if (mission == nullptr || mission->IsAchievement()) continue; GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), sample, m_Parent->GetObjectID()); @@ -191,19 +165,18 @@ void MissionOfferComponent::OfferMissions(Entity* entity, const uint32_t specifi break; } - if (std::find(offered.begin(), offered.end(), sample) == offered.end() && MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) { + if (MissionPrerequisites::CanAccept(sample, missionComponent->GetMissions())) { canAcceptPool.push_back(sample); } } // If the mission is already active or we already completed one of them today - if (canAcceptPool.empty()) - continue; + if (canAcceptPool.empty()) continue; const auto selected = canAcceptPool[GeneralUtils::GenerateRandomNumber(0, canAcceptPool.size() - 1)]; GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), selected, m_Parent->GetObjectID()); - } else if (std::find(offered.begin(), offered.end(), missionId) == offered.end() && offeredMission->GetOfferMission()) { + } else if (offeredMission.GetOffersMission()) { GameMessages::SendOfferMission(entity->GetObjectID(), entity->GetSystemAddress(), missionId, m_Parent->GetObjectID()); } } diff --git a/dGame/dComponents/MissionOfferComponent.h b/dGame/dComponents/MissionOfferComponent.h index 6e22ca05..d842a92e 100644 --- a/dGame/dComponents/MissionOfferComponent.h +++ b/dGame/dComponents/MissionOfferComponent.h @@ -30,13 +30,13 @@ struct OfferedMission { * Returns if this mission is offered by the entity * @return true if this mission is offered by the entity, false otherwise */ - bool GetOfferMission() const; + bool GetOffersMission() const; /** * Returns if this mission may be accepted by the entity (currently unused) * @return true if this mission may be accepted by the entity, false otherwise */ - bool GetAcceptMission() const; + bool GetAcceptsMission() const; private: @@ -59,12 +59,11 @@ private: /** * Allows entities to offer missions to other entities, depending on their mission inventory progression. */ -class MissionOfferComponent : public Component { +class MissionOfferComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MISSION_OFFER; MissionOfferComponent(Entity* parent, LOT parentLot); - ~MissionOfferComponent() override; /** * Handles the OnUse event triggered by some entity, determines which missions to show based on what they may @@ -85,7 +84,7 @@ private: /** * The missions this entity has to offer */ - std::vector offeredMissions; + std::vector offeredMissions; }; #endif // MISSIONOFFERCOMPONENT_H diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index 74f614d1..3f8858c0 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -1,6 +1,12 @@ #include "ModelComponent.h" #include "Entity.h" +#include "Game.h" +#include "Logger.h" + +#include "BehaviorStates.h" +#include "ControlBehaviorMsgs.h" + ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_OriginalPosition = m_Parent->GetDefaultPosition(); m_OriginalRotation = m_Parent->GetDefaultRotation(); @@ -8,7 +14,7 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_userModelID = m_Parent->GetVarAs(u"userModelID"); } -void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { // ItemComponent Serialization. Pets do not get this serialization. if (!m_Parent->HasComponent(eReplicaComponentType::PET)) { outBitStream->Write1(); @@ -29,3 +35,40 @@ void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialU outBitStream->Write1(); // Is this model paused if (bIsInitialUpdate) outBitStream->Write0(); // We are not writing model editing info } + +void ModelComponent::UpdatePendingBehaviorId(const int32_t newId) { + for (auto& behavior : m_Behaviors) if (behavior.GetBehaviorId() == -1) behavior.SetBehaviorId(newId); +} + +void ModelComponent::SendBehaviorListToClient(AMFArrayValue& args) const { + args.Insert("objectID", std::to_string(m_Parent->GetObjectID())); + + auto* behaviorArray = args.InsertArray("behaviors"); + for (auto& behavior : m_Behaviors) { + auto* behaviorArgs = behaviorArray->PushArray(); + behavior.SendBehaviorListToClient(*behaviorArgs); + } +} + +void ModelComponent::VerifyBehaviors() { + for (auto& behavior : m_Behaviors) behavior.VerifyLastEditedState(); +} + +void ModelComponent::SendBehaviorBlocksToClient(int32_t behaviorToSend, AMFArrayValue& args) const { + args.Insert("BehaviorID", std::to_string(behaviorToSend)); + args.Insert("objectID", std::to_string(m_Parent->GetObjectID())); + for (auto& behavior : m_Behaviors) if (behavior.GetBehaviorId() == behaviorToSend) behavior.SendBehaviorBlocksToClient(args); +} + +void ModelComponent::AddBehavior(AddMessage& msg) { + // Can only have 1 of the loot behaviors + for (auto& behavior : m_Behaviors) if (behavior.GetBehaviorId() == msg.GetBehaviorId()) return; + m_Behaviors.insert(m_Behaviors.begin() + msg.GetBehaviorIndex(), PropertyBehavior()); + m_Behaviors.at(msg.GetBehaviorIndex()).HandleMsg(msg); +} + +void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) { + if (msg.GetBehaviorIndex() >= m_Behaviors.size() || m_Behaviors.at(msg.GetBehaviorIndex()).GetBehaviorId() != msg.GetBehaviorId()) return; + m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex()); + // TODO move to the inventory +} diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index b5224869..0d720d04 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -1,4 +1,7 @@ #pragma once + +#include + #include "dCommonVars.h" #include "RakNetTypes.h" #include "NiPoint3.h" @@ -6,18 +9,26 @@ #include "Component.h" #include "eReplicaComponentType.h" +#include "Action.h" +#include "PropertyBehavior.h" +#include "StripUiPosition.h" + +class AddMessage; +class AMFArrayValue; +class BehaviorMessageBase; class Entity; +class MoveToInventoryMessage; /** * Component that represents entities that are a model, e.g. collectible models and BBB models. */ -class ModelComponent : public Component { +class ModelComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MODEL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODEL; ModelComponent(Entity* parent); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Returns the original position of the model @@ -43,7 +54,68 @@ public: */ void SetRotation(const NiQuaternion& rot) { m_OriginalRotation = rot; } + /** + * Main gateway for all behavior messages to be passed to their respective behaviors. + * + * @tparam Msg The message type to pass + * @param args the arguments of the message to be deserialized + */ + template + void HandleControlBehaviorsMsg(AMFArrayValue* args) { + static_assert(std::is_base_of_v, "Msg must be a BehaviorMessageBase"); + Msg msg(args); + for (auto& behavior : m_Behaviors) { + if (behavior.GetBehaviorId() == msg.GetBehaviorId()) { + behavior.HandleMsg(msg); + return; + } + } + + // If we somehow added more than 5 behaviors, resize to 5. + if (m_Behaviors.size() > 5) m_Behaviors.resize(5); + + // Do not allow more than 5 to be added. The client UI will break if you do! + if (m_Behaviors.size() == 5) return; + + auto newBehavior = m_Behaviors.insert(m_Behaviors.begin(), PropertyBehavior()); + // Generally if we are inserting a new behavior, it is because the client is creating a new behavior. + // However if we are testing behaviors the behavior will not exist on the initial pass, so we set the ID here to that of the msg. + // This will either set the ID to -1 (no change in the current default) or set the ID to the ID of the behavior we are testing. + newBehavior->SetBehaviorId(msg.GetBehaviorId()); + newBehavior->HandleMsg(msg); + }; + + void AddBehavior(AddMessage& msg); + + void MoveToInventory(MoveToInventoryMessage& msg); + + // Updates the pending behavior ID to the new ID. + void UpdatePendingBehaviorId(const int32_t newId); + + // Sends the behavior list to the client. + + /** + * The behaviors AMFArray will have up to 5 elements in the dense portion. + * Each element in the dense portion will be made up of another AMFArray + * with the following information mapped in the associative portion + * "id": Behavior ID cast to an AMFString + * "isLocked": AMFTrue or AMFFalse of whether or not the behavior is locked + * "isLoot": AMFTrue or AMFFalse of whether or not the behavior is a custom behavior (true if custom) + * "name": The name of the behavior formatted as an AMFString + */ + void SendBehaviorListToClient(AMFArrayValue& args) const; + + void SendBehaviorBlocksToClient(int32_t behaviorToSend, AMFArrayValue& args) const; + + void VerifyBehaviors(); + private: + /** + * The behaviors of the model + * Note: This is a vector because the order of the behaviors matters when serializing to the client. + * Note: No two PropertyBehaviors should have the same behavior ID. + */ + std::vector m_Behaviors; /** * The original position of the model diff --git a/dGame/dComponents/ModuleAssemblyComponent.cpp b/dGame/dComponents/ModuleAssemblyComponent.cpp index 8e197f0c..5b7042df 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.cpp +++ b/dGame/dComponents/ModuleAssemblyComponent.cpp @@ -46,7 +46,7 @@ const std::u16string& ModuleAssemblyComponent::GetAssemblyPartsLOTs() const { return m_AssemblyPartsLOTs; } -void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) { outBitStream->Write1(); @@ -57,7 +57,7 @@ void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bI outBitStream->Write(m_UseOptionalParts); - outBitStream->Write(static_cast(m_AssemblyPartsLOTs.size())); + outBitStream->Write(m_AssemblyPartsLOTs.size()); for (char16_t character : m_AssemblyPartsLOTs) { outBitStream->Write(character); } diff --git a/dGame/dComponents/ModuleAssemblyComponent.h b/dGame/dComponents/ModuleAssemblyComponent.h index 39670c9a..47e7baa6 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.h +++ b/dGame/dComponents/ModuleAssemblyComponent.h @@ -10,14 +10,14 @@ * same as having said items in your inventory (the subkey for this component) this component is the one that * renders the entity into the world. */ -class ModuleAssemblyComponent : public Component { +class ModuleAssemblyComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MODULE_ASSEMBLY; ModuleAssemblyComponent(Entity* parent); ~ModuleAssemblyComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; /** diff --git a/dGame/dComponents/MovementAIComponent.cpp b/dGame/dComponents/MovementAIComponent.cpp index 1966638d..8377031a 100644 --- a/dGame/dComponents/MovementAIComponent.cpp +++ b/dGame/dComponents/MovementAIComponent.cpp @@ -16,6 +16,8 @@ #include "CDComponentsRegistryTable.h" #include "CDPhysicsComponentTable.h" +#include "dNavMesh.h" + namespace { /** * Cache of all lots and their respective speeds @@ -41,7 +43,7 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : m_NextWaypoint = m_Parent->GetPosition(); m_Acceleration = 0.4f; m_PullingToPoint = false; - m_PullPoint = NiPoint3::ZERO; + m_PullPoint = NiPoint3Constant::ZERO; m_HaltDistance = 0; m_TimeToTravel = 0; m_TimeTravelled = 0; @@ -86,7 +88,7 @@ void MovementAIComponent::Update(const float deltaTime) { SetPosition(source); - NiPoint3 velocity = NiPoint3::ZERO; + NiPoint3 velocity = NiPoint3Constant::ZERO; if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek? { @@ -169,8 +171,8 @@ NiPoint3 MovementAIComponent::ApproximateLocation() const { auto approximation = source + ((destination - source) * percentageToWaypoint); - if (dpWorld::Instance().IsLoaded()) { - approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation); + if (dpWorld::IsLoaded()) { + approximation.y = dpWorld::GetNavMesh()->GetHeightAtPoint(approximation); } return approximation; @@ -181,8 +183,8 @@ bool MovementAIComponent::Warp(const NiPoint3& point) { NiPoint3 destination = point; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(point); if (std::abs(destination.y - point.y) > 3) { return false; @@ -201,7 +203,7 @@ void MovementAIComponent::Stop() { SetPosition(ApproximateLocation()); - SetVelocity(NiPoint3::ZERO); + SetVelocity(NiPoint3Constant::ZERO); m_TimeToTravel = 0; m_TimeTravelled = 0; @@ -242,8 +244,8 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) { return it->second; } - CDComponentsRegistryTable* componentRegistryTable = CDClientManager::Instance().GetTable(); - CDPhysicsComponentTable* physicsComponentTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* componentRegistryTable = CDClientManager::GetTable(); + CDPhysicsComponentTable* physicsComponentTable = CDClientManager::GetTable(); int32_t componentID; CDPhysicsComponent* physicsComponent = nullptr; @@ -302,8 +304,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { } std::vector computedPath; - if (dpWorld::Instance().IsLoaded()) { - computedPath = dpWorld::Instance().GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); + if (dpWorld::IsLoaded()) { + computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); } // Somehow failed @@ -328,8 +330,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) { // Simply path for (auto& point : computedPath) { - if (dpWorld::Instance().IsLoaded()) { - point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point); + if (dpWorld::IsLoaded()) { + point.y = dpWorld::GetNavMesh()->GetHeightAtPoint(point); } m_InterpolatedWaypoints.push_back(point); diff --git a/dGame/dComponents/MovementAIComponent.h b/dGame/dComponents/MovementAIComponent.h index 4a4e4c0a..852f7001 100644 --- a/dGame/dComponents/MovementAIComponent.h +++ b/dGame/dComponents/MovementAIComponent.h @@ -11,7 +11,7 @@ #include "GameMessages.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "Component.h" #include "eReplicaComponentType.h" #include @@ -55,9 +55,9 @@ struct MovementAIInfo { * Component that handles the movement settings of an entity. Not to be confused with the BaseCombatAI component that * actually handles attackig and following enemy entities. */ -class MovementAIComponent : public Component { +class MovementAIComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI; MovementAIComponent(Entity* parentEntity, MovementAIInfo info); diff --git a/dGame/dComponents/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index 01680bef..ae0e616c 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -8,7 +8,7 @@ #include "GeneralUtils.h" #include "dZoneManager.h" #include "EntityManager.h" -#include "dLogger.h" +#include "Logger.h" #include "GameMessages.h" #include "CppScripts.h" #include "SimplePhysicsComponent.h" @@ -133,9 +133,9 @@ void PlatformSubComponent::AdvanceToNextReverseWaypoint() { void PlatformSubComponent::SetupPath(const std::string& pathName, uint32_t startingWaypointIndex, bool startsInReverse) { m_Path = Game::zoneManager->GetZone()->GetPath(pathName); - Game::logger->Log("MovingPlatformComponent", "setting up path %s", pathName.c_str()); + LOG("setting up path %s", pathName.c_str()); if (!m_Path) { - Game::logger->Log("MovingPlatformComponent", "Failed to find path (%s)", pathName.c_str()); + LOG("Failed to find path (%s)", pathName.c_str()); return; } m_InReverse = startsInReverse; @@ -366,7 +366,7 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI outBitStream->Write0(); // No more platforms to write } -void MovingPlatformComponent::OnRebuildInitilized() { +void MovingPlatformComponent::OnQuickBuildInitilized() { StopPathing(); } diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index fb075622..5a5b7b7e 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -158,9 +158,9 @@ public: * don't at all do what you expect them to as we don't instruct the client of changes made here. * ^^^ Trivia: This made the red blocks platform and property platforms a pain to implement. */ -class MovingPlatformComponent : public Component { +class MovingPlatformComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MOVING_PLATFORM; MovingPlatformComponent(Entity* parent, const std::string& pathName); @@ -168,17 +168,17 @@ public: void LoadConfigData(); void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Stops all pathing, called when an entity starts a quick build associated with this platform */ - void OnRebuildInitilized(); + void OnQuickBuildInitilized(); /** * Starts the pathing, called when an entity completed a quick build associated with this platform */ - void OnCompleteRebuild(); + void OnCompleteQuickBuild(); /** * Updates the movement state for the moving platform diff --git a/dGame/dComponents/RocketLaunchLupComponent.cpp b/dGame/dComponents/MultiZoneEntranceComponent.cpp similarity index 69% rename from dGame/dComponents/RocketLaunchLupComponent.cpp rename to dGame/dComponents/MultiZoneEntranceComponent.cpp index 3c326540..7c2e2b79 100644 --- a/dGame/dComponents/RocketLaunchLupComponent.cpp +++ b/dGame/dComponents/MultiZoneEntranceComponent.cpp @@ -1,9 +1,9 @@ -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "RocketLaunchpadControlComponent.h" #include "InventoryComponent.h" #include "CharacterComponent.h" -RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) { +MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) { m_Parent = parent; std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar(u"MultiZoneIDs")); std::stringstream ss(zoneString); @@ -14,17 +14,17 @@ RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(p } } -RocketLaunchLupComponent::~RocketLaunchLupComponent() {} +MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {} -void RocketLaunchLupComponent::OnUse(Entity* originator) { +void MultiZoneEntranceComponent::OnUse(Entity* originator) { auto* rocket = originator->GetComponent()->RocketEquip(originator); if (!rocket) return; // the LUP world menu is just the property menu, the client knows how to handle it - GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress()); + GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), originator->GetSystemAddress()); } -void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) { +void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) { auto* rocketLaunchpadControlComponent = m_Parent->GetComponent(); if (!rocketLaunchpadControlComponent) return; diff --git a/dGame/dComponents/RocketLaunchLupComponent.h b/dGame/dComponents/MultiZoneEntranceComponent.h similarity index 79% rename from dGame/dComponents/RocketLaunchLupComponent.h rename to dGame/dComponents/MultiZoneEntranceComponent.h index 226fa1b2..8928be27 100644 --- a/dGame/dComponents/RocketLaunchLupComponent.h +++ b/dGame/dComponents/MultiZoneEntranceComponent.h @@ -1,7 +1,6 @@ #pragma once #include "Entity.h" -#include "GameMessages.h" #include "Component.h" #include "eReplicaComponentType.h" @@ -9,16 +8,16 @@ * Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds. * */ -class RocketLaunchLupComponent : public Component { +class MultiZoneEntranceComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH_LUP; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE; /** * Constructor for this component, builds the m_LUPWorlds vector * @param parent parent that contains this component */ - RocketLaunchLupComponent(Entity* parent); - ~RocketLaunchLupComponent() override; + MultiZoneEntranceComponent(Entity* parent); + ~MultiZoneEntranceComponent() override; /** * Handles an OnUse event from some entity, preparing it for launch to some other world diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 7df08c60..1af65b4e 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -13,7 +13,7 @@ #include "DestroyableComponent.h" #include "dpWorld.h" #include "PetDigServer.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "eUnequippableActiveType.h" #include "eTerminateType.h" #include "ePetTamingNotifyType.h" @@ -29,6 +29,8 @@ #include "RenderComponent.h" #include "eObjectBits.h" #include "eGameMasterLevel.h" +#include "eMissionState.h" +#include "dNavMesh.h" std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; @@ -68,7 +70,8 @@ std::map PetComponent::petFlags = { { 13067, 838 }, // Skeleton dragon }; -PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(parent) { +PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Component{ parentEntity } { + m_PetInfo = CDClientManager::GetTable()->GetByID(componentId); // TODO: Make reference when safe m_ComponentId = componentId; m_Interaction = LWOOBJID_EMPTY; @@ -80,40 +83,26 @@ PetComponent::PetComponent(Entity* parent, uint32_t componentId): Component(pare m_TimerAway = 0; m_DatabaseId = LWOOBJID_EMPTY; m_Status = 67108866; // Tamable - m_Ability = PetAbilityType::Invalid; - m_StartPosition = NiPoint3::ZERO; + m_Ability = ePetAbilityType::Invalid; + m_StartPosition = NiPoint3Constant::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; m_Preconditions = nullptr; - std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parent->GetVar(u"CheckPrecondition")); + std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); if (!checkPreconditions.empty()) { SetPreconditions(checkPreconditions); } - // Get the imagination drain rate from the CDClient - auto query = CDClientDatabase::CreatePreppedStmt("SELECT imaginationDrainRate FROM PetComponent WHERE id = ?;"); - - query.bind(1, static_cast(componentId)); - - auto result = query.execQuery(); - - // Should a result not exist for this pet default to 60 seconds. - if (!result.eof() && !result.fieldIsNull(0)) { - imaginationDrainRate = result.getFloatField(0, 60.0f); - } else { - imaginationDrainRate = 60.0f; - } - result.finalize(); } -void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { const bool tamed = m_Owner != LWOOBJID_EMPTY; outBitStream->Write1(); // Always serialize as dirty for now - outBitStream->Write(static_cast(m_Status)); - outBitStream->Write(static_cast(tamed ? m_Ability : PetAbilityType::Invalid)); // Something with the overhead icon? + outBitStream->Write(m_Status); + outBitStream->Write(tamed ? m_Ability : ePetAbilityType::Invalid); // Something with the overhead icon? const bool interacting = m_Interaction != LWOOBJID_EMPTY; @@ -135,12 +124,12 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd const auto nameData = GeneralUtils::UTF8ToUTF16(m_Name); const auto ownerNameData = GeneralUtils::UTF8ToUTF16(m_OwnerName); - outBitStream->Write(static_cast(nameData.size())); + outBitStream->Write(nameData.size()); for (const auto c : nameData) { outBitStream->Write(c); } - outBitStream->Write(static_cast(ownerNameData.size())); + outBitStream->Write(ownerNameData.size()); for (const auto c : ownerNameData) { outBitStream->Write(c); } @@ -189,7 +178,7 @@ void PetComponent::OnUse(Entity* originator) { if (cached == buildCache.end()) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); - query.bind(1, (int)m_Parent->GetLOT()); + query.bind(1, static_cast(m_Parent->GetLOT())); auto result = query.execQuery(); @@ -240,7 +229,7 @@ void PetComponent::OnUse(Entity* originator) { if (bricks.empty()) { ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet."); - Game::logger->Log("PetComponent", "Couldn't find %s for minigame!", buildFile.c_str()); + LOG("Couldn't find %s for minigame!", buildFile.c_str()); return; } @@ -262,17 +251,17 @@ void PetComponent::OnUse(Entity* originator) { NiPoint3 forward = NiQuaternion::LookAt(m_Parent->GetPosition(), originator->GetPosition()).GetForwardVector(); forward.y = 0; - if (dpWorld::Instance().IsLoaded()) { + if (dpWorld::IsLoaded()) { NiPoint3 attempt = petPosition + forward * interactionDistance; - float y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); + float y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); while (std::abs(y - petPosition.y) > 4 && interactionDistance > 10) { const NiPoint3 forward = m_Parent->GetRotation().GetForwardVector(); attempt = originatorPosition + forward * interactionDistance; - y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(attempt); + y = dpWorld::GetNavMesh()->GetHeightAtPoint(attempt); interactionDistance -= 0.5f; } @@ -323,7 +312,7 @@ void PetComponent::OnUse(Entity* originator) { } void PetComponent::Update(float deltaTime) { - if (m_StartPosition == NiPoint3::ZERO) { + if (m_StartPosition == NiPoint3Constant::ZERO) { m_StartPosition = m_Parent->GetPosition(); } @@ -439,9 +428,15 @@ void PetComponent::Update(float deltaTime) { } } + auto* missionComponent = owner->GetComponent(); + if (!missionComponent) return; + + // Determine if the "Lost Tags" mission has been completed and digging has been unlocked + const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE; + Entity* closestTresure = PetDigServer::GetClosestTresure(position); - if (closestTresure != nullptr) { + if (closestTresure != nullptr && digUnlocked) { // Skeleton Dragon Pat special case for bone digging if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) { goto skipTresure; @@ -452,7 +447,7 @@ void PetComponent::Update(float deltaTime) { if (distance < 5 * 5) { m_Interaction = closestTresure->GetObjectID(); - Command(NiPoint3::ZERO, LWOOBJID_EMPTY, 1, 202, true); + Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, 202, true); m_TresureTime = 2; } else if (distance < 10 * 10) { @@ -536,7 +531,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { EntityInfo info{}; info.lot = cached->second.puzzleModelLot; info.pos = position; - info.rot = NiQuaternion::IDENTITY; + info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = tamer->GetObjectID(); auto* modelEntity = Game::entityManager->CreateEntity(info); @@ -555,7 +550,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { return; } - LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID(); + LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID(); GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER); GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT); @@ -596,9 +591,9 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { LWOOBJID_EMPTY, false, ePetTamingNotifyType::NAMINGPET, - NiPoint3::ZERO, - NiPoint3::ZERO, - NiQuaternion::IDENTITY, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternionConstant::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS ); @@ -647,7 +642,7 @@ void PetComponent::RequestSetPetName(std::u16string name) { return; } - Game::logger->Log("PetComponent", "Got set pet name (%s)", GeneralUtils::UTF16ToWTF8(name).c_str()); + LOG("Got set pet name (%s)", GeneralUtils::UTF16ToWTF8(name).c_str()); auto* inventoryComponent = tamer->GetComponent(); @@ -676,9 +671,9 @@ void PetComponent::RequestSetPetName(std::u16string name) { m_Tamer, false, ePetTamingNotifyType::SUCCESS, - NiPoint3::ZERO, - NiPoint3::ZERO, - NiQuaternion::IDENTITY, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternionConstant::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS ); @@ -717,9 +712,9 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { m_Tamer, false, ePetTamingNotifyType::QUIT, - NiPoint3::ZERO, - NiPoint3::ZERO, - NiQuaternion::IDENTITY, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternionConstant::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS ); @@ -768,9 +763,9 @@ void PetComponent::ClientFailTamingMinigame() { m_Tamer, false, ePetTamingNotifyType::FAILED, - NiPoint3::ZERO, - NiPoint3::ZERO, - NiQuaternion::IDENTITY, + NiPoint3Constant::ZERO, + NiPoint3Constant::ZERO, + NiQuaternionConstant::IDENTITY, UNASSIGNED_SYSTEM_ADDRESS ); @@ -818,8 +813,8 @@ void PetComponent::Wander() { auto destination = m_StartPosition + delta; - if (dpWorld::Instance().IsLoaded()) { - destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination); + if (dpWorld::IsLoaded()) { + destination.y = dpWorld::GetNavMesh()->GetHeightAtPoint(destination); } if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) { @@ -828,11 +823,11 @@ void PetComponent::Wander() { return; } - m_MovementAI->SetMaxSpeed(info.wanderSpeed); + m_MovementAI->SetMaxSpeed(m_PetInfo.sprintSpeed); m_MovementAI->SetDestination(destination); - m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / info.wanderSpeed; + m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_PetInfo.sprintSpeed; } void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { @@ -898,8 +893,6 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { GameMessages::SendRegisterPetDBID(m_Owner, m_DatabaseId, owner->GetSystemAddress()); } - - GameMessages::SendShowPetActionButton(m_Owner, 3, true, owner->GetSystemAddress()); } void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { @@ -921,22 +914,22 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) { if (!fromTaming) playerDestroyableComponent->Imagine(-1); // Set this to a variable so when this is called back from the player the timer doesn't fire off. - m_Parent->AddCallbackTimer(imaginationDrainRate, [playerDestroyableComponent, this, item]() { + m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() { if (!playerDestroyableComponent) { - Game::logger->Log("PetComponent", "No petComponent and/or no playerDestroyableComponent"); + LOG("No petComponent and/or no playerDestroyableComponent"); return; } - // If we are out of imagination despawn the pet. - if (playerDestroyableComponent->GetImagination() == 0) { - this->Deactivate(); - auto playerEntity = playerDestroyableComponent->GetParent(); - if (!playerEntity) return; + // If we are out of imagination despawn the pet. + if (playerDestroyableComponent->GetImagination() == 0) { + this->Deactivate(); + auto playerEntity = playerDestroyableComponent->GetParent(); + if (!playerEntity) return; - GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); - } + GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet); + } - this->AddDrainImaginationTimer(item); + this->AddDrainImaginationTimer(item); }); } @@ -959,7 +952,7 @@ void PetComponent::Deactivate() { GameMessages::SendRegisterPetDBID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress()); - GameMessages::SendShowPetActionButton(m_Owner, 0, false, owner->GetSystemAddress()); + GameMessages::SendShowPetActionButton(m_Owner, ePetAbilityType::Invalid, false, owner->GetSystemAddress()); } void PetComponent::Release() { @@ -978,12 +971,9 @@ void PetComponent::Release() { item->SetCount(0, false, false); } -void PetComponent::Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey) { +void PetComponent::Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey) { auto* owner = GetOwner(); - - if (owner == nullptr) { - return; - } + if (!owner) return; if (commandType == 1) { // Emotes @@ -1023,7 +1013,7 @@ uint32_t PetComponent::GetStatus() const { return m_Status; } -PetAbilityType PetComponent::GetAbility() const { +ePetAbilityType PetComponent::GetAbility() const { return m_Ability; } @@ -1035,7 +1025,7 @@ void PetComponent::SetStatus(uint32_t value) { m_Status = value; } -void PetComponent::SetAbility(PetAbilityType value) { +void PetComponent::SetAbility(ePetAbilityType value) { m_Ability = value; } @@ -1090,37 +1080,18 @@ void PetComponent::SetPetNameForModeration(const std::string& petName) { approved = 2; //approved } - auto deleteStmt = Database::CreatePreppedStmt("DELETE FROM pet_names WHERE id = ? LIMIT 1;"); - deleteStmt->setUInt64(1, m_DatabaseId); - - deleteStmt->execute(); - - delete deleteStmt; - //Save to db: - auto stmt = Database::CreatePreppedStmt("INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?);"); - stmt->setUInt64(1, m_DatabaseId); - stmt->setString(2, petName); - stmt->setInt(3, approved); - stmt->execute(); - delete stmt; + Database::Get()->SetPetNameModerationStatus(m_DatabaseId, IPetNames::Info{ petName, approved }); } void PetComponent::LoadPetNameFromModeration() { - auto stmt = Database::CreatePreppedStmt("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;"); - stmt->setUInt64(1, m_DatabaseId); - - auto res = stmt->executeQuery(); - while (res->next()) { - m_ModerationStatus = res->getInt(2); - + auto petNameInfo = Database::Get()->GetPetNameInfo(m_DatabaseId); + if (petNameInfo) { + m_ModerationStatus = petNameInfo->approvalStatus; if (m_ModerationStatus == 2) { - m_Name = res->getString(1); + m_Name = petNameInfo->petName; } } - - delete res; - delete stmt; } void PetComponent::SetPreconditions(std::string& preconditions) { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index b3d089a9..6d13bea9 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -1,32 +1,27 @@ -#pragma once +#ifndef PETCOMPONENT_H +#define PETCOMPONENT_H #include "Entity.h" #include "MovementAIComponent.h" #include "Component.h" #include "Preconditions.h" +#include "ePetAbilityType.h" #include "eReplicaComponentType.h" - -enum class PetAbilityType -{ - Invalid, - GoToObject, - JumpOnObject, - DigAtPosition -}; +#include "CDPetComponentTable.h" /** * Represents an entity that is a pet. This pet can be tamed and consequently follows the tamer around, allowing it * to dig for treasure and activate pet bouncers. */ -class PetComponent : public Component +class PetComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PET; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PET; explicit PetComponent(Entity* parentEntity, uint32_t componentId); ~PetComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; /** @@ -103,7 +98,7 @@ public: * @param typeId extra information about the command, e.g. the emote to play * @param overrideObey unused */ - void Command(NiPoint3 position, LWOOBJID source, int32_t commandType, int32_t typeId, bool overrideObey); + void Command(const NiPoint3& position, const LWOOBJID source, const int32_t commandType, const int32_t typeId, const bool overrideObey); /** * Returns the ID of the owner of this pet (if any) @@ -158,13 +153,13 @@ public: * Returns an ability the pet may perform, currently unused * @return an ability the pet may perform */ - PetAbilityType GetAbility() const; + ePetAbilityType GetAbility() const; /** * Sets the ability of the pet, currently unused * @param value the ability to set */ - void SetAbility(PetAbilityType value); + void SetAbility(ePetAbilityType value); /** * Sets preconditions for the pet that need to be met before it can be tamed @@ -323,7 +318,7 @@ private: /** * A currently active ability, mostly unused */ - PetAbilityType m_Ability; + ePetAbilityType m_Ability; /** * The time an entity has left to complete the minigame @@ -357,7 +352,10 @@ private: PreconditionExpression* m_Preconditions; /** - * The rate at which imagination is drained from the user for having the pet out. + * Pet information loaded from the CDClientDatabase + * TODO: Switch to a reference when safe to do so */ - float imaginationDrainRate; + CDPetComponent m_PetInfo; }; + +#endif // !PETCOMPONENT_H diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index 640281f8..b3021415 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -9,7 +9,7 @@ #include "PhantomPhysicsComponent.h" #include "Game.h" #include "LDFFormat.h" -#include "dLogger.h" +#include "Logger.h" #include "Entity.h" #include "EntityManager.h" #include "ControllablePhysicsComponent.h" @@ -27,14 +27,13 @@ #include "dpShapeBox.h" #include "dpShapeSphere.h" -PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(parent) { +PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); m_Scale = m_Parent->GetDefaultScale(); m_dpEntity = nullptr; m_EffectInfoDirty = false; - m_PositionInfoDirty = false; m_IsPhysicsEffectActive = false; m_EffectType = ePhysicsEffectType::PUSH; @@ -144,10 +143,10 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par */ if (!m_HasCreatedPhysics) { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); - CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable(); + CDPhysicsComponentTable* physComp = CDClientManager::GetTable(); if (physComp == nullptr) return; @@ -162,10 +161,10 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "miscellaneous\\misc_phys_640x640.hkx") { // Move this down by 13.521004 units so it is still effectively at the same height as before - m_Position = m_Position - NiPoint3::UNIT_Y * 13.521004f; + m_Position = m_Position - NiPoint3Constant::UNIT_Y * 13.521004f; // 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); @@ -173,65 +172,65 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\trigger_wall_tall.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 10.0f, 25.0f, 1.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\env_gen_placeholderphysics.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 20.0f, 20.0f, 20.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } 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 m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } 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_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 7.5f); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } 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_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position + m_Rotation.GetForwardVector() * 6.0f); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\Ring_Trigger.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 6.0f, 6.0f, 6.0f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else if (info->physicsAsset == "env\\vfx_propertyImaginationBall.hkx") { m_dpEntity = new dpEntity(m_Parent->GetObjectID(), 4.5f); m_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); - } else if (info->physicsAsset == "env\\env_won_fv_gas-blocking-volume.hkx"){ + dpWorld::AddEntity(m_dpEntity); + } 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_dpEntity->SetScale(m_Scale); m_dpEntity->SetRotation(m_Rotation); m_Position.y -= (111.467964f * m_Scale) / 2; m_dpEntity->SetPosition(m_Position); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } else { - //Game::logger->Log("PhantomPhysicsComponent", "This one is supposed to have %s", info->physicsAsset.c_str()); + //LOG("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); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); } } @@ -239,7 +238,7 @@ PhantomPhysicsComponent::PhantomPhysicsComponent(Entity* parent) : Component(par PhantomPhysicsComponent::~PhantomPhysicsComponent() { if (m_dpEntity) { - dpWorld::Instance().RemoveEntity(m_dpEntity); + dpWorld::RemoveEntity(m_dpEntity); } } @@ -261,10 +260,10 @@ void PhantomPhysicsComponent::CreatePhysics() { y = m_Parent->GetVar(u"primitiveModelValueY"); z = m_Parent->GetVar(u"primitiveModelValueZ"); } else { - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); auto componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::PHANTOM_PHYSICS); - CDPhysicsComponentTable* physComp = CDClientManager::Instance().GetTable(); + CDPhysicsComponentTable* physComp = CDClientManager::GetTable(); if (physComp == nullptr) return; @@ -301,24 +300,13 @@ void PhantomPhysicsComponent::CreatePhysics() { m_dpEntity->SetPosition({ m_Position.x, m_Position.y - (height / 2), m_Position.z }); - dpWorld::Instance().AddEntity(m_dpEntity); + dpWorld::AddEntity(m_dpEntity); m_HasCreatedPhysics = true; } -void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write(m_PositionInfoDirty || bIsInitialUpdate); - if (m_PositionInfoDirty || bIsInitialUpdate) { - outBitStream->Write(m_Position.x); - outBitStream->Write(m_Position.y); - outBitStream->Write(m_Position.z); - outBitStream->Write(m_Rotation.x); - outBitStream->Write(m_Rotation.y); - outBitStream->Write(m_Rotation.z); - outBitStream->Write(m_Rotation.w); - - m_PositionInfoDirty = false; - } +void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate); outBitStream->Write(m_EffectInfoDirty || bIsInitialUpdate); if (m_EffectInfoDirty || bIsInitialUpdate) { @@ -348,9 +336,28 @@ void PhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bI } } -void PhantomPhysicsComponent::ResetFlags() { - m_EffectInfoDirty = false; - m_PositionInfoDirty = false; +// Even if we were to implement Friction server side, +// it also defaults to 1.0f in the last argument, so we dont need two functions to do the same thing. +void ApplyCollisionEffect(const LWOOBJID& target, const ePhysicsEffectType effectType, const float effectScale) { + switch (effectType) { + case ePhysicsEffectType::GRAVITY_SCALE: { + auto* targetEntity = Game::entityManager->GetEntity(target); + if (targetEntity) { + auto* controllablePhysicsComponent = targetEntity->GetComponent(); + // dont want to apply an effect to nothing. + if (!controllablePhysicsComponent) return; + controllablePhysicsComponent->SetGravityScale(effectScale); + GameMessages::SendSetGravityScale(target, effectScale, targetEntity->GetSystemAddress()); + } + } + // The other types are not handled by the server + case ePhysicsEffectType::ATTRACT: + case ePhysicsEffectType::FRICTION: + case ePhysicsEffectType::PUSH: + case ePhysicsEffectType::REPULSE: + default: + break; + } } void PhantomPhysicsComponent::Update(float deltaTime) { @@ -358,6 +365,8 @@ void PhantomPhysicsComponent::Update(float deltaTime) { //Process enter events 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: @@ -374,6 +383,8 @@ void PhantomPhysicsComponent::Update(float deltaTime) { //Process exit events for (auto en : m_dpEntity->GetRemovedObjects()) { + if (!en) continue; + ApplyCollisionEffect(en->GetObjectID(), m_EffectType, 1.0f); m_Parent->OnCollisionLeavePhantom(en->GetObjectID()); } } @@ -391,10 +402,10 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) { void PhantomPhysicsComponent::SpawnVertices() { if (!m_dpEntity) return; - std::cout << m_Parent->GetObjectID() << std::endl; + LOG("%llu", m_Parent->GetObjectID()); auto box = static_cast(m_dpEntity->GetShape()); for (auto vert : box->GetVertices()) { - std::cout << vert.x << ", " << vert.y << ", " << vert.z << std::endl; + LOG("%f, %f, %f", vert.x, vert.y, vert.z); EntityInfo info; info.lot = 33; @@ -431,13 +442,11 @@ void PhantomPhysicsComponent::SetMax(uint32_t max) { } void PhantomPhysicsComponent::SetPosition(const NiPoint3& pos) { - m_Position = pos; - + PhysicsComponent::SetPosition(pos); if (m_dpEntity) m_dpEntity->SetPosition(pos); } void PhantomPhysicsComponent::SetRotation(const NiQuaternion& rot) { - m_Rotation = rot; - + PhysicsComponent::SetRotation(rot); if (m_dpEntity) m_dpEntity->SetRotation(rot); } diff --git a/dGame/dComponents/PhantomPhysicsComponent.h b/dGame/dComponents/PhantomPhysicsComponent.h index cc0d1844..2ea9e979 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.h +++ b/dGame/dComponents/PhantomPhysicsComponent.h @@ -11,8 +11,8 @@ #include #include "CppScripts.h" #include "InvalidScript.h" -#include "Component.h" #include "eReplicaComponentType.h" +#include "PhysicsComponent.h" class LDFBaseData; class Entity; @@ -25,15 +25,14 @@ enum class ePhysicsEffectType : uint32_t ; * trigger gameplay events, for example the bus in Avant Gardens that moves around when the player touches its physics * body. Optionally this object can also have effects, like the fans in AG. */ -class PhantomPhysicsComponent : public Component { +class PhantomPhysicsComponent final : public PhysicsComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; PhantomPhysicsComponent(Entity* parent); ~PhantomPhysicsComponent() override; void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - void ResetFlags(); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Creates the physics shape for this entity based on LDF data @@ -76,29 +75,17 @@ public: */ void SetPhysicsEffectActive(bool val) { m_IsPhysicsEffectActive = val; m_EffectInfoDirty = true; } - /** - * Returns the position of this physics object - * @return the position of this physics object - */ - const NiPoint3& GetPosition() const { return m_Position; } - /** * Sets the position of this physics object * @param pos the position to set */ - void SetPosition(const NiPoint3& pos); - - /** - * Returns the rotation of this physics object - * @return the rotation of this physics object - */ - const NiQuaternion& GetRotation() const { return m_Rotation; } + void SetPosition(const NiPoint3& pos) override; /** * Sets the rotation of this physics object * @param rot the rotation to set */ - void SetRotation(const NiQuaternion& rot); + void SetRotation(const NiQuaternion& rot) override; /** * Returns the effect that's currently active, defaults to 0 @@ -135,27 +122,11 @@ public: void SetMax(uint32_t max); private: - - /** - * The position of the physics object - */ - NiPoint3 m_Position; - - /** - * The rotation of the physics object - */ - NiQuaternion m_Rotation; - /** * A scale to apply to the size of the physics object */ float m_Scale; - /** - * Whether or not the position has changed and needs to be serialized - */ - bool m_PositionInfoDirty; - /** * Whether or not the effect has changed and needs to be serialized */ diff --git a/dGame/dComponents/PhysicsComponent.cpp b/dGame/dComponents/PhysicsComponent.cpp new file mode 100644 index 00000000..ea57735f --- /dev/null +++ b/dGame/dComponents/PhysicsComponent.cpp @@ -0,0 +1,21 @@ +#include "PhysicsComponent.h" + +PhysicsComponent::PhysicsComponent(Entity* parent) : Component(parent) { + m_Position = NiPoint3Constant::ZERO; + m_Rotation = NiQuaternionConstant::IDENTITY; + m_DirtyPosition = false; +} + +void PhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); + if (bIsInitialUpdate || m_DirtyPosition) { + outBitStream->Write(m_Position.x); + outBitStream->Write(m_Position.y); + outBitStream->Write(m_Position.z); + outBitStream->Write(m_Rotation.x); + outBitStream->Write(m_Rotation.y); + outBitStream->Write(m_Rotation.z); + outBitStream->Write(m_Rotation.w); + if (!bIsInitialUpdate) m_DirtyPosition = false; + } +} diff --git a/dGame/dComponents/PhysicsComponent.h b/dGame/dComponents/PhysicsComponent.h new file mode 100644 index 00000000..c1a7f34a --- /dev/null +++ b/dGame/dComponents/PhysicsComponent.h @@ -0,0 +1,32 @@ +#ifndef __PHYSICSCOMPONENT__H__ +#define __PHYSICSCOMPONENT__H__ + +#include "Component.h" +#include "NiPoint3.h" +#include "NiQuaternion.h" + +namespace Raknet { + class BitStream; +}; + +class PhysicsComponent : public Component { +public: + PhysicsComponent(Entity* parent); + virtual ~PhysicsComponent() = default; + + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + + const NiPoint3& GetPosition() const { return m_Position; } + virtual void SetPosition(const NiPoint3& pos) { if (m_Position == pos) return; m_Position = pos; m_DirtyPosition = true; } + + 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: + NiPoint3 m_Position; + + NiQuaternion m_Rotation; + + bool m_DirtyPosition; +}; + +#endif //!__PHYSICSCOMPONENT__H__ diff --git a/dGame/dComponents/PlayerForcedMovementComponent.cpp b/dGame/dComponents/PlayerForcedMovementComponent.cpp index 76993507..d511ad78 100644 --- a/dGame/dComponents/PlayerForcedMovementComponent.cpp +++ b/dGame/dComponents/PlayerForcedMovementComponent.cpp @@ -6,7 +6,7 @@ PlayerForcedMovementComponent::PlayerForcedMovementComponent(Entity* parent) : C PlayerForcedMovementComponent::~PlayerForcedMovementComponent() {} -void PlayerForcedMovementComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void PlayerForcedMovementComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_DirtyInfo || bIsInitialUpdate); if (m_DirtyInfo || bIsInitialUpdate) { outBitStream->Write(m_PlayerOnRail); diff --git a/dGame/dComponents/PlayerForcedMovementComponent.h b/dGame/dComponents/PlayerForcedMovementComponent.h index 90708c9a..e5aca37d 100644 --- a/dGame/dComponents/PlayerForcedMovementComponent.h +++ b/dGame/dComponents/PlayerForcedMovementComponent.h @@ -8,9 +8,9 @@ * Component that handles player forced movement * */ -class PlayerForcedMovementComponent : public Component { +class PlayerForcedMovementComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PLAYER_FORCED_MOVEMENT; /** * Constructor for this component @@ -19,7 +19,7 @@ public: PlayerForcedMovementComponent(Entity* parent); ~PlayerForcedMovementComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * @brief Set the Player On Rail object diff --git a/dGame/dComponents/PossessableComponent.cpp b/dGame/dComponents/PossessableComponent.cpp index 5c45a6c1..509b1a07 100644 --- a/dGame/dComponents/PossessableComponent.cpp +++ b/dGame/dComponents/PossessableComponent.cpp @@ -27,7 +27,7 @@ PossessableComponent::PossessableComponent(Entity* parent, uint32_t componentId) result.finalize(); } -void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void PossessableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_DirtyPossessable || bIsInitialUpdate); if (m_DirtyPossessable || bIsInitialUpdate) { m_DirtyPossessable = false; // reset flag diff --git a/dGame/dComponents/PossessableComponent.h b/dGame/dComponents/PossessableComponent.h index 2026c11e..3b075d33 100644 --- a/dGame/dComponents/PossessableComponent.h +++ b/dGame/dComponents/PossessableComponent.h @@ -12,13 +12,13 @@ * Represents an entity that can be controlled by some other entity, generally used by cars to indicate that some * player is controlling it. */ -class PossessableComponent : public Component { +class PossessableComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSABLE; PossessableComponent(Entity* parentEntity, uint32_t componentId); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * @brief mounts the Entity diff --git a/dGame/dComponents/PossessorComponent.cpp b/dGame/dComponents/PossessorComponent.cpp index 8019f91c..0cb64956 100644 --- a/dGame/dComponents/PossessorComponent.cpp +++ b/dGame/dComponents/PossessorComponent.cpp @@ -26,7 +26,7 @@ PossessorComponent::~PossessorComponent() { } } -void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void PossessorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_DirtyPossesor || bIsInitialUpdate); if (m_DirtyPossesor || bIsInitialUpdate) { m_DirtyPossesor = false; diff --git a/dGame/dComponents/PossessorComponent.h b/dGame/dComponents/PossessorComponent.h index 4456af27..e1aba048 100644 --- a/dGame/dComponents/PossessorComponent.h +++ b/dGame/dComponents/PossessorComponent.h @@ -16,14 +16,14 @@ enum class ePossessionType : uint8_t { /** * Represents an entity that can posess other entities. Generally used by players to drive a car. */ -class PossessorComponent : public Component { +class PossessorComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::POSSESSOR; PossessorComponent(Entity* parent); ~PossessorComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * @brief Mounts the entity diff --git a/dGame/dComponents/PropertyComponent.cpp b/dGame/dComponents/PropertyComponent.cpp deleted file mode 100644 index 4f8df40c..00000000 --- a/dGame/dComponents/PropertyComponent.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "PropertyComponent.h" -#include "GameMessages.h" -#include "dZoneManager.h" - -PropertyComponent::PropertyComponent(Entity* parent) : Component(parent) { - m_PropertyName = parent->GetVar(u"propertyName"); - m_PropertyState = new PropertyState(); -} - -PropertyComponent::~PropertyComponent() = default; - diff --git a/dGame/dComponents/PropertyComponent.h b/dGame/dComponents/PropertyComponent.h index 41f93677..1085aac4 100644 --- a/dGame/dComponents/PropertyComponent.h +++ b/dGame/dComponents/PropertyComponent.h @@ -1,34 +1,22 @@ /* * Darkflame Universe - * Copyright 2018 + * Copyright 2024 */ #ifndef PROPERTYCOMPONENT_H #define PROPERTYCOMPONENT_H -#include "BitStream.h" #include "Entity.h" #include "Component.h" #include "eReplicaComponentType.h" -struct PropertyState { - LWOOBJID ownerID; - LWOOBJID propertyID; - bool rented; -}; - /** * This component is unused and has no functionality */ -class PropertyComponent : public Component { +class PropertyComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; - explicit PropertyComponent(Entity* parentEntity); - ~PropertyComponent() override; - [[nodiscard]] PropertyState* GetPropertyState() const { return m_PropertyState; }; -private: - PropertyState* m_PropertyState; - std::string m_PropertyName; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY; + explicit PropertyComponent(Entity* const parentEntity) noexcept : Component{ parentEntity } {} }; -#endif // PROPERTYCOMPONENT_H +#endif // !PROPERTYCOMPONENT_H diff --git a/dGame/dComponents/PropertyEntranceComponent.cpp b/dGame/dComponents/PropertyEntranceComponent.cpp index bff917d8..ab3bb5da 100644 --- a/dGame/dComponents/PropertyEntranceComponent.cpp +++ b/dGame/dComponents/PropertyEntranceComponent.cpp @@ -1,6 +1,6 @@ #include "PropertyEntranceComponent.h" -#include +#include "CDPropertyEntranceComponentTable.h" #include "Character.h" #include "Database.h" @@ -10,15 +10,15 @@ #include "RocketLaunchpadControlComponent.h" #include "CharacterComponent.h" #include "UserManager.h" -#include "dLogger.h" +#include "Logger.h" #include "Amf3.h" #include "eObjectBits.h" #include "eGameMasterLevel.h" -PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entity* parent) : Component(parent) { +PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, uint32_t componentID) : Component(parent) { this->propertyQueries = {}; - auto table = CDClientManager::Instance().GetTable(); + auto table = CDClientManager::GetTable(); const auto& entry = table->GetByID(componentID); this->m_MapID = entry.mapID; @@ -103,7 +103,7 @@ std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMe if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) { std::string friendsList = " AND p.owner_id IN ("; - auto friendsListQuery = Database::CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); + auto friendsListQuery = Database::Get()->CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;"); friendsListQuery->setUInt(1, character->GetID()); friendsListQuery->setUInt(2, character->GetID()); @@ -147,7 +147,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl if (!character) return; // Player property goes in index 1 of the vector. This is how the client expects it. - auto playerPropertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); + auto playerPropertyLookup = Database::Get()->CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?"); playerPropertyLookup->setInt(1, character->GetID()); playerPropertyLookup->setInt(2, this->m_MapID); @@ -163,7 +163,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl const auto modApproved = playerPropertyLookupResults->getBoolean(10); const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11); const auto reputation = playerPropertyLookupResults->getUInt(14); - const auto performanceCost = (float)playerPropertyLookupResults->getDouble(16); + const auto performanceCost = playerPropertyLookupResults->getFloat(16); playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost); } else { @@ -180,14 +180,14 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl const auto query = BuildQuery(entity, sortMethod, character); - auto propertyLookup = Database::CreatePreppedStmt(query); + auto propertyLookup = Database::Get()->CreatePreppedStmt(query); const auto searchString = "%" + filterText + "%"; propertyLookup->setUInt(1, this->m_MapID); propertyLookup->setString(2, searchString.c_str()); propertyLookup->setString(3, searchString.c_str()); propertyLookup->setString(4, searchString.c_str()); - propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? (uint32_t)PropertyPrivacyOption::Friends : (uint32_t)PropertyPrivacyOption::Public); + propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? static_cast(PropertyPrivacyOption::Friends) : static_cast(PropertyPrivacyOption::Public)); propertyLookup->setInt(6, numResults); propertyLookup->setInt(7, startIndex); @@ -203,13 +203,13 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl const auto modApproved = propertyEntry->getBoolean(10); const auto dateLastUpdated = propertyEntry->getInt(11); const float reputation = propertyEntry->getInt(14); - const auto performanceCost = (float)propertyEntry->getDouble(16); + const auto performanceCost = propertyEntry->getFloat(16); PropertySelectQueryProperty entry{}; std::string ownerName = ""; bool isOwned = true; - auto nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); + auto nameLookup = Database::Get()->CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); nameLookup->setUInt64(1, cloneId); @@ -219,7 +219,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl delete nameLookup; nameLookup = nullptr; - Game::logger->Log("PropertyEntranceComponent", "Failed to find property owner name for %llu!", cloneId); + LOG("Failed to find property owner name for %llu!", cloneId); continue; } else { @@ -245,7 +245,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT); // Query to get friend and best friend fields - auto friendCheck = Database::CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); + auto friendCheck = Database::Get()->CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)"); friendCheck->setUInt(1, character->GetID()); friendCheck->setUInt(2, ownerObjId); @@ -278,7 +278,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl bool isAlt = false; // Query to determine whether this property is an alt character of the entity. - auto isAltQuery = Database::CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); + auto isAltQuery = Database::Get()->CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;"); isAltQuery->setInt(1, character->GetID()); isAltQuery->setInt(2, owner); @@ -312,7 +312,7 @@ void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool incl int32_t numberOfProperties = 0; auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false); - auto propertiesLeft = Database::CreatePreppedStmt(buttonQuery); + auto propertiesLeft = Database::Get()->CreatePreppedStmt(buttonQuery); propertiesLeft->setUInt(1, this->m_MapID); propertiesLeft->setString(2, searchString.c_str()); diff --git a/dGame/dComponents/PropertyEntranceComponent.h b/dGame/dComponents/PropertyEntranceComponent.h index e37d1daa..510bb489 100644 --- a/dGame/dComponents/PropertyEntranceComponent.h +++ b/dGame/dComponents/PropertyEntranceComponent.h @@ -11,10 +11,10 @@ /** * Represents the launch pad that's used to select and browse properties */ -class PropertyEntranceComponent : public Component { +class PropertyEntranceComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; - explicit PropertyEntranceComponent(uint32_t componentID, Entity* parent); + explicit PropertyEntranceComponent(Entity* parent, uint32_t componentID); + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_ENTRANCE; /** * Handles an OnUse request for some other entity, rendering the property browse menu diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index c8937c35..0dfc04af 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -13,13 +13,14 @@ #include "Game.h" #include "Item.h" #include "Database.h" -#include "../dWorldServer/ObjectIDManager.h" -#include "Player.h" +#include "ObjectIDManager.h" #include "RocketLaunchpadControlComponent.h" #include "PropertyEntranceComponent.h" #include "InventoryComponent.h" #include "eMissionTaskType.h" #include "eObjectBits.h" +#include "CharacterComponent.h" +#include "PlayerManager.h" #include #include "CppScripts.h" @@ -39,13 +40,12 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo instance = this; const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); - const auto zoneId = worldId.GetMapID(); const auto cloneId = worldId.GetCloneID(); - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT id FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT id FROM PropertyTemplate WHERE mapID = ?;"); + + query.bind(1, static_cast(zoneId)); auto result = query.execQuery(); @@ -55,34 +55,25 @@ PropertyManagementComponent::PropertyManagementComponent(Entity* parent) : Compo templateId = result.getIntField(0); - result.finalize(); + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); - auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;"); - - propertyLookup->setInt(1, templateId); - propertyLookup->setInt64(2, cloneId); - - auto* propertyEntry = propertyLookup->executeQuery(); - - if (propertyEntry->next()) { - this->propertyId = propertyEntry->getUInt64(1); - this->owner = propertyEntry->getUInt64(2); + if (propertyInfo) { + this->propertyId = propertyInfo->id; + this->owner = propertyInfo->ownerId; GeneralUtils::SetBit(this->owner, eObjectBits::CHARACTER); GeneralUtils::SetBit(this->owner, eObjectBits::PERSISTENT); - this->clone_Id = propertyEntry->getInt(2); - this->propertyName = propertyEntry->getString(5).c_str(); - this->propertyDescription = propertyEntry->getString(6).c_str(); - this->privacyOption = static_cast(propertyEntry->getUInt(9)); - this->moderatorRequested = propertyEntry->getInt(10) == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public; - this->LastUpdatedTime = propertyEntry->getUInt64(11); - this->claimedTime = propertyEntry->getUInt64(12); - this->rejectionReason = std::string(propertyEntry->getString(13).c_str()); - this->reputation = propertyEntry->getUInt(14); + this->clone_Id = propertyInfo->cloneId; + this->propertyName = propertyInfo->name; + this->propertyDescription = propertyInfo->description; + this->privacyOption = static_cast(propertyInfo->privacyOption); + this->rejectionReason = propertyInfo->rejectionReason; + this->moderatorRequested = propertyInfo->modApproved == 0 && rejectionReason == "" && privacyOption == PropertyPrivacyOption::Public; + this->LastUpdatedTime = propertyInfo->lastUpdatedTime; + this->claimedTime = propertyInfo->claimedTime; + this->reputation = propertyInfo->reputation; Load(); } - - delete propertyLookup; } LWOOBJID PropertyManagementComponent::GetOwnerId() const { @@ -102,7 +93,7 @@ std::vector PropertyManagementComponent::GetPaths() const { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT path FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); + query.bind(1, static_cast(zoneId)); auto result = query.execQuery(); @@ -123,7 +114,7 @@ std::vector PropertyManagementComponent::GetPaths() const { points.push_back(value); } catch (std::invalid_argument& exception) { - Game::logger->Log("PropertyManagementComponent", "Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); + LOG("Failed to parse value (%s): (%s)!", token.c_str(), exception.what()); } } @@ -152,14 +143,13 @@ void PropertyManagementComponent::SetPrivacyOption(PropertyPrivacyOption value) } privacyOption = value; - auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.privacyOption = static_cast(privacyOption); + info.rejectionReason = rejectionReason; + info.modApproved = 0; - propertyUpdate->setInt(1, static_cast(value)); - propertyUpdate->setString(2, ""); - propertyUpdate->setInt(3, 0); - propertyUpdate->setInt64(4, propertyId); - - propertyUpdate->executeUpdate(); + Database::Get()->UpdatePropertyModerationInfo(info); } void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::string description) { @@ -169,13 +159,12 @@ void PropertyManagementComponent::UpdatePropertyDetails(std::string name, std::s propertyDescription = description; - auto* propertyUpdate = Database::CreatePreppedStmt("UPDATE properties SET name = ?, description = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.name = propertyName; + info.description = propertyDescription; - propertyUpdate->setString(1, name.c_str()); - propertyUpdate->setString(2, description.c_str()); - propertyUpdate->setInt64(3, propertyId); - - propertyUpdate->executeUpdate(); + Database::Get()->UpdatePropertyDetails(info); OnQueryPropertyData(GetOwner(), UNASSIGNED_SYSTEM_ADDRESS); } @@ -187,8 +176,6 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) { auto* entity = Game::entityManager->GetEntity(playerId); - auto* user = entity->GetParentUser(); - auto character = entity->GetCharacter(); if (!character) return false; @@ -217,28 +204,14 @@ bool PropertyManagementComponent::Claim(const LWOOBJID playerId) { propertyId = ObjectIDManager::GenerateRandomObjectID(); - auto* insertion = Database::CreatePreppedStmt( - "INSERT INTO properties" - "(id, owner_id, template_id, clone_id, name, description, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, zone_id, performance_cost)" - "VALUES (?, ?, ?, ?, ?, ?, 0, 0, 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), '', 0, ?, 0.0)" - ); - insertion->setUInt64(1, propertyId); - insertion->setUInt64(2, (uint32_t)playerId); - insertion->setUInt(3, templateId); - insertion->setUInt64(4, playerCloneId); - insertion->setString(5, name.c_str()); - insertion->setString(6, description.c_str()); - insertion->setInt(7, propertyZoneId); + IProperty::Info info; + info.id = propertyId; + info.ownerId = character->GetID(); + info.cloneId = playerCloneId; + info.name = name; + info.description = description; - // Try and execute the query, print an error if it fails. - try { - insertion->execute(); - } catch (sql::SQLException& exception) { - Game::logger->Log("PropertyManagementComponent", "Failed to execute query: (%s)!", exception.what()); - - throw exception; - return false; - } + Database::Get()->InsertNewProperty(info, templateId, worldId); auto* zoneControlObject = Game::zoneManager->GetZoneControlObject(); for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControlObject)) { @@ -252,7 +225,7 @@ void PropertyManagementComponent::OnStartBuilding() { if (ownerEntity == nullptr) return; - const auto players = Player::GetAllPlayers(); + const auto players = PlayerManager::GetAllPlayers(); LWOMAPID zoneId = 1100; @@ -273,7 +246,8 @@ void PropertyManagementComponent::OnStartBuilding() { for (auto* player : players) { if (player == ownerEntity) continue; - player->SendToZone(zoneId); + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(zoneId); } auto inventoryComponent = ownerEntity->GetComponent(); @@ -294,7 +268,7 @@ void PropertyManagementComponent::OnFinishBuilding() { } void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const NiPoint3 position, NiQuaternion rotation) { - Game::logger->Log("PropertyManagementComponent", "Placing model <%f, %f, %f>", position.x, position.y, position.z); + LOG("Placing model <%f, %f, %f>", position.x, position.y, position.z); auto* entity = GetOwner(); @@ -311,7 +285,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N auto* item = inventoryComponent->FindItemById(id); if (item == nullptr) { - Game::logger->Log("PropertyManagementComponent", "Failed to find item with id %d", id); + LOG("Failed to find item with id %d", id); return; } @@ -320,7 +294,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N const auto modelLOT = item->GetLot(); - if (rotation != NiQuaternion::IDENTITY) { + if (rotation != NiQuaternionConstant::IDENTITY) { rotation = { rotation.w, rotation.z, rotation.y, rotation.x }; } @@ -360,7 +334,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N node->position = position; node->rotation = rotation; - ObjectIDManager::Instance()->RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) { + ObjectIDManager::RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) { SpawnerInfo info{}; info.templateID = modelLOT; @@ -409,7 +383,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N } void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int deleteReason) { - Game::logger->Log("PropertyManagementComponent", "Delete model: (%llu) (%i)", id, deleteReason); + LOG("Delete model: (%llu) (%i)", id, deleteReason); auto* entity = GetOwner(); @@ -423,10 +397,29 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet return; } + auto* model = Game::entityManager->GetEntity(id); + + if (model == nullptr) { + LOG("Failed to find model entity"); + + return; + } + + if (model->GetLOT() == 14 && deleteReason == 0) { + LOG("User is trying to pick up a BBB model, but this is not implemented, so we return to prevent the user from losing the model"); + + GameMessages::SendUGCEquipPostDeleteBasedOnEditMode(entity->GetObjectID(), entity->GetSystemAddress(), LWOOBJID_EMPTY, 0); + + // Need this to pop the user out of their current state + GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), entity->GetPosition(), m_Parent->GetObjectID(), 14, entity->GetRotation()); + + return; + } + const auto index = models.find(id); if (index == models.end()) { - Game::logger->Log("PropertyManagementComponent", "Failed to find model"); + LOG("Failed to find model"); return; } @@ -438,20 +431,12 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet models.erase(id); if (spawner == nullptr) { - Game::logger->Log("PropertyManagementComponent", "Failed to find spawner"); - } - - auto* model = Game::entityManager->GetEntity(id); - - if (model == nullptr) { - Game::logger->Log("PropertyManagementComponent", "Failed to find model entity"); - - return; + LOG("Failed to find spawner"); } Game::entityManager->DestructEntity(model); - Game::logger->Log("PropertyManagementComponent", "Deleting model LOT %i", model->GetLOT()); + LOG("Deleting model LOT %i", model->GetLOT()); if (model->GetLOT() == 14) { //add it to the inv @@ -493,7 +478,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3::ZERO, LWOOBJID_EMPTY, 16, NiQuaternion::IDENTITY); + GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, NiQuaternionConstant::IDENTITY); if (spawner != nullptr) { Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID); @@ -534,19 +519,19 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet { item->SetCount(item->GetCount() - 1); - Game::logger->Log("BODGE TIME", "YES IT GOES HERE"); + LOG("DLU currently does not support breaking apart brick by brick models."); break; } default: { - Game::logger->Log("PropertyManagementComponent", "Invalid delete reason"); + LOG("Invalid delete reason"); } } GameMessages::SendGetModelsOnProperty(entity->GetObjectID(), GetModels(), UNASSIGNED_SYSTEM_ADDRESS); - GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3::ZERO, LWOOBJID_EMPTY, 16, NiQuaternion::IDENTITY); + GameMessages::SendPlaceModelResponse(entity->GetObjectID(), entity->GetSystemAddress(), NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 16, NiQuaternionConstant::IDENTITY); if (spawner != nullptr) { Game::zoneManager->RemoveSpawner(spawner->m_Info.spawnerID); @@ -558,14 +543,13 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet void PropertyManagementComponent::UpdateApprovedStatus(const bool value) { if (owner == LWOOBJID_EMPTY) return; - auto* update = Database::CreatePreppedStmt("UPDATE properties SET mod_approved = ? WHERE id = ?;"); + IProperty::Info info; + info.id = propertyId; + info.modApproved = value; + info.privacyOption = static_cast(privacyOption); + info.rejectionReason = ""; - update->setBoolean(1, value); - update->setInt64(2, propertyId); - - update->executeUpdate(); - - delete update; + Database::Get()->UpdatePropertyModerationInfo(info); } void PropertyManagementComponent::Load() { @@ -573,39 +557,17 @@ void PropertyManagementComponent::Load() { return; } - auto* lookup = Database::CreatePreppedStmt("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;"); - - lookup->setUInt64(1, propertyId); - - auto* lookupResult = lookup->executeQuery(); - - while (lookupResult->next()) { - const LWOOBJID id = lookupResult->getUInt64(1); - const LOT lot = lookupResult->getInt(2); - - const NiPoint3 position = - { - static_cast(lookupResult->getDouble(3)), - static_cast(lookupResult->getDouble(4)), - static_cast(lookupResult->getDouble(5)) - }; - - const NiQuaternion rotation = - { - static_cast(lookupResult->getDouble(9)), - static_cast(lookupResult->getDouble(6)), - static_cast(lookupResult->getDouble(7)), - static_cast(lookupResult->getDouble(8)) - }; + auto propertyModels = Database::Get()->GetPropertyModels(propertyId); + for (const auto& databaseModel : propertyModels) { auto* node = new SpawnerNode(); - node->position = position; - node->rotation = rotation; + node->position = databaseModel.position; + node->rotation = databaseModel.rotation; SpawnerInfo info{}; - info.templateID = lot; + info.templateID = databaseModel.lot; info.nodes = { node }; info.templateScale = 1.0f; info.activeOnLoad = true; @@ -615,13 +577,13 @@ void PropertyManagementComponent::Load() { //info.emulated = true; //info.emulator = Game::entityManager->GetZoneControlEntity()->GetObjectID(); - info.spawnerID = id; + info.spawnerID = databaseModel.id; std::vector settings; //BBB property models need to have extra stuff set for them: - if (lot == 14) { - LWOOBJID blueprintID = lookupResult->getUInt(10); + if (databaseModel.lot == 14) { + LWOOBJID blueprintID = databaseModel.ugcId; GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); @@ -629,7 +591,7 @@ void PropertyManagementComponent::Load() { LDFBaseData* componentWhitelist = new LDFData(u"componentWhitelist", 1); LDFBaseData* modelType = new LDFData(u"modelType", 2); LDFBaseData* propertyObjectID = new LDFData(u"propertyObjectID", true); - LDFBaseData* userModelID = new LDFData(u"userModelID", id); + LDFBaseData* userModelID = new LDFData(u"userModelID", databaseModel.id); settings.push_back(ldfBlueprintID); settings.push_back(componentWhitelist); @@ -638,7 +600,7 @@ void PropertyManagementComponent::Load() { settings.push_back(userModelID); } else { auto modelType = new LDFData(u"modelType", 2); - auto userModelID = new LDFData(u"userModelID", id); + auto userModelID = new LDFData(u"userModelID", databaseModel.id); auto ldfModelBehavior = new LDFData(u"modelBehaviors", 0); auto propertyObjectID = new LDFData(u"propertyObjectID", true); auto componentWhitelist = new LDFData(u"componentWhitelist", 1); @@ -660,8 +622,6 @@ void PropertyManagementComponent::Load() { models.insert_or_assign(model->GetObjectID(), spawnerId); } - - delete lookup; } void PropertyManagementComponent::Save() { @@ -669,27 +629,7 @@ void PropertyManagementComponent::Save() { return; } - auto* insertion = Database::CreatePreppedStmt("INSERT INTO properties_contents VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);"); - auto* update = Database::CreatePreppedStmt("UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;"); - auto* lookup = Database::CreatePreppedStmt("SELECT id FROM properties_contents WHERE property_id = ?;"); - auto* remove = Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE id = ?;"); - - lookup->setUInt64(1, propertyId); - sql::ResultSet* lookupResult = nullptr; - try { - lookupResult = lookup->executeQuery(); - } catch (sql::SQLException& ex) { - Game::logger->Log("PropertyManagementComponent", "lookup error %s", ex.what()); - } - std::vector present; - - while (lookupResult->next()) { - const auto dbId = lookupResult->getUInt64(1); - - present.push_back(dbId); - } - - delete lookupResult; + auto present = Database::Get()->GetPropertyModels(propertyId); std::vector modelIds; @@ -708,69 +648,26 @@ void PropertyManagementComponent::Save() { const auto rotation = entity->GetRotation(); if (std::find(present.begin(), present.end(), id) == present.end()) { - insertion->setInt64(1, id); - insertion->setUInt64(2, propertyId); - insertion->setNull(3, 0); - insertion->setInt(4, entity->GetLOT()); - insertion->setDouble(5, position.x); - insertion->setDouble(6, position.y); - insertion->setDouble(7, position.z); - insertion->setDouble(8, rotation.x); - insertion->setDouble(9, rotation.y); - insertion->setDouble(10, rotation.z); - insertion->setDouble(11, rotation.w); - insertion->setString(12, ("Objects_" + std::to_string(entity->GetLOT()) + "_name").c_str()); // Model name. TODO make this customizable - insertion->setString(13, ""); // Model description. TODO implement this. - insertion->setDouble(14, 0); // behavior 1. TODO implement this. - insertion->setDouble(15, 0); // behavior 2. TODO implement this. - insertion->setDouble(16, 0); // behavior 3. TODO implement this. - insertion->setDouble(17, 0); // behavior 4. TODO implement this. - insertion->setDouble(18, 0); // behavior 5. TODO implement this. - try { - insertion->execute(); - } catch (sql::SQLException& ex) { - Game::logger->Log("PropertyManagementComponent", "Error inserting into properties_contents. Error %s", ex.what()); - } - } else { - update->setDouble(1, position.x); - update->setDouble(2, position.y); - update->setDouble(3, position.z); - update->setDouble(4, rotation.x); - update->setDouble(5, rotation.y); - update->setDouble(6, rotation.z); - update->setDouble(7, rotation.w); + IPropertyContents::Model model; + model.id = id; + model.lot = entity->GetLOT(); + model.position = position; + model.rotation = rotation; + model.ugcId = 0; - update->setInt64(8, id); - try { - update->executeUpdate(); - } catch (sql::SQLException& ex) { - Game::logger->Log("PropertyManagementComponent", "Error updating properties_contents. Error: %s", ex.what()); - } + Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name"); + } else { + Database::Get()->UpdateModelPositionRotation(id, position, rotation); } } - for (auto id : present) { - if (std::find(modelIds.begin(), modelIds.end(), id) != modelIds.end()) { + for (auto model : present) { + if (std::find(modelIds.begin(), modelIds.end(), model.id) != modelIds.end()) { continue; } - remove->setInt64(1, id); - try { - remove->execute(); - } catch (sql::SQLException& ex) { - Game::logger->Log("PropertyManagementComponent", "Error removing from properties_contents. Error %s", ex.what()); - } + Database::Get()->RemoveModel(model.id); } - - auto* removeUGC = Database::CreatePreppedStmt("DELETE FROM ugc WHERE id NOT IN (SELECT ugc_id FROM properties_contents);"); - - removeUGC->execute(); - - delete removeUGC; - delete insertion; - delete update; - delete lookup; - delete remove; } void PropertyManagementComponent::AddModel(LWOOBJID modelId, LWOOBJID spawnerId) { @@ -788,52 +685,33 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); const auto zoneId = worldId.GetMapID(); + const auto cloneId = worldId.GetCloneID(); - Game::logger->Log("Properties", "Getting property info for %d", zoneId); + LOG("Getting property info for %d", zoneId); GameMessages::PropertyDataMessage message = GameMessages::PropertyDataMessage(zoneId); const auto isClaimed = GetOwnerId() != LWOOBJID_EMPTY; LWOOBJID ownerId = GetOwnerId(); - std::string ownerName = ""; + std::string ownerName; + auto charInfo = Database::Get()->GetCharacterInfo(ownerId); + if (charInfo) ownerName = charInfo->name; std::string name = ""; std::string description = ""; uint64_t claimed = 0; char privacy = 0; if (isClaimed) { - const auto cloneId = worldId.GetCloneID(); - - auto* nameLookup = Database::CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;"); - nameLookup->setUInt64(1, cloneId); - - auto* nameResult = nameLookup->executeQuery(); - if (nameResult->next()) { - ownerName = nameResult->getString(1).c_str(); - } - - delete nameResult; - delete nameLookup; - name = propertyName; description = propertyDescription; claimed = claimedTime; privacy = static_cast(this->privacyOption); if (moderatorRequested) { - auto checkStatus = Database::CreatePreppedStmt("SELECT rejection_reason, mod_approved FROM properties WHERE id = ?;"); - - checkStatus->setInt64(1, propertyId); - - auto result = checkStatus->executeQuery(); - - result->next(); - - const auto reason = std::string(result->getString(1).c_str()); - const auto modApproved = result->getInt(2); - if (reason != "") { + auto moderationInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); + if (moderationInfo->rejectionReason != "") { moderatorRequested = false; - rejectionReason = reason; - } else if (reason == "" && modApproved == 1) { + rejectionReason = moderationInfo->rejectionReason; + } else if (moderationInfo->rejectionReason == "" && moderationInfo->modApproved == 1) { moderatorRequested = false; rejectionReason = ""; } else { diff --git a/dGame/dComponents/PropertyManagementComponent.h b/dGame/dComponents/PropertyManagementComponent.h index 2ee010a8..6a9ed09d 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -8,8 +8,7 @@ /** * Information regarding which players may visit this property */ -enum class PropertyPrivacyOption -{ +enum class PropertyPrivacyOption { /** * Default, only you can visit your property */ @@ -29,10 +28,9 @@ enum class PropertyPrivacyOption /** * Main component that handles interactions with a property, generally the plaques you see on properties. */ -class PropertyManagementComponent : public Component -{ +class PropertyManagementComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_MANAGEMENT; PropertyManagementComponent(Entity* parent); static PropertyManagementComponent* Instance(); @@ -164,6 +162,8 @@ public: LWOCLONEID GetCloneId() { return clone_Id; }; + LWOOBJID GetId() const noexcept { return propertyId; } + private: /** * This diff --git a/dGame/dComponents/PropertyVendorComponent.cpp b/dGame/dComponents/PropertyVendorComponent.cpp index cff4995c..502888d5 100644 --- a/dGame/dComponents/PropertyVendorComponent.cpp +++ b/dGame/dComponents/PropertyVendorComponent.cpp @@ -6,7 +6,7 @@ #include "EntityManager.h" #include "dZoneManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "PropertyManagementComponent.h" #include "UserManager.h" @@ -19,7 +19,7 @@ void PropertyVendorComponent::OnUse(Entity* originator) { OnQueryPropertyData(originator, originator->GetSystemAddress()); if (PropertyManagementComponent::Instance()->GetOwnerId() == LWOOBJID_EMPTY) { - Game::logger->Log("PropertyVendorComponent", "Property vendor opening!"); + LOG("Property vendor opening!"); GameMessages::SendOpenPropertyVendor(m_Parent->GetObjectID(), originator->GetSystemAddress()); @@ -37,7 +37,7 @@ void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool con if (PropertyManagementComponent::Instance() == nullptr) return; if (PropertyManagementComponent::Instance()->Claim(originator->GetObjectID()) == false) { - Game::logger->Log("PropertyVendorComponent", "FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu", originator->GetObjectID()); + LOG("FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu", originator->GetObjectID()); return; } @@ -51,6 +51,6 @@ void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool con PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, originator->GetSystemAddress()); - Game::logger->Log("PropertyVendorComponent", "Fired event; (%d) (%i) (%i)", confirmed, lot, count); + LOG("Fired event; (%d) (%i) (%i)", confirmed, lot, count); } diff --git a/dGame/dComponents/PropertyVendorComponent.h b/dGame/dComponents/PropertyVendorComponent.h index 5055b445..fee8af9c 100644 --- a/dGame/dComponents/PropertyVendorComponent.h +++ b/dGame/dComponents/PropertyVendorComponent.h @@ -7,10 +7,9 @@ /** * The property guard that stands on a property before it's claimed, allows entities to attempt claiming this property. */ -class PropertyVendorComponent : public Component -{ +class PropertyVendorComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROPERTY_VENDOR; explicit PropertyVendorComponent(Entity* parent); /** diff --git a/dGame/dComponents/ProximityMonitorComponent.cpp b/dGame/dComponents/ProximityMonitorComponent.cpp index acc93fde..fbac8ddb 100644 --- a/dGame/dComponents/ProximityMonitorComponent.cpp +++ b/dGame/dComponents/ProximityMonitorComponent.cpp @@ -18,7 +18,7 @@ ProximityMonitorComponent::~ProximityMonitorComponent() { for (const auto& en : m_ProximitiesData) { if (!en.second) continue; - dpWorld::Instance().RemoveEntity(en.second); + dpWorld::RemoveEntity(en.second); } m_ProximitiesData.clear(); @@ -28,12 +28,12 @@ void ProximityMonitorComponent::SetProximityRadius(float proxRadius, const std:: dpEntity* en = new dpEntity(m_Parent->GetObjectID(), proxRadius); en->SetPosition(m_Parent->GetPosition()); - dpWorld::Instance().AddEntity(en); + dpWorld::AddEntity(en); m_ProximitiesData.insert(std::make_pair(name, en)); } void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::string& name) { - dpWorld::Instance().AddEntity(entity); + dpWorld::AddEntity(entity); entity->SetPosition(m_Parent->GetPosition()); m_ProximitiesData.insert(std::make_pair(name, entity)); } diff --git a/dGame/dComponents/ProximityMonitorComponent.h b/dGame/dComponents/ProximityMonitorComponent.h index 2f51917d..512b2848 100644 --- a/dGame/dComponents/ProximityMonitorComponent.h +++ b/dGame/dComponents/ProximityMonitorComponent.h @@ -17,9 +17,9 @@ * Utility component for detecting how close entities are to named proximities for this entity. Allows you to store * proximity checks for multiple ojects. */ -class ProximityMonitorComponent : public Component { +class ProximityMonitorComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::PROXIMITY_MONITOR; ProximityMonitorComponent(Entity* parentEntity, int smallRadius = -1, int largeRadius = -1); ~ProximityMonitorComponent() override; diff --git a/dGame/dComponents/RebuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp similarity index 62% rename from dGame/dComponents/RebuildComponent.cpp rename to dGame/dComponents/QuickBuildComponent.cpp index e669489d..6cef87d3 100644 --- a/dGame/dComponents/RebuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -1,10 +1,10 @@ -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Entity.h" #include "DestroyableComponent.h" #include "GameMessages.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "CharacterComponent.h" #include "MissionComponent.h" #include "eMissionTaskType.h" @@ -14,7 +14,6 @@ #include "eGameActivity.h" #include "dServer.h" -#include "PacketUtils.h" #include "Spawner.h" #include "MovingPlatformComponent.h" #include "Preconditions.h" @@ -24,7 +23,7 @@ #include "CppScripts.h" -RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) { +QuickBuildComponent::QuickBuildComponent(Entity* const entity) : Component{ entity } { std::u16string checkPreconditions = entity->GetVar(u"CheckPrecondition"); if (!checkPreconditions.empty()) { @@ -34,30 +33,29 @@ RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) { // Should a setting that has the build activator position exist, fetch that setting here and parse it for position. // It is assumed that the user who sets this setting uses the correct character delimiter (character 31 or in hex 0x1F) auto positionAsVector = GeneralUtils::SplitString(m_Parent->GetVarAsString(u"rebuild_activators"), 0x1F); - if (positionAsVector.size() == 3 && - GeneralUtils::TryParse(positionAsVector[0], m_ActivatorPosition.x) && - GeneralUtils::TryParse(positionAsVector[1], m_ActivatorPosition.y) && - GeneralUtils::TryParse(positionAsVector[2], m_ActivatorPosition.z)) { + const auto activatorPositionValid = GeneralUtils::TryParse(positionAsVector); + if (positionAsVector.size() == 3 && activatorPositionValid) { + m_ActivatorPosition = activatorPositionValid.value(); } else { - Game::logger->Log("RebuildComponent", "Failed to find activator position for lot %i. Defaulting to parents position.", m_Parent->GetLOT()); + LOG("Failed to find activator position for lot %i. Defaulting to parents position.", m_Parent->GetLOT()); m_ActivatorPosition = m_Parent->GetPosition(); } SpawnActivator(); } -RebuildComponent::~RebuildComponent() { +QuickBuildComponent::~QuickBuildComponent() { delete m_Precondition; Entity* builder = GetBuilder(); if (builder) { - CancelRebuild(builder, eQuickBuildFailReason::BUILD_ENDED, true); + CancelQuickBuild(builder, eQuickBuildFailReason::BUILD_ENDED, true); } DespawnActivator(); } -void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void QuickBuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (m_Parent->GetComponent(eReplicaComponentType::DESTROYABLE) == nullptr) { if (bIsInitialUpdate) { outBitStream->Write(false); @@ -70,7 +68,7 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia // If build state is completed and we've already serialized once in the completed state, // don't serializing this component anymore as this will cause the build to jump again. // If state changes, serialization will begin again. - if (!m_StateDirty && m_State == eRebuildState::COMPLETED) { + if (!m_StateDirty && m_State == eQuickBuildState::COMPLETED) { outBitStream->Write0(); outBitStream->Write0(); return; @@ -81,14 +79,14 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia Entity* builder = GetBuilder(); if (builder) { - outBitStream->Write((uint32_t)1); + outBitStream->Write(1); outBitStream->Write(builder->GetObjectID()); for (int i = 0; i < 10; i++) { outBitStream->Write(0.0f); } } else { - outBitStream->Write((uint32_t)0); + outBitStream->Write(0); } // END Scripted Activity @@ -110,7 +108,7 @@ void RebuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitia m_StateDirty = false; } -void RebuildComponent::Update(float deltaTime) { +void QuickBuildComponent::Update(float deltaTime) { m_Activator = GetActivator(); // Serialize the quickbuild every so often, fixes the odd bug where the quickbuild is not buildable @@ -124,7 +122,7 @@ void RebuildComponent::Update(float deltaTime) { }*/ switch (m_State) { - case eRebuildState::OPEN: { + case eQuickBuildState::OPEN: { SpawnActivator(); m_TimeBeforeDrain = 0; @@ -147,14 +145,14 @@ void RebuildComponent::Update(float deltaTime) { GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); - ResetRebuild(false); + ResetQuickBuild(false); } } } break; } - case eRebuildState::COMPLETED: { + case eQuickBuildState::COMPLETED: { m_Timer += deltaTime; // For reset times < 0 this has to be handled manually @@ -171,17 +169,17 @@ void RebuildComponent::Update(float deltaTime) { GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); - ResetRebuild(false); + ResetQuickBuild(false); } } break; } - case eRebuildState::BUILDING: + case eQuickBuildState::BUILDING: { Entity* builder = GetBuilder(); if (!builder) { - ResetRebuild(false); + ResetQuickBuild(false); return; } @@ -197,27 +195,26 @@ void RebuildComponent::Update(float deltaTime) { DestroyableComponent* destComp = builder->GetComponent(); if (!destComp) break; - int newImagination = destComp->GetImagination(); - ++m_DrainedImagination; - --newImagination; + const int32_t imaginationCostRemaining = m_TakeImagination - m_DrainedImagination; + + const int32_t newImagination = destComp->GetImagination() - 1; destComp->SetImagination(newImagination); Game::entityManager->SerializeEntity(builder); - if (newImagination <= 0) { - CancelRebuild(builder, eQuickBuildFailReason::OUT_OF_IMAGINATION, true); + if (newImagination <= 0 && imaginationCostRemaining > 0) { + CancelQuickBuild(builder, eQuickBuildFailReason::OUT_OF_IMAGINATION, true); break; } - } if (m_Timer >= m_CompleteTime && m_DrainedImagination >= m_TakeImagination) { - CompleteRebuild(builder); + CompleteQuickBuild(builder); } break; } - case eRebuildState::INCOMPLETE: { + case eQuickBuildState::INCOMPLETE: { m_TimerIncomplete += deltaTime; // For reset times < 0 this has to be handled manually @@ -233,17 +230,17 @@ void RebuildComponent::Update(float deltaTime) { GameMessages::SendDieNoImplCode(m_Parent, LWOOBJID_EMPTY, LWOOBJID_EMPTY, eKillType::VIOLENT, u"", 0.0f, 0.0f, 0.0f, false, true); - ResetRebuild(false); + ResetQuickBuild(false); } } break; } - case eRebuildState::RESETTING: break; + case eQuickBuildState::RESETTING: break; } } -void RebuildComponent::OnUse(Entity* originator) { - if (GetBuilder() != nullptr || m_State == eRebuildState::COMPLETED) { +void QuickBuildComponent::OnUse(Entity* originator) { + if (GetBuilder() != nullptr || m_State == eQuickBuildState::COMPLETED) { return; } @@ -251,17 +248,17 @@ void RebuildComponent::OnUse(Entity* originator) { return; } - StartRebuild(originator); + StartQuickBuild(originator); } -void RebuildComponent::SpawnActivator() { - if (!m_SelfActivator || m_ActivatorPosition != NiPoint3::ZERO) { +void QuickBuildComponent::SpawnActivator() { + if (!m_SelfActivator || m_ActivatorPosition != NiPoint3Constant::ZERO) { if (!m_Activator) { EntityInfo info; info.lot = 6604; info.spawnerID = m_Parent->GetObjectID(); - info.pos = m_ActivatorPosition == NiPoint3::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition; + info.pos = m_ActivatorPosition == NiPoint3Constant::ZERO ? m_Parent->GetPosition() : m_ActivatorPosition; m_Activator = Game::entityManager->CreateEntity(info, nullptr, m_Parent); if (m_Activator) { @@ -274,7 +271,7 @@ void RebuildComponent::SpawnActivator() { } } -void RebuildComponent::DespawnActivator() { +void QuickBuildComponent::DespawnActivator() { if (m_Activator) { Game::entityManager->DestructEntity(m_Activator); @@ -286,73 +283,73 @@ void RebuildComponent::DespawnActivator() { } } -Entity* RebuildComponent::GetActivator() { +Entity* QuickBuildComponent::GetActivator() const { return Game::entityManager->GetEntity(m_ActivatorId); } -NiPoint3 RebuildComponent::GetActivatorPosition() { +NiPoint3 QuickBuildComponent::GetActivatorPosition() const noexcept { return m_ActivatorPosition; } -float RebuildComponent::GetResetTime() { +float QuickBuildComponent::GetResetTime() const noexcept { return m_ResetTime; } -float RebuildComponent::GetCompleteTime() { +float QuickBuildComponent::GetCompleteTime() const noexcept { return m_CompleteTime; } -int RebuildComponent::GetTakeImagination() { +int32_t QuickBuildComponent::GetTakeImagination() const noexcept { return m_TakeImagination; } -bool RebuildComponent::GetInterruptible() { +bool QuickBuildComponent::GetInterruptible() const noexcept { return m_Interruptible; } -bool RebuildComponent::GetSelfActivator() { +bool QuickBuildComponent::GetSelfActivator() const noexcept { return m_SelfActivator; } -std::vector RebuildComponent::GetCustomModules() { +std::vector QuickBuildComponent::GetCustomModules() const noexcept { return m_CustomModules; } -int RebuildComponent::GetActivityId() { +int32_t QuickBuildComponent::GetActivityId() const noexcept { return m_ActivityId; } -int RebuildComponent::GetPostImaginationCost() { +int32_t QuickBuildComponent::GetPostImaginationCost() const noexcept { return m_PostImaginationCost; } -float RebuildComponent::GetTimeBeforeSmash() { +float QuickBuildComponent::GetTimeBeforeSmash() const noexcept { return m_TimeBeforeSmash; } -eRebuildState RebuildComponent::GetState() { +eQuickBuildState QuickBuildComponent::GetState() const noexcept { return m_State; } -Entity* RebuildComponent::GetBuilder() const { - auto* builder = Game::entityManager->GetEntity(m_Builder); +Entity* QuickBuildComponent::GetBuilder() const { + auto* const builder = Game::entityManager->GetEntity(m_Builder); return builder; } -bool RebuildComponent::GetRepositionPlayer() const { +bool QuickBuildComponent::GetRepositionPlayer() const noexcept { return m_RepositionPlayer; } -void RebuildComponent::SetActivatorPosition(NiPoint3 value) { +void QuickBuildComponent::SetActivatorPosition(const NiPoint3& value) noexcept { m_ActivatorPosition = value; } -void RebuildComponent::SetResetTime(float value) { +void QuickBuildComponent::SetResetTime(const float value) noexcept { m_ResetTime = value; } -void RebuildComponent::SetCompleteTime(float value) { +void QuickBuildComponent::SetCompleteTime(const float value) noexcept { if (value < 0) { m_CompleteTime = 4.5f; } else { @@ -360,31 +357,31 @@ void RebuildComponent::SetCompleteTime(float value) { } } -void RebuildComponent::SetTakeImagination(int value) { +void QuickBuildComponent::SetTakeImagination(const int32_t value) noexcept { m_TakeImagination = value; } -void RebuildComponent::SetInterruptible(bool value) { +void QuickBuildComponent::SetInterruptible(const bool value) noexcept { m_Interruptible = value; } -void RebuildComponent::SetSelfActivator(bool value) { +void QuickBuildComponent::SetSelfActivator(const bool value) noexcept { m_SelfActivator = value; } -void RebuildComponent::SetCustomModules(std::vector value) { +void QuickBuildComponent::SetCustomModules(const std::vector& value) noexcept { m_CustomModules = value; } -void RebuildComponent::SetActivityId(int value) { +void QuickBuildComponent::SetActivityId(const int32_t value) noexcept { m_ActivityId = value; } -void RebuildComponent::SetPostImaginationCost(int value) { +void QuickBuildComponent::SetPostImaginationCost(const int32_t value) noexcept { m_PostImaginationCost = value; } -void RebuildComponent::SetTimeBeforeSmash(float value) { +void QuickBuildComponent::SetTimeBeforeSmash(const float value) noexcept { if (value < 0) { m_TimeBeforeSmash = 10.0f; } else { @@ -392,12 +389,12 @@ void RebuildComponent::SetTimeBeforeSmash(float value) { } } -void RebuildComponent::SetRepositionPlayer(bool value) { +void QuickBuildComponent::SetRepositionPlayer(const bool value) noexcept { m_RepositionPlayer = value; } -void RebuildComponent::StartRebuild(Entity* user) { - if (m_State == eRebuildState::OPEN || m_State == eRebuildState::COMPLETED || m_State == eRebuildState::INCOMPLETE) { +void QuickBuildComponent::StartQuickBuild(Entity* const user) { + if (m_State == eQuickBuildState::OPEN || m_State == eQuickBuildState::COMPLETED || m_State == eQuickBuildState::INCOMPLETE) { m_Builder = user->GetObjectID(); auto* character = user->GetComponent(); @@ -405,53 +402,51 @@ void RebuildComponent::StartRebuild(Entity* user) { Game::entityManager->SerializeEntity(user); - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::BUILDING, user->GetObjectID()); - GameMessages::SendEnableRebuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID()); + GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::BUILDING, user->GetObjectID()); + GameMessages::SendEnableQuickBuild(m_Parent, true, false, false, eQuickBuildFailReason::NOT_GIVEN, 0.0f, user->GetObjectID()); - m_State = eRebuildState::BUILDING; + m_State = eQuickBuildState::BUILDING; m_StateDirty = true; Game::entityManager->SerializeEntity(m_Parent); auto* movingPlatform = m_Parent->GetComponent(); if (movingPlatform != nullptr) { - movingPlatform->OnRebuildInitilized(); + movingPlatform->OnQuickBuildInitilized(); } for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnRebuildStart(m_Parent, user); + script->OnQuickBuildStart(m_Parent, user); } // Notify scripts and possible subscribers for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) + script->OnQuickBuildNotifyState(m_Parent, m_State); + for (const auto& cb : m_QuickBuildStateCallbacks) cb(m_State); } } -void RebuildComponent::CompleteRebuild(Entity* user) { - if (user == nullptr) { - return; - } +void QuickBuildComponent::CompleteQuickBuild(Entity* const user) { + if (!user) return; auto* characterComponent = user->GetComponent(); if (characterComponent != nullptr) { characterComponent->SetCurrentActivity(eGameActivity::NONE); - characterComponent->TrackRebuildComplete(); + characterComponent->TrackQuickBuildComplete(); } else { - Game::logger->Log("RebuildComponent", "Some user tried to finish the rebuild but they didn't have a character somehow."); + LOG("Some user tried to finish the rebuild but they didn't have a character somehow."); return; } Game::entityManager->SerializeEntity(user); - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::COMPLETED, user->GetObjectID()); + GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::COMPLETED, user->GetObjectID()); GameMessages::SendPlayFXEffect(m_Parent, 507, u"create", "BrickFadeUpVisCompleteEffect", LWOOBJID_EMPTY, 0.4f, 1.0f, true); - GameMessages::SendEnableRebuild(m_Parent, false, false, true, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, user->GetObjectID()); + GameMessages::SendEnableQuickBuild(m_Parent, false, false, true, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, user->GetObjectID()); GameMessages::SendTerminateInteraction(user->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); - m_State = eRebuildState::COMPLETED; + m_State = eQuickBuildState::COMPLETED; m_StateDirty = true; m_Timer = 0.0f; m_DrainedImagination = 0; @@ -486,26 +481,26 @@ void RebuildComponent::CompleteRebuild(Entity* user) { auto* missionComponent = builder->GetComponent(); if (missionComponent) missionComponent->Progress(eMissionTaskType::ACTIVITY, m_ActivityId); } - LootGenerator::Instance().DropActivityLoot(builder, m_Parent, m_ActivityId, 1); + Loot::DropActivityLoot(builder, m_Parent, m_ActivityId, 1); } // Notify scripts for (auto* script : CppScripts::GetEntityScripts(m_Parent)) { - script->OnRebuildComplete(m_Parent, user); - script->OnRebuildNotifyState(m_Parent, m_State); + script->OnQuickBuildComplete(m_Parent, user); + script->OnQuickBuildNotifyState(m_Parent, m_State); } // Notify subscribers - for (const auto& callback : m_RebuildStateCallbacks) + for (const auto& callback : m_QuickBuildStateCallbacks) callback(m_State); - for (const auto& callback : m_RebuildCompleteCallbacks) + for (const auto& callback : m_QuickBuildCompleteCallbacks) callback(user); m_Parent->TriggerEvent(eTriggerEventType::REBUILD_COMPLETE, user); auto* movingPlatform = m_Parent->GetComponent(); if (movingPlatform != nullptr) { - movingPlatform->OnCompleteRebuild(); + movingPlatform->OnCompleteQuickBuild(); } // Set flag @@ -521,20 +516,20 @@ void RebuildComponent::CompleteRebuild(Entity* user) { RenderComponent::PlayAnimation(user, u"rebuild-celebrate", 1.09f); } -void RebuildComponent::ResetRebuild(bool failed) { +void QuickBuildComponent::ResetQuickBuild(const bool failed) { Entity* builder = GetBuilder(); - if (m_State == eRebuildState::BUILDING && builder) { - GameMessages::SendEnableRebuild(m_Parent, false, false, failed, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, builder->GetObjectID()); + if (m_State == eQuickBuildState::BUILDING && builder) { + GameMessages::SendEnableQuickBuild(m_Parent, false, false, failed, eQuickBuildFailReason::NOT_GIVEN, m_ResetTime, builder->GetObjectID()); if (failed) { RenderComponent::PlayAnimation(builder, u"rebuild-fail"); } } - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::RESETTING, LWOOBJID_EMPTY); + GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::RESETTING, LWOOBJID_EMPTY); - m_State = eRebuildState::RESETTING; + m_State = eQuickBuildState::RESETTING; m_StateDirty = true; m_Timer = 0.0f; m_TimerIncomplete = 0.0f; @@ -545,8 +540,8 @@ void RebuildComponent::ResetRebuild(bool failed) { // Notify scripts and possible subscribers for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) + script->OnQuickBuildNotifyState(m_Parent, m_State); + for (const auto& cb : m_QuickBuildStateCallbacks) cb(m_State); m_Parent->ScheduleKillAfterUpdate(); @@ -556,37 +551,35 @@ void RebuildComponent::ResetRebuild(bool failed) { } } -void RebuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failReason, bool skipChecks) { - if (m_State != eRebuildState::COMPLETED || skipChecks) { +void QuickBuildComponent::CancelQuickBuild(Entity* const entity, const eQuickBuildFailReason failReason, const bool skipChecks) { + if (m_State != eQuickBuildState::COMPLETED || skipChecks) { m_Builder = LWOOBJID_EMPTY; const auto entityID = entity != nullptr ? entity->GetObjectID() : LWOOBJID_EMPTY; // Notify the client that a state has changed - GameMessages::SendRebuildNotifyState(m_Parent, m_State, eRebuildState::INCOMPLETE, entityID); - GameMessages::SendEnableRebuild(m_Parent, false, true, false, failReason, m_Timer, entityID); + GameMessages::SendQuickBuildNotifyState(m_Parent, m_State, eQuickBuildState::INCOMPLETE, entityID); + GameMessages::SendEnableQuickBuild(m_Parent, false, true, false, failReason, m_Timer, entityID); // Now terminate any interaction with the rebuild GameMessages::SendTerminateInteraction(entityID, eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); GameMessages::SendTerminateInteraction(m_Parent->GetObjectID(), eTerminateType::FROM_INTERACTION, m_Parent->GetObjectID()); // Now update the component itself - m_State = eRebuildState::INCOMPLETE; + m_State = eQuickBuildState::INCOMPLETE; m_StateDirty = true; // Notify scripts and possible subscribers for (auto* script : CppScripts::GetEntityScripts(m_Parent)) - script->OnRebuildNotifyState(m_Parent, m_State); - for (const auto& cb : m_RebuildStateCallbacks) + script->OnQuickBuildNotifyState(m_Parent, m_State); + for (const auto& cb : m_QuickBuildStateCallbacks) cb(m_State); Game::entityManager->SerializeEntity(m_Parent); } - if (entity == nullptr) { - return; - } + if (!entity) return; CharacterComponent* characterComponent = entity->GetComponent(); if (characterComponent) { @@ -595,10 +588,10 @@ void RebuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failR } } -void RebuildComponent::AddRebuildCompleteCallback(const std::function& callback) { - m_RebuildCompleteCallbacks.push_back(callback); +void QuickBuildComponent::AddQuickBuildCompleteCallback(const std::function& callback) { + m_QuickBuildCompleteCallbacks.push_back(callback); } -void RebuildComponent::AddRebuildStateCallback(const std::function& callback) { - m_RebuildStateCallbacks.push_back(callback); +void QuickBuildComponent::AddQuickBuildStateCallback(const std::function& callback) { + m_QuickBuildStateCallbacks.push_back(callback); } diff --git a/dGame/dComponents/QuickBuildComponent.h b/dGame/dComponents/QuickBuildComponent.h new file mode 100644 index 00000000..556264a8 --- /dev/null +++ b/dGame/dComponents/QuickBuildComponent.h @@ -0,0 +1,365 @@ +#ifndef QUICKBUILDCOMPONENT_H +#define QUICKBUILDCOMPONENT_H + +#include "BitStream.h" +#include +#include +#include "dCommonVars.h" +#include "NiPoint3.h" +#include "ScriptedActivityComponent.h" +#include "Preconditions.h" +#include "Component.h" +#include "eReplicaComponentType.h" +#include "eQuickBuildState.h" + +class Entity; +enum class eQuickBuildFailReason : uint32_t; + +/** + * Component that handles entities that can be built into other entities using the quick build mechanic. Generally + * consists of an activator that shows a popup and then the actual entity that the bricks are built into. Note + * that quick builds are also scripted activities so this shared some logic with the ScriptedActivityComponent. + */ +class QuickBuildComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; + + QuickBuildComponent(Entity* const entity); + ~QuickBuildComponent() override; + + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + void Update(float deltaTime) override; + + /** + * Handles a OnUse event from some entity, initiating the quick build + * @param originator the entity that triggered the event + */ + void OnUse(Entity* originator) override; + + /** + * Spawns the activator that can be used to initiate the quickbuild + */ + void SpawnActivator(); + + /** + * Despawns the activiator that can be used to initiate the quickbuild + */ + void DespawnActivator(); + + /** + * Returns the entity that acts as the activator for this quickbuild + * @return the entity that acts as the activator for this quickbuild + */ + [[nodiscard]] Entity* GetActivator() const; + + /** + * Returns the spawn position of the activator for this quickbuild, if any + * @return the spawn position of the activator for this quickbuild, if any + */ + [[nodiscard]] NiPoint3 GetActivatorPosition() const noexcept; + + /** + * Sets the spawn position for the activator of this quickbuild + * @param value the spawn position to set for the activator + */ + void SetActivatorPosition(const NiPoint3& value) noexcept; + + /** + * Returns the time it takes for the quickbuild to reset after being built + * @return the time it takes for the quickbuild to reset after being built + */ + [[nodiscard]] float GetResetTime() const noexcept; + + /** + * Sets the time it takes for the quickbuild to reset after being built + * @param value the reset time to set + */ + void SetResetTime(const float value) noexcept; + + /** + * Returns the time it takes to complete the quickbuild + * @return the time it takes to complete the quickbuild + */ + [[nodiscard]] float GetCompleteTime() const noexcept; + + /** + * Sets the time it takes to complete the quickbuild + * @param value the completion time to set + */ + void SetCompleteTime(const float value) noexcept; + + /** + * Returns the imagination that's taken when completing the quickbuild + * @return the imagination that's taken when completing the quickbuild + */ + [[nodiscard]] int32_t GetTakeImagination() const noexcept; + + /** + * Sets the imagination that's taken when completing the quickbuild + * @param value the imagination deduction to set + */ + void SetTakeImagination(const int32_t value) noexcept; + + /** + * Returns if the quickbuild can be interrupted, currently unused + * @return if the quickbuild can be interrupted + */ + [[nodiscard]] bool GetInterruptible() const noexcept; + + /** + * Sets whether or not the quickbuild can be interrupted, currently unused + * @param value true if the quickbuild may be interrupted, false otherwise + */ + void SetInterruptible(const bool value) noexcept; + + /** + * Returns whether or not this entity contains a built-in activator + * @return whether or not this entity contains a built-in activator + */ + [[nodiscard]] bool GetSelfActivator() const noexcept; + + /** + * Sets whether or not this entity contains a built-in activator. If set to false this will spawn activators on + * each new quickbuild. + * @param value whether or not this entity contains a built-in activator + */ + void SetSelfActivator(const bool value) noexcept; + + /** + * Currently unused + */ + [[nodiscard]] std::vector GetCustomModules() const noexcept; + + /** + * Currently unused + */ + void SetCustomModules(const std::vector& value) noexcept; + + /** + * Returns the activity ID for participating in this quickbuild + * @return the activity ID for participating in this quickbuild + */ + [[nodiscard]] int32_t GetActivityId() const noexcept; + + /** + * Sets the activity ID for participating in this quickbuild + * @param value the activity ID to set + */ + void SetActivityId(const int32_t value) noexcept; + + /** + * Currently unused + */ + [[nodiscard]] int32_t GetPostImaginationCost() const noexcept; + + /** + * Currently unused + */ + void SetPostImaginationCost(const int32_t value) noexcept; + + /** + * Returns the time it takes for an incomplete quickbuild to be smashed automatically + * @return the time it takes for an incomplete quickbuild to be smashed automatically + */ + [[nodiscard]] float GetTimeBeforeSmash() const noexcept; + + /** + * Sets the time it takes for an incomplete quickbuild to be smashed automatically + * @param value the time to set + */ + void SetTimeBeforeSmash(const float value) noexcept; + + /** + * Returns the current quickbuild state + * @return the current quickbuild state + */ + [[nodiscard]] eQuickBuildState GetState() const noexcept; + + /** + * Returns the player that is currently building this quickbuild + * @return the player that is currently building this quickbuild + */ + [[nodiscard]] Entity* GetBuilder() const; + + /** + * Returns whether or not the player is repositioned when initiating the quickbuild + * @return whether or not the player is repositioned when initiating the quickbuild + */ + [[nodiscard]] bool GetRepositionPlayer() const noexcept; + + /** + * Sets whether or not the player is repositioned when initiating the quickbuild + * @param value whether or not the player is repositioned when initiating the quickbuild + */ + void SetRepositionPlayer(const bool value) noexcept; + + /** + * Adds a callback that is called when the quickbuild is completed + * @param callback the callback to add + */ + void AddQuickBuildCompleteCallback(const std::function& callback); + + /** + * Adds a callback when the quickbuild state is updated + * @param callback the callback to add + */ + void AddQuickBuildStateCallback(const std::function& callback); + + /** + * Resets the quickbuild + * @param failed whether or not the player failed to complete the quickbuild, triggers an extra animation + */ + void ResetQuickBuild(const bool failed); + + /** + * Cancels the quickbuild if it wasn't completed + * @param builder the player that's currently building + * @param failReason the reason the quickbuild was cancelled + * @param skipChecks whether or not to skip the check for the quickbuild not being completed + */ + void CancelQuickBuild(Entity* const builder, const eQuickBuildFailReason failReason, const bool skipChecks = false); +private: + /** + * Whether or not the quickbuild state has been changed since we last serialized it. + */ + bool m_StateDirty = true; + + /** + * The state the quickbuild is currently in + */ + eQuickBuildState m_State = eQuickBuildState::OPEN; + + /** + * The time that has passed since initiating the quickbuild + */ + float m_Timer = 0; + + /** + * The time that has passed before completing the quickbuild + */ + float m_TimerIncomplete = 0; + + /** + * The position that the quickbuild activator is spawned at + */ + NiPoint3 m_ActivatorPosition = NiPoint3Constant::ZERO; + + /** + * The entity that represents the quickbuild activator + */ + Entity* m_Activator = nullptr; + + /** + * The ID of the entity that represents the quickbuild activator + */ + LWOOBJID m_ActivatorId = LWOOBJID_EMPTY; + + /** + * Triggers the blinking that indicates that the quickbuild is resetting + */ + bool m_ShowResetEffect = false; + + /** + * Currently unused + */ + float m_Taken = 0; + + /** + * The callbacks that are called when the quickbuild is completed + */ + std::vector> m_QuickBuildCompleteCallbacks{}; + + /** + * The callbacks that are called when the quickbuild state is updated + */ + std::vector> m_QuickBuildStateCallbacks{}; + + /** + * The time it takes for the quickbuild to reset after being completed + */ + float m_ResetTime = 0; + + /** + * The time it takes to complete the quickbuild + */ + float m_CompleteTime = 0; + + /** + * The imagination that's deducted when completing the quickbuild + */ + int32_t m_TakeImagination = 0; + + /** + * Currently unused + */ + bool m_Interruptible = false; + + /** + * Whether or not this quickbuild entity also has an activator attached. If not a new one will be spawned + */ + bool m_SelfActivator = false; + + /** + * Currently unused + */ + std::vector m_CustomModules{}; + + /** + * The activity ID that players partake in when doing this quickbuild + */ + int32_t m_ActivityId = 0; + + /** + * Currently unused + */ + int32_t m_PostImaginationCost = 0; + + /** + * The time it takes for the quickbuild to reset when it's not completed yet + */ + float m_TimeBeforeSmash = 0; + + /** + * The time it takes to drain imagination + */ + float m_TimeBeforeDrain = 0; + + /** + * The amount of imagination that was drained when building this quickbuild + */ + int32_t m_DrainedImagination = 0; + + /** + * Whether to reposition the player or not when building + */ + bool m_RepositionPlayer = true; + + /** + * Currently unused + */ + int32_t m_SoftTimer = 0; + + /** + * The ID of the entity that's currently building the quickbuild + */ + LWOOBJID m_Builder = LWOOBJID_EMPTY; + + /** + * Preconditions to be met before being able to start the quickbuild + */ + PreconditionExpression* m_Precondition = nullptr; + + /** + * Starts the quickbuild for a certain entity + * @param user the entity to start the quickbuild + */ + void StartQuickBuild(Entity* const user); + + /** + * Completes the quickbuild for an entity, dropping loot and despawning the activator + * @param user the entity that completed the quickbuild + */ + void CompleteQuickBuild(Entity* const user); +}; + +#endif // QUICKBUILDCOMPONENT_H diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index 5de24445..f3772aad 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -12,12 +12,11 @@ #include "Item.h" #include "MissionComponent.h" #include "ModuleAssemblyComponent.h" -#include "Player.h" #include "PossessableComponent.h" #include "PossessorComponent.h" #include "eRacingTaskParam.h" #include "Spawner.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "dServer.h" #include "dZoneManager.h" #include "dConfig.h" @@ -26,6 +25,7 @@ #include "LeaderboardManager.h" #include "dZoneManager.h" #include "CDActivitiesTable.h" +#include #ifndef M_PI #define M_PI 3.14159265358979323846264338327950288 @@ -50,10 +50,10 @@ RacingControlComponent::RacingControlComponent(Entity* parent) m_MainWorld = 1200; const auto worldID = Game::server->GetZoneID(); - if (Game::zoneManager->CheckIfAccessibleZone((worldID/10)*10)) m_MainWorld = (worldID/10)*10; + if (Game::zoneManager->CheckIfAccessibleZone((worldID / 10) * 10)) m_MainWorld = (worldID / 10) * 10; m_ActivityID = 42; - CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); + CDActivitiesTable* activitiesTable = CDClientManager::GetTable(); std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.instanceMapID == worldID); }); for (CDActivities activity : activities) m_ActivityID = activity.ActivityID; } @@ -61,23 +61,25 @@ RacingControlComponent::RacingControlComponent(Entity* parent) RacingControlComponent::~RacingControlComponent() {} void RacingControlComponent::OnPlayerLoaded(Entity* player) { - // If the race has already started, send the player back to the main world. - if (m_Loaded) { - auto* playerInstance = dynamic_cast(player); - - playerInstance->SendToZone(m_MainWorld); - + auto* inventoryComponent = player->GetComponent(); + if (!inventoryComponent) { return; } - const auto objectID = player->GetObjectID(); + auto* vehicle = inventoryComponent->FindItemByLot(8092); + + // If the race has already started, send the player back to the main world. + if (m_Loaded || !vehicle) { + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); + return; + } m_LoadedPlayers++; - Game::logger->Log("RacingControlComponent", "Loading player %i", + LOG("Loading player %i", m_LoadedPlayers); - - m_LobbyPlayers.push_back(objectID); + m_LobbyPlayers.push_back(player->GetObjectID()); } void RacingControlComponent::LoadPlayerVehicle(Entity* player, @@ -99,9 +101,15 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, auto* item = inventoryComponent->FindItemByLot(8092); if (item == nullptr) { - Game::logger->Log("RacingControlComponent", "Failed to find item"); + LOG("Failed to find item"); + auto* characterComponent = player->GetComponent(); + if (characterComponent) { + m_LoadedPlayers--; + characterComponent->SendToZone(m_MainWorld); + } return; + } // Calculate the vehicle's starting position. @@ -110,8 +118,8 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, GeneralUtils::UTF16ToWTF8(m_PathName)); auto spawnPointEntities = Game::entityManager->GetEntitiesByLOT(4843); - auto startPosition = NiPoint3::ZERO; - auto startRotation = NiQuaternion::IDENTITY; + auto startPosition = NiPoint3Constant::ZERO; + auto startRotation = NiQuaternionConstant::IDENTITY; const std::string placementAsString = std::to_string(positionNumber); for (auto entity : spawnPointEntities) { if (!entity) continue; @@ -203,6 +211,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, 0, 0, 0 }); + m_AllPlayersReady = false; } // Construct and serialize everything when done. @@ -301,7 +310,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) { } // Respawn the player in 2 seconds, as was done in live. Not sure if this value is in a setting somewhere else... - vehicle->AddCallbackTimer(2.0f, [=]() { + vehicle->AddCallbackTimer(2.0f, [=, this]() { if (!vehicle || !this->m_Parent) return; GameMessages::SendRacingResetPlayerToLastReset( m_Parent->GetObjectID(), racingPlayer.playerID, @@ -320,7 +329,7 @@ void RacingControlComponent::OnRequestDie(Entity* player) { // Reset imagination to half its current value, rounded up to the nearest value divisible by 10, as it was done in live. if (destroyableComponent) destroyableComponent->SetImagination(respawnImagination); Game::entityManager->SerializeEntity(vehicle); - }); + }); auto* characterComponent = player->GetComponent(); if (characterComponent != nullptr) { @@ -374,12 +383,12 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu // Calculate the score, different loot depending on player count auto playersRating = m_LoadedPlayers; - if(m_LoadedPlayers == 1 && m_SoloRacing) { + if (m_LoadedPlayers == 1 && m_SoloRacing) { playersRating *= 2; } - const auto score = playersRating * 10 + data->finished; - LootGenerator::Instance().GiveActivityLoot(player, m_Parent, m_ActivityID, score); + const auto score = playersRating * 10 + data->finished; + Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score); // Giving rewards GameMessages::SendNotifyRacingClient( @@ -390,18 +399,18 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu if (missionComponent == nullptr) return; - missionComponent->Progress(eMissionTaskType::RACING, 0, (LWOOBJID)eRacingTaskParam::COMPETED_IN_RACE); // Progress task for competing in a race - missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, (LWOOBJID)eRacingTaskParam::SAFE_DRIVER); // Finish a race without being smashed. + missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race + missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast(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, (LWOOBJID)eRacingTaskParam::FINISH_WITH_PLACEMENT); // Finish in 1st place on a race + missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race if (data->finished == 1) { - missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS); // Finish in 1st place on multiple tracks. - missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::WIN_RACE_IN_WORLD); // Finished first place in specific world. + missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks. + missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world. } if (data->finished == m_LoadedPlayers) { - missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), (LWOOBJID)eRacingTaskParam::LAST_PLACE_FINISH); // Finished first place in specific world. + missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world. } } } else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) { @@ -416,76 +425,92 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu m_Parent->GetObjectID(), 3, 0, LWOOBJID_EMPTY, u"", player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS); - auto* playerInstance = dynamic_cast(player); + auto* characterComponent = player->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); vehicle->Kill(); } } -void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, - bool bIsInitialUpdate, - unsigned int& flags) { +void RacingControlComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { // BEGIN Scripted Activity - outBitStream->Write1(); - outBitStream->Write(static_cast(m_RacingPlayers.size())); + outBitStream->Write(m_RacingPlayers.size()); for (const auto& player : m_RacingPlayers) { outBitStream->Write(player.playerID); - for (int i = 0; i < 10; i++) { - outBitStream->Write(player.data[i]); - } + outBitStream->Write(player.data[0]); + if (player.finished != 0) outBitStream->Write(player.raceTime); + else outBitStream->Write(player.data[1]); + if (player.finished != 0) outBitStream->Write(player.bestLapTime); + else outBitStream->Write(player.data[2]); + if (player.finished == 1) outBitStream->Write(1.0f); + else outBitStream->Write(player.data[3]); + outBitStream->Write(player.data[4]); + outBitStream->Write(player.data[5]); + outBitStream->Write(player.data[6]); + outBitStream->Write(player.data[7]); + outBitStream->Write(player.data[8]); + outBitStream->Write(player.data[9]); } // END Scripted Activity - outBitStream->Write1(); // Dirty? - outBitStream->Write(static_cast(m_RacingPlayers.size())); + outBitStream->Write1(); + outBitStream->Write(m_RacingPlayers.size()); - outBitStream->Write(!m_RacingPlayers.empty()); - if (!m_RacingPlayers.empty()) { + outBitStream->Write(!m_AllPlayersReady); + if (!m_AllPlayersReady) { + int32_t numReady = 0; for (const auto& player : m_RacingPlayers) { - outBitStream->Write1(); // Has more date - + outBitStream->Write1(); // Has more player data outBitStream->Write(player.playerID); outBitStream->Write(player.vehicleID); outBitStream->Write(player.playerIndex); outBitStream->Write(player.playerLoaded); + if (player.playerLoaded) numReady++; } outBitStream->Write0(); // No more data + if (numReady == m_RacingPlayers.size()) m_AllPlayersReady = true; } outBitStream->Write(!m_RacingPlayers.empty()); if (!m_RacingPlayers.empty()) { for (const auto& player : m_RacingPlayers) { + if (player.finished == 0) continue; outBitStream->Write1(); // Has more date outBitStream->Write(player.playerID); - outBitStream->Write(0); + outBitStream->Write(player.finished); } outBitStream->Write0(); // No more data } - outBitStream->Write1(); // Dirty? - - outBitStream->Write(m_RemainingLaps); - - outBitStream->Write(static_cast(m_PathName.size())); - for (const auto character : m_PathName) { - outBitStream->Write(character); + outBitStream->Write(bIsInitialUpdate); + if (bIsInitialUpdate) { + outBitStream->Write(m_RemainingLaps); + outBitStream->Write(m_PathName.size()); + for (const auto character : m_PathName) { + outBitStream->Write(character); + } } - outBitStream->Write1(); // ??? - outBitStream->Write1(); // ??? + outBitStream->Write(!m_RacingPlayers.empty()); + if (!m_RacingPlayers.empty()) { + for (const auto& player : m_RacingPlayers) { + if (player.finished == 0) continue; + outBitStream->Write1(); // Has more data + outBitStream->Write(player.playerID); + outBitStream->Write(player.bestLapTime); + outBitStream->Write(player.raceTime); + } - outBitStream->Write(m_LeadingPlayer); - outBitStream->Write(m_RaceBestLap); - outBitStream->Write(m_RaceBestTime); + outBitStream->Write0(); // No more data + } } RacingPlayerInfo* RacingControlComponent::GetPlayerData(LWOOBJID playerID) { @@ -534,9 +559,9 @@ void RacingControlComponent::Update(float deltaTime) { continue; } - auto* playerInstance = dynamic_cast(playerEntity); + auto* characterComponent = playerEntity->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); } m_LobbyPlayers.clear(); @@ -545,12 +570,10 @@ void RacingControlComponent::Update(float deltaTime) { // From the first 2 players loading in the rest have a max of 15 seconds // to load in, can raise this if it's too low if (m_LoadTimer >= 15) { - Game::logger->Log("RacingControlComponent", - "Loading all players..."); + LOG("Loading all players..."); for (size_t positionNumber = 0; positionNumber < m_LobbyPlayers.size(); positionNumber++) { - Game::logger->Log("RacingControlComponent", - "Loading player now!"); + LOG("Loading player now!"); auto* player = Game::entityManager->GetEntity(m_LobbyPlayers[positionNumber]); @@ -559,12 +582,11 @@ void RacingControlComponent::Update(float deltaTime) { return; } - Game::logger->Log("RacingControlComponent", - "Loading player now NOW!"); + LOG("Loading player now NOW!"); LoadPlayerVehicle(player, positionNumber + 1, true); - m_Loaded = true; + Game::entityManager->SerializeEntity(m_Parent); } m_Loaded = true; @@ -599,9 +621,9 @@ void RacingControlComponent::Update(float deltaTime) { continue; } - auto* playerInstance = dynamic_cast(playerEntity); + auto* characterComponent = playerEntity->GetComponent(); - playerInstance->SendToZone(m_MainWorld); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); } return; @@ -710,7 +732,7 @@ void RacingControlComponent::Update(float deltaTime) { m_Started = true; - Game::logger->Log("RacingControlComponent", "Starting race"); + LOG("Starting race"); Game::entityManager->SerializeEntity(m_Parent); @@ -752,6 +774,8 @@ void RacingControlComponent::Update(float deltaTime) { continue; } + if (m_Finished != 0) Game::entityManager->SerializeEntity(m_Parent); + // Loop through all the waypoints and see if the player has reached a // new checkpoint uint32_t respawnIndex = 0; @@ -768,7 +792,7 @@ void RacingControlComponent::Update(float deltaTime) { const auto& position = waypoint.position; - if (std::abs((int)respawnIndex - (int)player.respawnIndex) > 10 && + if (std::abs(static_cast(respawnIndex) - static_cast(player.respawnIndex)) > 10 && player.respawnIndex != path->pathWaypoints.size() - 1) { ++respawnIndex; @@ -793,7 +817,7 @@ 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 - player.respawnPosition = position + NiPoint3::UNIT_Y * 5; + player.respawnPosition = position + NiPoint3Constant::UNIT_Y * 5; player.respawnRotation = vehicle->GetRotation(); player.respawnIndex = respawnIndex; @@ -813,8 +837,7 @@ void RacingControlComponent::Update(float deltaTime) { if (player.bestLapTime == 0 || player.bestLapTime > lapTime) { player.bestLapTime = lapTime; - Game::logger->Log("RacingControlComponent", - "Best lap time (%llu)", lapTime); + LOG("Best lap time (%llu)", lapTime); } auto* missionComponent = @@ -823,7 +846,7 @@ void RacingControlComponent::Update(float deltaTime) { if (missionComponent != nullptr) { // Progress lap time tasks - missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, (LWOOBJID)eRacingTaskParam::LAP_TIME); + missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, static_cast(eRacingTaskParam::LAP_TIME)); if (player.lap == 3) { m_Finished++; @@ -834,58 +857,28 @@ void RacingControlComponent::Update(float deltaTime) { player.raceTime = raceTime; - Game::logger->Log("RacingControlComponent", - "Completed time %llu, %llu", + LOG("Completed time %llu, %llu", raceTime, raceTime * 1000); LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast(player.raceTime), static_cast(player.bestLapTime), static_cast(player.finished == 1)); // Entire race time - missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, (LWOOBJID)eRacingTaskParam::TOTAL_TRACK_TIME); + missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast(eRacingTaskParam::TOTAL_TRACK_TIME)); auto* characterComponent = playerEntity->GetComponent(); if (characterComponent != nullptr) { characterComponent->TrackRaceCompleted(m_Finished == 1); } - - // TODO: Figure out how to update the GUI leaderboard. } } - Game::logger->Log("RacingControlComponent", - "Lapped (%i) in (%llu)", player.lap, + LOG("Lapped (%i) in (%llu)", player.lap, lapTime); } - Game::logger->Log("RacingControlComponent", - "Reached point (%i)/(%i)", player.respawnIndex, + LOG("Reached point (%i)/(%i)", player.respawnIndex, path->pathWaypoints.size()); break; } } } - -std::string RacingControlComponent::FormatTimeString(time_t time) { - int32_t min = time / 60; - time -= min * 60; - int32_t sec = time; - - std::string minText; - std::string secText; - - if (min <= 0) { - minText = "0"; - } else { - minText = std::to_string(min); - } - - if (sec <= 0) { - secText = "00"; - } else if (sec <= 9) { - secText = "0" + std::to_string(sec); - } else { - secText = std::to_string(sec); - } - - return minText + ":" + secText + ".00"; -} diff --git a/dGame/dComponents/RacingControlComponent.h b/dGame/dComponents/RacingControlComponent.h index a81121e1..4a7d387f 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -103,15 +103,15 @@ struct RacingPlayerInfo { /** * Component that's attached to a manager entity in each race zone that loads player vehicles, keep scores, etc. */ -class RacingControlComponent : public Component { +class RacingControlComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_CONTROL; RacingControlComponent(Entity* parentEntity); ~RacingControlComponent(); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - void Update(float deltaTime); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + void Update(float deltaTime) override; /** * Invoked when a player loads into the zone. @@ -151,13 +151,6 @@ public: */ RacingPlayerInfo* GetPlayerData(LWOOBJID playerID); - /** - * Formats a time to a string, currently unused - * @param time the time to format - * @return the time formatted as string - */ - static std::string FormatTimeString(time_t time); - private: /** @@ -251,4 +244,5 @@ private: * Value for message box response to know if we are exiting the race via the activity dialogue */ const int32_t m_ActivityExitConfirm = 1; + bool m_AllPlayersReady = false; }; diff --git a/dGame/dComponents/RacingSoundTriggerComponent.h b/dGame/dComponents/RacingSoundTriggerComponent.h new file mode 100644 index 00000000..140bbe20 --- /dev/null +++ b/dGame/dComponents/RacingSoundTriggerComponent.h @@ -0,0 +1,15 @@ +#ifndef __RACINGSOUNDTRIGGERCOMPONENT__H__ +#define __RACINGSOUNDTRIGGERCOMPONENT__H__ + +#include "SoundTriggerComponent.h" +#include "eReplicaComponentType.h" + +class Entity; + +class RacingSoundTriggerComponent : public SoundTriggerComponent { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_SOUND_TRIGGER; + RacingSoundTriggerComponent(Entity* parent) : SoundTriggerComponent(parent){}; +}; + +#endif //!__RACINGSOUNDTRIGGERCOMPONENT__H__ diff --git a/dGame/dComponents/RacingStatsComponent.h b/dGame/dComponents/RacingStatsComponent.h new file mode 100644 index 00000000..ad1b35bf --- /dev/null +++ b/dGame/dComponents/RacingStatsComponent.h @@ -0,0 +1,14 @@ +#ifndef __RACINGSTATSCOMPONENT__H__ +#define __RACINGSTATSCOMPONENT__H__ + +#include "Component.h" +#include "eReplicaComponentType.h" + +class RacingStatsComponent final : public Component { +public: + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RACING_STATS; + + RacingStatsComponent(Entity* parent) : Component(parent) {} +}; + +#endif //!__RACINGSTATSCOMPONENT__H__ diff --git a/dGame/dComponents/RailActivatorComponent.cpp b/dGame/dComponents/RailActivatorComponent.cpp index c7d58999..f269da49 100644 --- a/dGame/dComponents/RailActivatorComponent.cpp +++ b/dGame/dComponents/RailActivatorComponent.cpp @@ -4,16 +4,16 @@ #include "CDRailActivatorComponent.h" #include "Entity.h" #include "GameMessages.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "RenderComponent.h" #include "EntityManager.h" #include "eStateChangeType.h" RailActivatorComponent::RailActivatorComponent(Entity* parent, int32_t componentID) : Component(parent) { m_ComponentID = componentID; - const auto tableData = CDClientManager::Instance().GetTable()->GetEntryByID(componentID);; + const auto tableData = CDClientManager::GetTable()->GetEntryByID(componentID);; m_Path = parent->GetVar(u"rail_path"); m_PathDirection = parent->GetVar(u"rail_path_direction"); @@ -43,13 +43,13 @@ RailActivatorComponent::RailActivatorComponent(Entity* parent, int32_t component RailActivatorComponent::~RailActivatorComponent() = default; void RailActivatorComponent::OnUse(Entity* originator) { - auto* rebuildComponent = m_Parent->GetComponent(); - if (rebuildComponent != nullptr && rebuildComponent->GetState() != eRebuildState::COMPLETED) + auto* quickBuildComponent = m_Parent->GetComponent(); + if (quickBuildComponent != nullptr && quickBuildComponent->GetState() != eQuickBuildState::COMPLETED) return; - if (rebuildComponent != nullptr) { + if (quickBuildComponent != nullptr) { // Don't want it to be destroyed while a player is using it - rebuildComponent->SetResetTime(rebuildComponent->GetResetTime() + 10.0f); + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() + 10.0f); } m_EntitiesOnRail.push_back(originator->GetObjectID()); @@ -116,11 +116,11 @@ void RailActivatorComponent::OnCancelRailMovement(Entity* originator) { true, true, true, true, true, true, true ); - auto* rebuildComponent = m_Parent->GetComponent(); + auto* quickBuildComponent = m_Parent->GetComponent(); - if (rebuildComponent != nullptr) { + if (quickBuildComponent != nullptr) { // Set back reset time - rebuildComponent->SetResetTime(rebuildComponent->GetResetTime() - 10.0f); + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() - 10.0f); } if (std::find(m_EntitiesOnRail.begin(), m_EntitiesOnRail.end(), originator->GetObjectID()) != m_EntitiesOnRail.end()) { diff --git a/dGame/dComponents/RailActivatorComponent.h b/dGame/dComponents/RailActivatorComponent.h index 5d625d2a..015f36b7 100644 --- a/dGame/dComponents/RailActivatorComponent.h +++ b/dGame/dComponents/RailActivatorComponent.h @@ -15,7 +15,7 @@ public: explicit RailActivatorComponent(Entity* parent, int32_t componentID); ~RailActivatorComponent() override; - static const eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RAIL_ACTIVATOR; /** * Handles the OnUse event from some entity, initiates the rail movement diff --git a/dGame/dComponents/RebuildComponent.h b/dGame/dComponents/RebuildComponent.h deleted file mode 100644 index 09dd0465..00000000 --- a/dGame/dComponents/RebuildComponent.h +++ /dev/null @@ -1,365 +0,0 @@ -#ifndef REBUILDCOMPONENT_H -#define REBUILDCOMPONENT_H - -#include -#include -#include -#include "dCommonVars.h" -#include "NiPoint3.h" -#include "ScriptedActivityComponent.h" -#include "Preconditions.h" -#include "Component.h" -#include "eReplicaComponentType.h" -#include "eRebuildState.h" - -class Entity; -enum class eQuickBuildFailReason : uint32_t; - -/** - * Component that handles entities that can be built into other entities using the quick build mechanic. Generally - * consists of an activator that shows a popup and then the actual entity that the bricks are built into. Note - * that quick builds are also scripted activities so this shared some logic with the ScriptedActivityComponent. - */ -class RebuildComponent : public Component { -public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; - - RebuildComponent(Entity* entity); - ~RebuildComponent() override; - - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - void Update(float deltaTime) override; - - /** - * Handles a OnUse event from some entity, initiating the quick build - * @param originator the entity that triggered the event - */ - void OnUse(Entity* originator) override; - - /** - * Spawns the activator that can be used to initiate the rebuild - */ - void SpawnActivator(); - - /** - * Despawns the activiator that can be used to initiate the rebuild - */ - void DespawnActivator(); - - /** - * Returns the entity that acts as the activator for this rebuild - * @return the entity that acts as the activator for this rebuild - */ - Entity* GetActivator(); - - /** - * Returns the spawn position of the activator for this rebuild, if any - * @return the spawn position of the activator for this rebuild, if any - */ - NiPoint3 GetActivatorPosition(); - - /** - * Sets the spawn position for the activator of this rebuild - * @param value the spawn position to set for the activator - */ - void SetActivatorPosition(NiPoint3 value); - - /** - * Returns the time it takes for the rebuild to reset after being built - * @return the time it takes for the rebuild to reset after being built - */ - float GetResetTime(); - - /** - * Sets the time it takes for the rebuild to reset after being built - * @param value the reset time to set - */ - void SetResetTime(float value); - - /** - * Returns the time it takes to complete the rebuild - * @return the time it takes to complete the rebuild - */ - float GetCompleteTime(); - - /** - * Sets the time it takes to complete the rebuild - * @param value the completion time to set - */ - void SetCompleteTime(float value); - - /** - * Returns the imagination that's taken when completing the rebuild - * @return the imagination that's taken when completing the rebuild - */ - int GetTakeImagination(); - - /** - * Sets the imagination that's taken when completing the rebuild - * @param value the imagination deduction to set - */ - void SetTakeImagination(int value); - - /** - * Returns if the rebuild can be interrupted, currently unused - * @return if the rebuild can be interrupted - */ - bool GetInterruptible(); - - /** - * Sets whether or not the rebuild can be interrupted, currently unused - * @param value true if the rebuild may be interrupted, false otherwise - */ - void SetInterruptible(bool value); - - /** - * Returns whether or not this entity contains a built-in activator - * @return whether or not this entity contains a built-in activator - */ - bool GetSelfActivator(); - - /** - * Sets whether or not this entity contains a built-in activator. If set to false this will spawn activators on - * each new rebuild. - * @param value whether or not this entity contains a built-in activator - */ - void SetSelfActivator(bool value); - - /** - * Currently unused - */ - std::vector GetCustomModules(); - - /** - * Currently unused - */ - void SetCustomModules(std::vector value); - - /** - * Returns the activity ID for participating in this rebuild - * @return the activity ID for participating in this rebuild - */ - int GetActivityId(); - - /** - * Sets the activity ID for participating in this rebuild - * @param value the activity ID to set - */ - void SetActivityId(int value); - - /** - * Currently unused - */ - int GetPostImaginationCost(); - - /** - * Currently unused - */ - void SetPostImaginationCost(int value); - - /** - * Returns the time it takes for an incomplete rebuild to be smashed automatically - * @return the time it takes for an incomplete rebuild to be smashed automatically - */ - float GetTimeBeforeSmash(); - - /** - * Sets the time it takes for an incomplete rebuild to be smashed automatically - * @param value the time to set - */ - void SetTimeBeforeSmash(float value); - - /** - * Returns the current rebuild state - * @return the current rebuild state - */ - eRebuildState GetState(); - - /** - * Returns the player that is currently building this rebuild - * @return the player that is currently building this rebuild - */ - Entity* GetBuilder() const; - - /** - * Returns whether or not the player is repositioned when initiating the rebuild - * @return whether or not the player is repositioned when initiating the rebuild - */ - bool GetRepositionPlayer() const; - - /** - * Sets whether or not the player is repositioned when initiating the rebuild - * @param value whether or not the player is repositioned when initiating the rebuild - */ - void SetRepositionPlayer(bool value); - - /** - * Adds a callback that is called when the rebuild is completed - * @param callback the callback to add - */ - void AddRebuildCompleteCallback(const std::function& callback); - - /** - * Adds a callback when the rebuild state is updated - * @param callback the callback to add - */ - void AddRebuildStateCallback(const std::function& callback); - - /** - * Resets the rebuild - * @param failed whether or not the player failed to complete the rebuild, triggers an extra animation - */ - void ResetRebuild(bool failed); - - /** - * Cancels the rebuild if it wasn't completed - * @param builder the player that's currently building - * @param failReason the reason the rebuild was cancelled - * @param skipChecks whether or not to skip the check for the rebuild not being completed - */ - void CancelRebuild(Entity* builder, eQuickBuildFailReason failReason, bool skipChecks = false); -private: - /** - * Whether or not the quickbuild state has been changed since we last serialized it. - */ - bool m_StateDirty = true; - - /** - * The state the rebuild is currently in - */ - eRebuildState m_State = eRebuildState::OPEN; - - /** - * The time that has passed since initiating the rebuild - */ - float m_Timer = 0; - - /** - * The time that has passed before completing the rebuild - */ - float m_TimerIncomplete = 0; - - /** - * The position that the rebuild activator is spawned at - */ - NiPoint3 m_ActivatorPosition = NiPoint3::ZERO; - - /** - * The entity that represents the rebuild activator - */ - Entity* m_Activator = nullptr; - - /** - * The ID of the entity that represents the rebuild activator - */ - LWOOBJID m_ActivatorId = LWOOBJID_EMPTY; - - /** - * Triggers the blinking that indicates that the rebuild is resetting - */ - bool m_ShowResetEffect = false; - - /** - * Currently unused - */ - float m_Taken = 0; - - /** - * The callbacks that are called when the rebuild is completed - */ - std::vector> m_RebuildCompleteCallbacks{}; - - /** - * The callbacks that are called when the rebuild state is updated - */ - std::vector> m_RebuildStateCallbacks{}; - - /** - * The time it takes for the rebuild to reset after being completed - */ - float m_ResetTime = 0; - - /** - * The time it takes to complete the rebuild - */ - float m_CompleteTime = 0; - - /** - * The imagination that's deducted when compeleting the rebuild - */ - int m_TakeImagination = 0; - - /** - * Currently unused - */ - bool m_Interruptible = false; - - /** - * Whether or not this rebuild entity also has an activator attached. If not a new one will be spawned - */ - bool m_SelfActivator = false; - - /** - * Currently unused - */ - std::vector m_CustomModules{}; - - /** - * The activity ID that players partake in when doing this rebuild - */ - int m_ActivityId = 0; - - /** - * Currently unused - */ - int m_PostImaginationCost = 0; - - /** - * The time it takes for the rebuild to reset when it's not completed yet - */ - float m_TimeBeforeSmash = 0; - - /** - * The time it takes to drain imagination - */ - float m_TimeBeforeDrain = 0; - - /** - * The amount of imagination that was drained when building this rebuild - */ - int m_DrainedImagination = 0; - - /** - * Whether to reposition the player or not when building - */ - bool m_RepositionPlayer = true; - - /** - * Currently unused - */ - float m_SoftTimer = 0; - - /** - * The ID of the entity that's currently building the rebuild - */ - LWOOBJID m_Builder = LWOOBJID_EMPTY; - - /** - * Preconditions to be met before being able to start the rebuild - */ - PreconditionExpression* m_Precondition = nullptr; - - /** - * Starts the rebuild for a certain entity - * @param user the entity to start the rebuild - */ - void StartRebuild(Entity* user); - - /** - * Completes the rebuild for an entity, dropping loot and despawning the activator - * @param user the entity that completed the rebuild - */ - void CompleteRebuild(Entity* user); -}; - -#endif // REBUILDCOMPONENT_H diff --git a/dGame/dComponents/RenderComponent.cpp b/dGame/dComponents/RenderComponent.cpp index 94f5fb5d..118e4847 100644 --- a/dGame/dComponents/RenderComponent.cpp +++ b/dGame/dComponents/RenderComponent.cpp @@ -1,22 +1,22 @@ #include "RenderComponent.h" +#include #include #include +#include #include #include "Entity.h" -#include "PacketUtils.h" #include "CDClientManager.h" #include "GameMessages.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "CDAnimationsTable.h" std::unordered_map RenderComponent::m_DurationCache{}; -RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component(parent) { - m_Effects = std::vector(); +RenderComponent::RenderComponent(Entity* const parentEntity, const int32_t componentId) : Component{ parentEntity } { m_LastAnimationName = ""; if (componentId == -1) return; @@ -27,114 +27,69 @@ RenderComponent::RenderComponent(Entity* parent, int32_t componentId): Component if (!result.eof()) { auto animationGroupIDs = std::string(result.getStringField("animationGroupIDs", "")); if (!animationGroupIDs.empty()) { - auto* animationsTable = CDClientManager::Instance().GetTable(); + auto* animationsTable = CDClientManager::GetTable(); auto groupIdsSplit = GeneralUtils::SplitString(animationGroupIDs, ','); for (auto& groupId : groupIdsSplit) { - int32_t groupIdInt; - if (!GeneralUtils::TryParse(groupId, groupIdInt)) { - Game::logger->Log("RenderComponent", "bad animation group Id %s", groupId.c_str()); + const auto groupIdInt = GeneralUtils::TryParse(groupId); + + if (!groupIdInt) { + LOG("bad animation group Id %s", groupId.c_str()); continue; } - m_animationGroupIds.push_back(groupIdInt); - animationsTable->CacheAnimationGroup(groupIdInt); + + m_animationGroupIds.push_back(groupIdInt.value()); + animationsTable->CacheAnimationGroup(groupIdInt.value()); } } } result.finalize(); } -RenderComponent::~RenderComponent() { - for (Effect* eff : m_Effects) { - if (eff) { - delete eff; - eff = nullptr; - } - } - - m_Effects.clear(); -} - -void RenderComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void RenderComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (!bIsInitialUpdate) return; outBitStream->Write(m_Effects.size()); - for (Effect* eff : m_Effects) { - // Check that the effect is non-null - assert(eff); + for (auto& eff : m_Effects) { + outBitStream->Write(eff.name.size()); + // if there is no name, then we don't write anything else + if (eff.name.empty()) continue; - outBitStream->Write(eff->name.size()); - for (const auto& value : eff->name) - outBitStream->Write(value); + for (const auto& value : eff.name) outBitStream->Write(value); - outBitStream->Write(eff->effectID); + outBitStream->Write(eff.effectID); - outBitStream->Write(eff->type.size()); - for (const auto& value : eff->type) - outBitStream->Write(value); + outBitStream->Write(eff.type.size()); + for (const auto& value : eff.type) outBitStream->Write(value); - outBitStream->Write(eff->scale); - outBitStream->Write(eff->secondary); + outBitStream->Write(eff.priority); + outBitStream->Write(eff.secondary); } } -Effect* RenderComponent::AddEffect(const int32_t effectId, const std::string& name, const std::u16string& type) { - auto* eff = new Effect(); - - eff->effectID = effectId; - - eff->name = name; - - eff->type = type; - - m_Effects.push_back(eff); - - return eff; +Effect& RenderComponent::AddEffect(const int32_t effectId, const std::string& name, const std::u16string& type, const float priority) { + return m_Effects.emplace_back(effectId, name, type, priority); } void RenderComponent::RemoveEffect(const std::string& name) { - uint32_t index = -1; + if (m_Effects.empty()) return; - for (auto i = 0u; i < m_Effects.size(); ++i) { - auto* eff = m_Effects[i]; + const auto effectToRemove = std::ranges::find_if(m_Effects, [&name](auto&& effect) { return effect.name == name; }); + if (effectToRemove == m_Effects.end()) return; // Return early if effect is not present - if (eff->name == name) { - index = i; - - delete eff; - - break; - } - } - - if (index == -1) { - return; - } - - m_Effects.erase(m_Effects.begin() + index); + const auto lastEffect = m_Effects.rbegin(); + *effectToRemove = std::move(*lastEffect); // Move-overwrite + m_Effects.pop_back(); } -void RenderComponent::Update(const float deltaTime) { - std::vector dead; +void RenderComponent::Update(const float deltaTime) { + for (auto& effect : m_Effects) { + if (effect.time == 0) continue; // Skip persistent effects - for (auto* effect : m_Effects) { - if (effect->time == 0) { - continue; // Skip persistent effects - } + const auto result = effect.time - deltaTime; + if (result <= 0) continue; - const auto result = effect->time - deltaTime; - - if (result <= 0) { - dead.push_back(effect); - - continue; - } - - effect->time = result; - } - - for (auto* effect : dead) { - // StopEffect(effect->name); + effect.time = result; } } @@ -143,12 +98,12 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e GameMessages::SendPlayFXEffect(m_Parent, effectId, effectType, name, secondary, priority, scale, serialize); - auto* effect = AddEffect(effectId, name, effectType); + auto& effect = AddEffect(effectId, name, effectType, priority); const auto& pair = m_DurationCache.find(effectId); if (pair != m_DurationCache.end()) { - effect->time = pair->second; + effect.time = pair->second; return; } @@ -167,16 +122,16 @@ void RenderComponent::PlayEffect(const int32_t effectId, const std::u16string& e m_DurationCache[effectId] = 0; - effect->time = 0; // Persistent effect + effect.time = 0; // Persistent effect return; } - effect->time = static_cast(result.getFloatField(0)); + effect.time = static_cast(result.getFloatField(0)); result.finalize(); - m_DurationCache[effectId] = effect->time; + m_DurationCache[effectId] = effect.time; } void RenderComponent::StopEffect(const std::string& name, const bool killImmediate) { @@ -185,11 +140,6 @@ void RenderComponent::StopEffect(const std::string& name, const bool killImmedia RemoveEffect(name); } -std::vector& RenderComponent::GetEffects() { - return m_Effects; -} - - float RenderComponent::PlayAnimation(Entity* self, const std::u16string& animation, float priority, float scale) { if (!self) return 0.0f; return RenderComponent::PlayAnimation(self, GeneralUtils::UTF16ToWTF8(animation), priority, scale); @@ -217,16 +167,15 @@ float RenderComponent::DoAnimation(Entity* self, const std::string& animation, b auto* renderComponent = self->GetComponent(); if (!renderComponent) return returnlength; - auto* animationsTable = CDClientManager::Instance().GetTable(); + auto* animationsTable = CDClientManager::GetTable(); for (auto& groupId : renderComponent->m_animationGroupIds) { auto animationGroup = animationsTable->GetAnimation(animation, renderComponent->GetLastAnimationName(), groupId); - if (animationGroup.FoundData()) { - auto data = animationGroup.Data(); - renderComponent->SetLastAnimationName(data.animation_name); - returnlength = data.animation_length; + if (animationGroup) { + renderComponent->SetLastAnimationName(animationGroup->animation_name); + returnlength = animationGroup->animation_length; } } if (sendAnimation) GameMessages::SendPlayAnimation(self, GeneralUtils::ASCIIToUTF16(animation), priority, scale); - if (returnlength == 0.0f) Game::logger->Log("RenderComponent", "WARNING: Unable to find animation %s for lot %i in any group.", animation.c_str(), self->GetLOT()); + if (returnlength == 0.0f) LOG("WARNING: Unable to find animation %s for lot %i in any group.", animation.c_str(), self->GetLOT()); return returnlength; } diff --git a/dGame/dComponents/RenderComponent.h b/dGame/dComponents/RenderComponent.h index cdf32160..87b210d7 100644 --- a/dGame/dComponents/RenderComponent.h +++ b/dGame/dComponents/RenderComponent.h @@ -1,7 +1,7 @@ #ifndef RENDERCOMPONENT_H #define RENDERCOMPONENT_H -#include +#include "BitStream.h" #include #include #include @@ -17,7 +17,12 @@ class Entity; * here. */ struct Effect { - Effect() { scale = 1.0f; } + explicit Effect(const int32_t effectID, const std::string& name, const std::u16string& type, const float priority = 1.0f) noexcept + : effectID{ effectID } + , name{ name } + , type{ type } + , priority{ priority } { + } /** * The ID of the effect @@ -35,9 +40,9 @@ struct Effect { std::u16string type = u""; /** - * How scaled (enlarged) the effect is + * The importantness of the effect */ - float scale = 1.0f; + float priority = 1.0f; /** * Some related entity that casted the effect @@ -54,14 +59,13 @@ struct Effect { * Determines that a component should be visibly rendered into the world, most entities have this. This component * also handles effects that play for entities. */ -class RenderComponent : public Component { +class RenderComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RENDER; - RenderComponent(Entity* entity, int32_t componentId = -1); - ~RenderComponent() override; + RenderComponent(Entity* const parentEntity, const int32_t componentId = -1); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; void Update(float deltaTime) override; /** @@ -69,9 +73,10 @@ public: * @param effectId the ID of the effect * @param name the name of the effect * @param type the type of the effect + * @param priority the priority of the effect * @return if successful, the effect that was created */ - Effect* AddEffect(int32_t effectId, const std::string& name, const std::u16string& type); + [[maybe_unused]] Effect& AddEffect(const int32_t effectId, const std::string& name, const std::u16string& type, const float priority); /** * Removes an effect for this entity @@ -98,36 +103,30 @@ public: */ void StopEffect(const std::string& name, bool killImmediate = true); - /** - * Returns the list of currently active effects - * @return - */ - std::vector& GetEffects(); - /** * Verifies that an animation can be played on this entity by checking * if it has the animation assigned to its group. If it does, the animation is echo'd * down to all clients to be played and the duration of the played animation is returned. * If the animation did not exist or the function was called in an invalid state, 0 is returned. - * + * * The logic here matches the exact client logic. - * + * * @param self The entity that wants to play an animation * @param animation The animation_type (animationID in the client) to be played. * @param sendAnimation Whether or not to echo the animation down to all clients. * @param priority The priority of the animation. Only used if sendAnimation is true. * @param scale The scale of the animation. Only used if sendAnimation is true. - * + * * @return The duration of the animation that was played. */ static float DoAnimation(Entity* self, const std::string& animation, bool sendAnimation, float priority = 0.0f, float scale = 1.0f); static float PlayAnimation(Entity* self, const std::u16string& animation, float priority = 0.0f, float scale = 1.0f); static float PlayAnimation(Entity* self, const std::string& animation, float priority = 0.0f, float scale = 1.0f); - static float GetAnimationTime(Entity* self, const std::string& animation); - static float GetAnimationTime(Entity* self, const std::u16string& animation); + [[nodiscard]] static float GetAnimationTime(Entity* self, const std::string& animation); + [[nodiscard]] static float GetAnimationTime(Entity* self, const std::u16string& animation); - const std::string& GetLastAnimationName() const { return m_LastAnimationName; }; + [[nodiscard]] const std::string& GetLastAnimationName() const { return m_LastAnimationName; }; void SetLastAnimationName(const std::string& name) { m_LastAnimationName = name; }; private: @@ -135,7 +134,7 @@ private: /** * List of currently active effects */ - std::vector m_Effects; + std::vector m_Effects; std::vector m_animationGroupIds; diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp index babd1974..414ce2e8 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.cpp @@ -1,32 +1,16 @@ /* * Darkflame Universe - * Copyright 2019 + * Copyright 2023 */ #include "RigidbodyPhantomPhysicsComponent.h" #include "Entity.h" -RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : Component(parent) { +RigidbodyPhantomPhysicsComponent::RigidbodyPhantomPhysicsComponent(Entity* parent) : PhysicsComponent(parent) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); - m_IsDirty = true; } -RigidbodyPhantomPhysicsComponent::~RigidbodyPhantomPhysicsComponent() { -} - -void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - outBitStream->Write(m_IsDirty || bIsInitialUpdate); - if (m_IsDirty || bIsInitialUpdate) { - outBitStream->Write(m_Position.x); - outBitStream->Write(m_Position.y); - outBitStream->Write(m_Position.z); - - outBitStream->Write(m_Rotation.x); - outBitStream->Write(m_Rotation.y); - outBitStream->Write(m_Rotation.z); - outBitStream->Write(m_Rotation.w); - - m_IsDirty = false; - } +void RigidbodyPhantomPhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate); } diff --git a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h index 480f9b81..082dd1e7 100644 --- a/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h +++ b/dGame/dComponents/RigidbodyPhantomPhysicsComponent.h @@ -1,71 +1,29 @@ /* * Darkflame Universe - * Copyright 2019 + * Copyright 2023 */ -#ifndef RIGIDBODYPHANTOMPHYSICS_H -#define RIGIDBODYPHANTOMPHYSICS_H +#ifndef __RIGIDBODYPHANTOMPHYSICS_H__ +#define __RIGIDBODYPHANTOMPHYSICS_H__ #include "BitStream.h" #include "dCommonVars.h" #include "NiPoint3.h" #include "NiQuaternion.h" -#include "Component.h" +#include "PhysicsComponent.h" #include "eReplicaComponentType.h" /** * 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. */ -class RigidbodyPhantomPhysicsComponent : public Component { +class RigidbodyPhantomPhysicsComponent : public PhysicsComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::PHANTOM_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS; RigidbodyPhantomPhysicsComponent(Entity* parent); - ~RigidbodyPhantomPhysicsComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - - /** - * Returns the position of this entity - * @return the position of this entity - */ - NiPoint3& GetPosition() { return m_Position; } - - /** - * Sets the position of this entity - * @param pos the position to set - */ - void SetPosition(const NiPoint3& pos) { m_Position = pos; m_IsDirty = true; } - - /** - * Returns the rotation of this entity - * @return the rotation of this entity - */ - NiQuaternion& GetRotation() { return m_Rotation; } - - /** - * Sets the rotation for this entity - * @param rot the rotation to tset - */ - void SetRotation(const NiQuaternion& rot) { m_Rotation = rot; m_IsDirty = true; } - -private: - - /** - * The position of this entity - */ - NiPoint3 m_Position; - - /** - * The rotation of this entity - */ - NiQuaternion m_Rotation; - - /** - * Whether or not the component should be serialized - */ - bool m_IsDirty; + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; }; -#endif // RIGIDBODYPHANTOMPHYSICS_H +#endif // __RIGIDBODYPHANTOMPHYSICS_H__ diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.cpp b/dGame/dComponents/RocketLaunchpadControlComponent.cpp index 10908d9e..5a385546 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.cpp +++ b/dGame/dComponents/RocketLaunchpadControlComponent.cpp @@ -8,14 +8,14 @@ #include "EntityManager.h" #include "Item.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "CDClientDatabase.h" #include "ChatPackets.h" #include "MissionComponent.h" #include "PropertyEntranceComponent.h" -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "dServer.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "eObjectWorldState.h" #include "eConnectionType.h" #include "eMasterMessageType.h" @@ -57,7 +57,7 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, auto* rocket = characterComponent->GetRocket(originator); if (!rocket) { - Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket!"); + LOG("Unable to find rocket!"); return; } @@ -94,7 +94,7 @@ void RocketLaunchpadControlComponent::OnUse(Entity* originator) { return; } - auto* rocketLaunchLUP = m_Parent->GetComponent(); + auto* rocketLaunchLUP = m_Parent->GetComponent(); if (rocketLaunchLUP) { return; } @@ -137,7 +137,7 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player) void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE); bitStream.Write(zoneID); Game::server->SendToMaster(&bitStream); } diff --git a/dGame/dComponents/RocketLaunchpadControlComponent.h b/dGame/dComponents/RocketLaunchpadControlComponent.h index 84cff22d..03d2f141 100644 --- a/dGame/dComponents/RocketLaunchpadControlComponent.h +++ b/dGame/dComponents/RocketLaunchpadControlComponent.h @@ -16,9 +16,9 @@ class PreconditionExpression; /** * Component that handles rocket launchpads that can be interacted with to travel to other worlds. */ -class RocketLaunchpadControlComponent : public Component { +class RocketLaunchpadControlComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH; RocketLaunchpadControlComponent(Entity* parent, int rocketId); ~RocketLaunchpadControlComponent() override; diff --git a/dGame/dComponents/ScriptedActivityComponent.h b/dGame/dComponents/ScriptedActivityComponent.h index 1d49a62d..308a0a86 100644 --- a/dGame/dComponents/ScriptedActivityComponent.h +++ b/dGame/dComponents/ScriptedActivityComponent.h @@ -1,381 +1,15 @@ -/* - * Darkflame Universe - * Copyright 2018 - */ +#ifndef __SCRIPTEDACTIVITYCOMPONENT__H__ +#define __SCRIPTEDACTIVITYCOMPONENT__H__ -#include "CDClientManager.h" - -#ifndef SCRIPTEDACTIVITYCOMPONENT_H -#define SCRIPTEDACTIVITYCOMPONENT_H - -#include "BitStream.h" -#include "Entity.h" -#include "Component.h" +#include "ActivityComponent.h" #include "eReplicaComponentType.h" -#include "CDActivitiesTable.h" +class Entity; - /** - * Represents an instance of an activity, having participants and score - */ -class ActivityInstance { +class ScriptedActivityComponent final : public ActivityComponent { public: - ActivityInstance(Entity* parent, CDActivities activityInfo) { m_Parent = parent; m_ActivityInfo = activityInfo; }; - //~ActivityInstance(); - - /** - * Adds an entity to this activity - * @param participant the entity to add - */ - void AddParticipant(Entity* participant); - - /** - * Removes all the participants from this activity - */ - void ClearParticipants() { m_Participants.clear(); }; - - /** - * Starts the instance world for this activity and sends all participants there - */ - void StartZone(); - - /** - * Gives the rewards for completing this activity to some participant - * @param participant the participant to give rewards to - */ - void RewardParticipant(Entity* participant); - - /** - * Removes a participant from this activity - * @param participant the participant to remove - */ - void RemoveParticipant(const Entity* participant); - - /** - * Returns all the participants of this activity - * @return all the participants of this activity - */ - std::vector GetParticipants() const; - - /** - * Currently unused - */ - uint32_t GetScore() const; - - /** - * Currently unused - */ - void SetScore(uint32_t score); -private: - - /** - * Currently unused - */ - uint32_t score = 0; - - /** - * The instance ID of this activity - */ - uint32_t m_NextZoneCloneID = 0; - - /** - * The database information for this activity - */ - CDActivities m_ActivityInfo; - - /** - * The entity that owns this activity (the entity that has the ScriptedActivityComponent) - */ - Entity* m_Parent; - - /** - * All the participants of this activity - */ - std::vector m_Participants; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; + ScriptedActivityComponent(Entity* parent, int activityID) : ActivityComponent(parent, activityID){}; }; -/** - * Represents an entity in a lobby - */ -struct LobbyPlayer { - - /** - * The ID of the entity that is in the lobby - */ - LWOOBJID entityID; - - /** - * Whether or not the entity is ready - */ - bool ready = false; - - /** - * Returns the entity that is in the lobby - * @return the entity that is in the lobby - */ - Entity* GetEntity() const; -}; - -/** - * Represents a lobby of players with a timer until it should start the activity - */ -struct Lobby { - - /** - * The lobby of players - */ - std::vector players; - - /** - * The timer that determines when the activity should start - */ - float timer; -}; - -/** - * Represents the score for the player in an activity, one index might represent score, another one time, etc. - */ -struct ActivityPlayer { - - /** - * The entity that the score is tracked for - */ - LWOOBJID playerID; - - /** - * The list of score for this entity - */ - float values[10]; -}; - -/** - * Welcome to the absolute behemoth that is the scripted activity component. I have now clue how this was managed in - * live but I figure somewhat similarly and it's terrible. In a nutshell, this components handles any activity that - * can be done in the game from quick builds to boss fights to races. On top of that, this component handles instancing - * and lobbying. - */ -class ScriptedActivityComponent : public Component { -public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SCRIPTED_ACTIVITY; - - ScriptedActivityComponent(Entity* parent, int activityID); - ~ScriptedActivityComponent() override; - - void Update(float deltaTime) override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const; - - /** - * Makes some entity join the minigame, if it's a lobbied one, the entity will be placed in the lobby - * @param player the entity to join the game - */ - void PlayerJoin(Entity* player); - - /** - * Makes an entity join the lobby for this minigame, if it exists - * @param player the entity to join - */ - void PlayerJoinLobby(Entity* player); - - /** - * Makes the player leave the lobby - * @param playerID the entity to leave the lobby - */ - void PlayerLeave(LWOOBJID playerID); - - /** - * Removes the entity from the minigame (and its score) - * @param playerID the entity to remove from the minigame - */ - void PlayerRemove(LWOOBJID playerID); - - /** - * Adds all the players to an instance of some activity - * @param instance the instance to load the players into - * @param lobby the players to load into the instance - */ - void LoadPlayersIntoInstance(ActivityInstance* instance, const std::vector& lobby) const; - - /** - * Removes a lobby from the activity manager - * @param lobby the lobby to remove - */ - void RemoveLobby(Lobby* lobby); - - /** - * Marks a player as (un)ready in a lobby - * @param player the entity to mark - * @param bReady true if the entity is ready, false otherwise - */ - void PlayerReady(Entity* player, bool bReady); - - /** - * Returns the ID of this activity - * @return the ID of this activity - */ - int GetActivityID() { return m_ActivityInfo.ActivityID; } - - /** - * Returns if this activity has a lobby, e.g. if it needs to instance players to some other map - * @return true if this activity has a lobby, false otherwise - */ - bool HasLobby() const; - - /** - * Checks if a player is currently waiting in a lobby - * @param player the entity to check for - * @return true if the entity is waiting in a lobby, false otherwise - */ - bool PlayerIsInQueue(Entity* player); - - /** - * Checks if an entity is currently playing this activity - * @param player the entity to check - * @return true if the entity is playing this lobby, false otherwise - */ - bool IsPlayedBy(Entity* player) const; - - /** - * Checks if an entity is currently playing this activity - * @param playerID the entity to check - * @return true if the entity is playing this lobby, false otherwise - */ - bool IsPlayedBy(LWOOBJID playerID) const; - - /** - * Legacy: used to check for unimplemented maps, gladly, this now just returns true :) - */ - bool IsValidActivity(Entity* player); - - /** - * Removes the cost of the activity (e.g. green imaginate) for the entity that plays this activity - * @param player the entity to take cost for - * @return true if the cost was successfully deducted, false otherwise - */ - bool TakeCost(Entity* player) const; - - /** - * Handles any response from a player clicking on a lobby / instance menu - * @param player the entity that clicked - * @param id the message that was passed - */ - void HandleMessageBoxResponse(Entity* player, const std::string& id); - - /** - * Creates a new instance for this activity - * @return a new instance for this activity - */ - ActivityInstance* NewInstance(); - - /** - * Returns all the currently active instances of this activity - * @return all the currently active instances of this activity - */ - const std::vector& GetInstances() const; - - /** - * Returns the instance that some entity is currently playing in - * @param playerID the entity to check for - * @return if any, the instance that the entity is currently in - */ - ActivityInstance* GetInstance(const LWOOBJID playerID); - - /** - * @brief Reloads the config settings for this component - * - */ - void ReloadConfig(); - - /** - * Removes all the instances - */ - void ClearInstances(); - - /** - * Returns all the score for the players that are currently playing this activity - * @return - */ - std::vector GetActivityPlayers() { return m_ActivityPlayers; }; - - /** - * Returns activity data for a specific entity (e.g. score and such). - * @param playerID the entity to get data for - * @return the activity data (score) for the passed player in this activity, if it exists - */ - ActivityPlayer* GetActivityPlayerData(LWOOBJID playerID); - - /** - * Sets some score value for an entity - * @param playerID the entity to set score for - * @param index the score index to set - * @param value the value to set in for that index - */ - void SetActivityValue(LWOOBJID playerID, uint32_t index, float_t value); - - /** - * Returns activity score for the passed parameters - * @param playerID the entity to get score for - * @param index the index to get score for - * @return activity score for the passed parameters - */ - float_t GetActivityValue(LWOOBJID playerID, uint32_t index); - - /** - * Removes activity score tracking for some entity - * @param playerID the entity to remove score for - */ - void RemoveActivityPlayerData(LWOOBJID playerID); - - /** - * Adds activity score tracking for some entity - * @param playerID the entity to add the activity score for - * @return the created entry - */ - ActivityPlayer* AddActivityPlayerData(LWOOBJID playerID); - - /** - * Sets the mapID that this activity points to - * @param mapID the map ID to set - */ - void SetInstanceMapID(uint32_t mapID) { m_ActivityInfo.instanceMapID = mapID; }; - - /** - * Returns the LMI that this activity points to for a team size - * @param teamSize the team size to get the LMI for - * @return the LMI that this activity points to for a team size - */ - uint32_t GetLootMatrixForTeamSize(uint32_t teamSize) { return m_ActivityLootMatrices[teamSize]; } -private: - - /** - * The database information for this activity - */ - CDActivities m_ActivityInfo; - - /** - * All the active instances of this activity - */ - std::vector m_Instances; - - /** - * The current lobbies for this activity - */ - std::vector m_Queue; - - /** - * All the activity score for the players in this activity - */ - std::vector m_ActivityPlayers; - - /** - * LMIs for team sizes - */ - std::unordered_map m_ActivityLootMatrices; - - /** - * The activity id - * - */ - int32_t m_ActivityID; -}; - -#endif // SCRIPTEDACTIVITYCOMPONENT_H +#endif //!__SCRIPTEDACTIVITYCOMPONENT__H__ diff --git a/dGame/dComponents/ShootingGalleryComponent.cpp b/dGame/dComponents/ShootingGalleryComponent.cpp index ed91ac96..45cd9342 100644 --- a/dGame/dComponents/ShootingGalleryComponent.cpp +++ b/dGame/dComponents/ShootingGalleryComponent.cpp @@ -17,13 +17,14 @@ void ShootingGalleryComponent::SetDynamicParams(const DynamicShootingGalleryPara Game::entityManager->SerializeEntity(m_Parent); } -void ShootingGalleryComponent::Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate, uint32_t& flags) const { +void ShootingGalleryComponent::Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate) { // Start ScriptedActivityComponent outBitStream->Write(true); if (m_CurrentPlayerID == LWOOBJID_EMPTY) { outBitStream->Write(0); } else { outBitStream->Write(1); + outBitStream->Write(m_CurrentPlayerID); for (size_t i = 0; i < 10; i++) { outBitStream->Write(0.0f); } @@ -60,6 +61,7 @@ void ShootingGalleryComponent::Serialize(RakNet::BitStream* outBitStream, bool i outBitStream->Write(m_CurrentPlayerID); outBitStream->Write(m_DynamicParams.cannonTimeout); outBitStream->Write(m_DynamicParams.cannonFOV); + if (!isInitialUpdate) m_Dirty = false; } } diff --git a/dGame/dComponents/ShootingGalleryComponent.h b/dGame/dComponents/ShootingGalleryComponent.h index c31575f1..c4b8fea2 100644 --- a/dGame/dComponents/ShootingGalleryComponent.h +++ b/dGame/dComponents/ShootingGalleryComponent.h @@ -71,13 +71,13 @@ struct StaticShootingGalleryParams { * A very ancient component that was used to guide shooting galleries, it's still kind of used but a lot of logic is * also in the related scripts. */ -class ShootingGalleryComponent : public Component { +class ShootingGalleryComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SHOOTING_GALLERY; explicit ShootingGalleryComponent(Entity* parent); ~ShootingGalleryComponent(); - void Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate, uint32_t& flags) const; + void Serialize(RakNet::BitStream* outBitStream, bool isInitialUpdate) override; /** * Returns the static params for the shooting gallery diff --git a/dGame/dComponents/SimplePhysicsComponent.cpp b/dGame/dComponents/SimplePhysicsComponent.cpp index 54a2e616..3d8165dd 100644 --- a/dGame/dComponents/SimplePhysicsComponent.cpp +++ b/dGame/dComponents/SimplePhysicsComponent.cpp @@ -6,17 +6,16 @@ #include "SimplePhysicsComponent.h" #include "BitStream.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "dpWorld.h" #include "CDClientManager.h" #include "CDPhysicsComponentTable.h" #include "Entity.h" -SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* parent) : Component(parent) { +SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, uint32_t componentID) : PhysicsComponent(parent) { m_Position = m_Parent->GetDefaultPosition(); m_Rotation = m_Parent->GetDefaultRotation(); - m_IsDirty = true; const auto& climbable_type = m_Parent->GetVar(u"climbable"); if (climbable_type == u"wall") { @@ -33,7 +32,7 @@ SimplePhysicsComponent::SimplePhysicsComponent(uint32_t componentID, Entity* par SimplePhysicsComponent::~SimplePhysicsComponent() { } -void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) { outBitStream->Write(m_ClimbableType != eClimbableType::CLIMBABLE_TYPE_NOT); outBitStream->Write(m_ClimbableType); @@ -54,19 +53,7 @@ void SimplePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIs } else { outBitStream->Write0(); } - - outBitStream->Write(m_IsDirty || bIsInitialUpdate); - if (m_IsDirty || bIsInitialUpdate) { - outBitStream->Write(m_Position.x); - outBitStream->Write(m_Position.y); - outBitStream->Write(m_Position.z); - outBitStream->Write(m_Rotation.x); - outBitStream->Write(m_Rotation.y); - outBitStream->Write(m_Rotation.z); - outBitStream->Write(m_Rotation.w); - - m_IsDirty = false; - } + PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate); } uint32_t SimplePhysicsComponent::GetPhysicsMotionState() const { diff --git a/dGame/dComponents/SimplePhysicsComponent.h b/dGame/dComponents/SimplePhysicsComponent.h index 51356710..8f92dd95 100644 --- a/dGame/dComponents/SimplePhysicsComponent.h +++ b/dGame/dComponents/SimplePhysicsComponent.h @@ -10,7 +10,7 @@ #include "RakNetTypes.h" #include "NiPoint3.h" #include "NiQuaternion.h" -#include "Component.h" +#include "PhysicsComponent.h" #include "eReplicaComponentType.h" class Entity; @@ -26,38 +26,14 @@ enum class eClimbableType : int32_t { /** * Component that serializes locations of entities to the client */ -class SimplePhysicsComponent : public Component { +class SimplePhysicsComponent : public PhysicsComponent { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SIMPLE_PHYSICS; - SimplePhysicsComponent(uint32_t componentID, Entity* parent); + SimplePhysicsComponent(Entity* parent, uint32_t componentID); ~SimplePhysicsComponent() override; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - - /** - * Returns the position of this entity - * @return the position of this entity - */ - NiPoint3& GetPosition() { return m_Position; } - - /** - * Sets the position of this entity - * @param pos the position to set - */ - void SetPosition(const NiPoint3& pos) { m_Position = pos; m_IsDirty = true; } - - /** - * Returns the rotation of this entity - * @return the rotation of this entity - */ - NiQuaternion& GetRotation() { return m_Rotation; } - - /** - * Sets the rotation of this entity - * @param rot - */ - void SetRotation(const NiQuaternion& rot) { m_Rotation = rot; m_IsDirty = true; } + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Returns the velocity of this entity @@ -108,37 +84,21 @@ public: void SetClimbableType(const eClimbableType& value) { m_ClimbableType = value; } private: - - /** - * The current position of the entity - */ - NiPoint3 m_Position = NiPoint3::ZERO; - - /** - * The current rotation of the entity - */ - NiQuaternion m_Rotation = NiQuaternion::IDENTITY; - /** * The current velocity of the entity */ - NiPoint3 m_Velocity = NiPoint3::ZERO; + NiPoint3 m_Velocity = NiPoint3Constant::ZERO; /** * The current angular velocity of the entity */ - NiPoint3 m_AngularVelocity = NiPoint3::ZERO; + NiPoint3 m_AngularVelocity = NiPoint3Constant::ZERO; /** * Whether or not the velocity has changed */ bool m_DirtyVelocity = true; - /** - * Whether or not the position has changed - */ - bool m_IsDirty = true; - /** * The current physics motion state */ diff --git a/dGame/dComponents/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index 5c1d221a..c43813c1 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -15,7 +15,7 @@ #include "dServer.h" #include "EntityManager.h" #include "Game.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "BaseCombatAIComponent.h" #include "ScriptComponent.h" #include "BuffComponent.h" @@ -55,7 +55,7 @@ void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syn const auto index = this->m_managedBehaviors.find(skillUid); if (index == this->m_managedBehaviors.end()) { - Game::logger->Log("SkillComponent", "Failed to find skill with uid (%i)!", skillUid, syncId); + LOG("Failed to find skill with uid (%i)!", skillUid, syncId); return; } @@ -80,7 +80,7 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B } if (index == -1) { - Game::logger->Log("SkillComponent", "Failed to find projectile id (%llu)!", projectileId); + LOG("Failed to find projectile id (%llu)!", projectileId); return; } @@ -89,12 +89,12 @@ void SkillComponent::SyncPlayerProjectile(const LWOOBJID projectileId, RakNet::B auto query = CDClientDatabase::CreatePreppedStmt( "SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);"); - query.bind(1, (int)sync_entry.lot); + query.bind(1, static_cast(sync_entry.lot)); auto result = query.execQuery(); if (result.eof()) { - Game::logger->Log("SkillComponent", "Failed to find skill id for (%i)!", sync_entry.lot); + LOG("Failed to find skill id for (%i)!", sync_entry.lot); return; } @@ -234,7 +234,7 @@ bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LW // if it's not in the cache look it up and cache it if (pair == m_skillBehaviorCache.end()) { - auto skillTable = CDClientManager::Instance().GetTable(); + auto skillTable = CDClientManager::GetTable(); behaviorId = skillTable->GetSkillByID(skillId).behaviorID; m_skillBehaviorCache.insert_or_assign(skillId, behaviorId); } else { @@ -243,7 +243,7 @@ bool SkillComponent::CastSkill(const uint32_t skillId, LWOOBJID target, const LW // check to see if we got back a valid behavior if (behaviorId == -1) { - Game::logger->LogDebug("SkillComponent", "Tried to cast skill %i but found no behavior", skillId); + LOG_DEBUG("Tried to cast skill %i but found no behavior", skillId); return false; } @@ -299,12 +299,12 @@ SkillExecutionResult SkillComponent::CalculateBehavior(const uint32_t skillId, c } //start.optionalTargetID = target; - start.sBitStream.assign((char*)bitStream->GetData(), bitStream->GetNumberOfBytesUsed()); + start.sBitStream.assign(reinterpret_cast(bitStream->GetData()), bitStream->GetNumberOfBytesUsed()); // Write message RakNet::BitStream message; - PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); + BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); message.Write(this->m_Parent->GetObjectID()); start.Serialize(&message); @@ -401,7 +401,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry) if (other == nullptr) { if (entry.branchContext.target != LWOOBJID_EMPTY) { - Game::logger->Log("SkillComponent", "Invalid projectile target (%llu)!", entry.branchContext.target); + LOG("Invalid projectile target (%llu)!", entry.branchContext.target); } return; @@ -409,11 +409,11 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry) auto query = CDClientDatabase::CreatePreppedStmt( "SELECT behaviorID FROM SkillBehavior WHERE skillID = (SELECT skillID FROM ObjectSkills WHERE objectTemplate = ?);"); - query.bind(1, (int)entry.lot); + query.bind(1, static_cast(entry.lot)); auto result = query.execQuery(); if (result.eof()) { - Game::logger->Log("SkillComponent", "Failed to find skill id for (%i)!", entry.lot); + LOG("Failed to find skill id for (%i)!", entry.lot); return; } @@ -430,14 +430,14 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry) DoClientProjectileImpact projectileImpact; - projectileImpact.sBitStream.assign((char*)bitStream->GetData(), bitStream->GetNumberOfBytesUsed()); + projectileImpact.sBitStream.assign(reinterpret_cast(bitStream->GetData()), bitStream->GetNumberOfBytesUsed()); projectileImpact.i64OwnerID = this->m_Parent->GetObjectID(); projectileImpact.i64OrgID = entry.id; projectileImpact.i64TargetID = entry.branchContext.target; RakNet::BitStream message; - PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); + BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); message.Write(this->m_Parent->GetObjectID()); projectileImpact.Serialize(&message); @@ -485,7 +485,7 @@ SkillComponent::~SkillComponent() { Reset(); } -void SkillComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void SkillComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) outBitStream->Write0(); } diff --git a/dGame/dComponents/SkillComponent.h b/dGame/dComponents/SkillComponent.h index 034e65ce..530c2a25 100644 --- a/dGame/dComponents/SkillComponent.h +++ b/dGame/dComponents/SkillComponent.h @@ -1,6 +1,6 @@ /* * Darkflame Universe - * Copyright 2018 + * Copyright 2024 */ #ifndef SKILLCOMPONENT_H @@ -12,7 +12,7 @@ #include "BitStream.h" #include "Component.h" #include "Entity.h" -#include "dLogger.h" +#include "Logger.h" #include "eReplicaComponentType.h" struct ProjectileSyncEntry { @@ -55,16 +55,16 @@ struct SkillExecutionResult { * * Skills are a built up by a tree of behaviors. See dGame/dBehaviors/ for a list of behaviors. * - * This system is very conveluted and still has a lot of unknowns. + * This system is very convoluted and still has a lot of unknowns. */ -class SkillComponent : public Component { +class SkillComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SKILL; explicit SkillComponent(Entity* parent); ~SkillComponent() override; - static void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Computes skill updates. Invokes CalculateUpdate. diff --git a/dGame/dComponents/SoundTriggerComponent.cpp b/dGame/dComponents/SoundTriggerComponent.cpp index 21d30948..34d2441c 100644 --- a/dGame/dComponents/SoundTriggerComponent.cpp +++ b/dGame/dComponents/SoundTriggerComponent.cpp @@ -1,93 +1,114 @@ #include "SoundTriggerComponent.h" -#include "EntityManager.h" #include "Game.h" +#include "Logger.h" +void MusicCue::Serialize(RakNet::BitStream* outBitStream){ + outBitStream->Write(name.size()); + outBitStream->Write(name.c_str(), name.size()); + outBitStream->Write(result); + outBitStream->Write(boredomTime); +} + +void MusicParameter::Serialize(RakNet::BitStream* outBitStream){ + outBitStream->Write(name.size()); + outBitStream->Write(name.c_str(), name.size()); + outBitStream->Write(value); +} + +void GUIDResults::Serialize(RakNet::BitStream* outBitStream){ + guid.Serialize(outBitStream); + outBitStream->Write(result); +} + +void MixerProgram::Serialize(RakNet::BitStream* outBitStream){ + outBitStream->Write(name.size()); + outBitStream->Write(name.c_str(), name.size()); + outBitStream->Write(result); +} SoundTriggerComponent::SoundTriggerComponent(Entity* parent) : Component(parent) { + const auto musicCueName = parent->GetVar(u"NDAudioMusicCue_Name"); - const auto musicCueBoredomTime = parent->GetVar(u"NDAudioMusicCue_BoredomTime"); + if (!musicCueName.empty()) { + auto newCue = MusicCue(musicCueName); + const auto musicCueBoredomTime = parent->GetVar(u"NDAudioMusicCue_BoredomTime"); + if (musicCueBoredomTime) newCue.boredomTime = musicCueBoredomTime; + this->m_MusicCues.push_back(newCue); + } - this->musicCues.push_back({ - musicCueName, - 1, - musicCueBoredomTime - }); + const auto musicParameterName = parent->GetVar(u"NDAudioMusicParameter_Name"); + if (!musicParameterName.empty()) { + auto newParams = MusicParameter(musicParameterName); + const auto musicParameterValue = parent->GetVar(u"NDAudioMusicParameter_Value"); + if (musicParameterValue) newParams.value = musicParameterValue; + this->m_MusicParameters.push_back(newParams); + } - const auto mixerName = parent->GetVar(u"NDAudioMixerProgram_Name"); - this->mixerPrograms.push_back(mixerName); + const auto guidString = parent->GetVar(u"NDAudioEventGUID"); + if (!guidString.empty()) + this->m_2DAmbientSounds.push_back(GUIDResults(guidString)); const auto guid2String = parent->GetVar(u"NDAudioEventGUID2"); - if (!guid2String.empty()) { - this->guids.emplace_back(guid2String); + if (!guid2String.empty()) + this->m_3DAmbientSounds.push_back(GUIDResults(guid2String)); + + const auto mixerName = parent->GetVar(u"NDAudioMixerProgram_Name"); + if (!mixerName.empty()) this->m_MixerPrograms.push_back(MixerProgram(mixerName)); +} + +void SoundTriggerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { + outBitStream->Write(this->m_Dirty || bIsInitialUpdate); + if (this->m_Dirty || bIsInitialUpdate) { + outBitStream->Write(this->m_MusicCues.size()); + for (auto& musicCue : this->m_MusicCues) { + musicCue.Serialize(outBitStream); + } + + outBitStream->Write(this->m_MusicParameters.size()); + for (auto& musicParam : this->m_MusicParameters) { + musicParam.Serialize(outBitStream); + } + + outBitStream->Write(this->m_2DAmbientSounds.size()); + for (auto twoDAmbientSound : this->m_2DAmbientSounds) { + twoDAmbientSound.Serialize(outBitStream); + } + + outBitStream->Write(this->m_3DAmbientSounds.size()); + for (auto threeDAmbientSound : this->m_3DAmbientSounds) { + threeDAmbientSound.Serialize(outBitStream); + } + + outBitStream->Write(this->m_MixerPrograms.size()); + for (auto& mixerProgram : this->m_MixerPrograms) { + mixerProgram.Serialize(outBitStream); + } + + if (!bIsInitialUpdate) this->m_Dirty = false; } } -SoundTriggerComponent::~SoundTriggerComponent() = default; - -void SoundTriggerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { - if (bIsInitialUpdate) - dirty = true; - - outBitStream->Write(dirty); - - if (dirty) { - outBitStream->Write(this->musicCues.size()); - for (const auto& musicCue : this->musicCues) { - outBitStream->Write(musicCue.name.size()); - outBitStream->Write(musicCue.name.c_str(), musicCue.name.size()); - outBitStream->Write(musicCue.result); - outBitStream->Write(musicCue.boredomTime); - } - - // Unknown part - outBitStream->Write(0); - - // GUID part - outBitStream->Write(this->guids.size()); - - for (const auto guid : this->guids) { - outBitStream->Write(guid.GetData1()); - outBitStream->Write(guid.GetData2()); - outBitStream->Write(guid.GetData3()); - for (const auto& guidSubPart : guid.GetData4()) { - outBitStream->Write(guidSubPart); - } - outBitStream->Write(1); // Unknown - } - - // Mixer program part - outBitStream->Write(this->mixerPrograms.size()); - for (const auto& mixerProgram : mixerPrograms) { - outBitStream->Write(mixerProgram.size()); - outBitStream->Write(mixerProgram.c_str(), mixerProgram.size()); - outBitStream->Write(1); // Unknown - } - - dirty = false; - } -} - -void SoundTriggerComponent::ActivateMusicCue(const std::string& name) { - if (std::find_if(this->musicCues.begin(), this->musicCues.end(), [name](const MusicCue& musicCue) { +void SoundTriggerComponent::ActivateMusicCue(const std::string& name, float bordemTime) { + const auto musicCue = std::find_if(this->m_MusicCues.begin(), this->m_MusicCues.end(), [name](const MusicCue& musicCue) { return musicCue.name == name; - }) == this->musicCues.end()) { - this->musicCues.push_back({ - name, - 1, - -1.0f - }); - dirty = true; + } + ); + + if (musicCue == this->m_MusicCues.end()) { + this->m_MusicCues.push_back(MusicCue(name, bordemTime)); + this->m_Dirty = true; Game::entityManager->SerializeEntity(m_Parent); } } void SoundTriggerComponent::DeactivateMusicCue(const std::string& name) { - const auto musicCue = std::find_if(this->musicCues.begin(), this->musicCues.end(), [name](const MusicCue& musicCue) { + const auto musicCue = std::find_if(this->m_MusicCues.begin(), this->m_MusicCues.end(), [name](const MusicCue& musicCue) { return musicCue.name == name; - }); + } + ); - if (musicCue != this->musicCues.end()) { - this->musicCues.erase(musicCue); - dirty = true; + if (musicCue != this->m_MusicCues.end()) { + this->m_MusicCues.erase(musicCue); + this->m_Dirty = true; Game::entityManager->SerializeEntity(m_Parent); } } diff --git a/dGame/dComponents/SoundTriggerComponent.h b/dGame/dComponents/SoundTriggerComponent.h index 954d8495..48366017 100644 --- a/dGame/dComponents/SoundTriggerComponent.h +++ b/dGame/dComponents/SoundTriggerComponent.h @@ -1,59 +1,77 @@ #pragma once + #include "dCommonVars.h" #include "Entity.h" #include "GUID.h" #include "Component.h" #include "eReplicaComponentType.h" -/** - * Music that should be played by the client - */ struct MusicCue { std::string name; uint32_t result; float boredomTime; + + MusicCue(std::string name, float boredomTime = -1.0, uint32_t result = 1){ + this->name = name; + this->result = result; + this->boredomTime = boredomTime; + }; + + void Serialize(RakNet::BitStream* outBitStream); }; -/** - * Handles specific music triggers like the instruments in Red Block - * Credits to https://github.com/SimonNitzsche/OpCrux-Server/blob/master/src/Entity/Components/SoundTriggerComponent.hpp - */ +struct MusicParameter { + std::string name; + float value; + + MusicParameter(std::string name, float value = 0.0){ + this->name = name; + this->value = value; + } + + void Serialize(RakNet::BitStream* outBitStream); +}; + +struct GUIDResults{ + GUID guid; + uint32_t result; + + GUIDResults(std::string guidString, uint32_t result = 1){ + this->guid = GUID(guidString); + this->result = result; + } + + void Serialize(RakNet::BitStream* outBitStream); +}; + +struct MixerProgram { + std::string name; + uint32_t result; + + MixerProgram(std::string name, uint32_t result = 0){ + this->name = name; + this->result = result; + } + + void Serialize(RakNet::BitStream* outBitStream); +}; + + class SoundTriggerComponent : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; - + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SOUND_TRIGGER; explicit SoundTriggerComponent(Entity* parent); - ~SoundTriggerComponent() override; - - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); - - /** - * Activates a music cue, making it played by any client in range - * @param name the name of the music to play - */ - void ActivateMusicCue(const std::string& name); - - /** - * Deactivates a music cue (if active) - * @param name name of the music to deactivate - */ + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + void ActivateMusicCue(const std::string& name, float bordemTime = -1.0); void DeactivateMusicCue(const std::string& name); private: - /** - * Currently active cues - */ - std::vector musicCues = {}; + std::vector m_MusicCues; + std::vector m_MusicParameters; + std::vector m_2DAmbientSounds; + std::vector m_3DAmbientSounds; + std::vector m_MixerPrograms; - /** - * Currently active mixer programs - */ - std::vector mixerPrograms = {}; - - /** - * GUID found in the LDF - */ - std::vector guids = {}; - bool dirty = false; + bool m_Dirty = false; }; diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index eee54342..25f18a4d 100644 --- a/dGame/dComponents/SwitchComponent.cpp +++ b/dGame/dComponents/SwitchComponent.cpp @@ -10,7 +10,7 @@ SwitchComponent::SwitchComponent(Entity* parent) : Component(parent) { m_ResetTime = m_Parent->GetVarAs(u"switch_reset_time"); - m_Rebuild = m_Parent->GetComponent(); + m_QuickBuild = m_Parent->GetComponent(); } SwitchComponent::~SwitchComponent() { @@ -21,7 +21,7 @@ SwitchComponent::~SwitchComponent() { } } -void SwitchComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void SwitchComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(m_Active); } @@ -39,8 +39,8 @@ bool SwitchComponent::GetActive() const { void SwitchComponent::EntityEnter(Entity* entity) { if (!m_Active) { - if (m_Rebuild) { - if (m_Rebuild->GetState() != eRebuildState::COMPLETED) return; + if (m_QuickBuild) { + if (m_QuickBuild->GetState() != eQuickBuildState::COMPLETED) return; } m_Active = true; if (!m_Parent) return; diff --git a/dGame/dComponents/SwitchComponent.h b/dGame/dComponents/SwitchComponent.h index fde3cfc0..f732a8c1 100644 --- a/dGame/dComponents/SwitchComponent.h +++ b/dGame/dComponents/SwitchComponent.h @@ -5,7 +5,7 @@ #include "Entity.h" #include "GameMessages.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "BouncerComponent.h" #include #include "Component.h" @@ -14,9 +14,9 @@ /** * A component for switches in game, including pet triggered switches. */ -class SwitchComponent : public Component { +class SwitchComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::SWITCH; SwitchComponent(Entity* parent); ~SwitchComponent() override; @@ -25,7 +25,7 @@ public: Entity* GetParentEntity() const; - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; /** * Sets whether the switch is on or off. @@ -75,7 +75,7 @@ private: /** * Attached rebuild component. */ - RebuildComponent* m_Rebuild; + QuickBuildComponent* m_QuickBuild; /** * If the switch is on or off. diff --git a/dGame/dComponents/TriggerComponent.cpp b/dGame/dComponents/TriggerComponent.cpp index ab52c4e5..5d4415f8 100644 --- a/dGame/dComponents/TriggerComponent.cpp +++ b/dGame/dComponents/TriggerComponent.cpp @@ -9,10 +9,10 @@ #include "ControllablePhysicsComponent.h" #include "MissionComponent.h" #include "PhantomPhysicsComponent.h" -#include "Player.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "SkillComponent.h" #include "eEndBehavior.h" +#include "PlayerManager.h" TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo): Component(parent) { @@ -21,10 +21,8 @@ TriggerComponent::TriggerComponent(Entity* parent, const std::string triggerInfo std::vector tokens = GeneralUtils::SplitString(triggerInfo, ':'); - uint32_t sceneID; - GeneralUtils::TryParse(tokens.at(0), sceneID); - uint32_t triggerID; - GeneralUtils::TryParse(tokens.at(1), triggerID); + const auto sceneID = GeneralUtils::TryParse(tokens.at(0)).value_or(0); + const auto triggerID = GeneralUtils::TryParse(tokens.at(1)).value_or(0); m_Trigger = Game::zoneManager->GetZone()->GetTrigger(sceneID, triggerID); @@ -155,7 +153,7 @@ void TriggerComponent::HandleTriggerCommand(LUTriggers::Command* command, Entity case eTriggerCommandType::DEACTIVATE_MIXER_PROGRAM: break; // DEPRECATED BLOCK END default: - Game::logger->LogDebug("TriggerComponent", "Event %i was not handled!", command->id); + LOG_DEBUG("Event %i was not handled!", command->id); break; } } @@ -175,7 +173,7 @@ std::vector TriggerComponent::GatherTargets(LUTriggers::Command* comman } } else if (command->target == "objGroup") entities = Game::entityManager->GetEntitiesInGroup(command->targetName); else if (command->target == "allPlayers") { - for (auto* player : Player::GetAllPlayers()) { + for (auto* player : PlayerManager::GetAllPlayers()) { entities.push_back(player); } } else if (command->target == "allNPCs") { /*UNUSED*/ } @@ -190,35 +188,33 @@ void TriggerComponent::HandleFireEvent(Entity* targetEntity, std::string args) { } void TriggerComponent::HandleDestroyObject(Entity* targetEntity, std::string args){ - uint32_t killType; - GeneralUtils::TryParse(args, killType); - targetEntity->Smash(m_Parent->GetObjectID(), static_cast(killType)); + const eKillType killType = GeneralUtils::TryParse(args).value_or(eKillType::VIOLENT); + targetEntity->Smash(m_Parent->GetObjectID(), killType); } void TriggerComponent::HandleToggleTrigger(Entity* targetEntity, std::string args){ auto* triggerComponent = targetEntity->GetComponent(); if (!triggerComponent) { - Game::logger->LogDebug("TriggerComponent::HandleToggleTrigger", "Trigger component not found!"); + LOG_DEBUG("Trigger component not found!"); return; } triggerComponent->SetTriggerEnabled(args == "1"); } void TriggerComponent::HandleResetRebuild(Entity* targetEntity, std::string args){ - auto* rebuildComponent = targetEntity->GetComponent(); - if (!rebuildComponent) { - Game::logger->LogDebug("TriggerComponent::HandleResetRebuild", "Rebuild component not found!"); + auto* quickBuildComponent = targetEntity->GetComponent(); + if (!quickBuildComponent) { + LOG_DEBUG("Rebuild component not found!"); return; } - rebuildComponent->ResetRebuild(args == "1"); + quickBuildComponent->ResetQuickBuild(args == "1"); } void TriggerComponent::HandleMoveObject(Entity* targetEntity, std::vector argArray){ if (argArray.size() <= 2) return; - auto position = targetEntity->GetPosition(); - NiPoint3 offset = NiPoint3::ZERO; - GeneralUtils::TryParse(argArray.at(0), argArray.at(1), argArray.at(2), offset); + NiPoint3 position = targetEntity->GetPosition(); + const NiPoint3 offset = GeneralUtils::TryParse(argArray).value_or(NiPoint3Constant::ZERO); position += offset; targetEntity->SetPosition(position); @@ -227,8 +223,7 @@ void TriggerComponent::HandleMoveObject(Entity* targetEntity, std::vector argArray){ if (argArray.size() <= 2) return; - NiPoint3 vector = NiPoint3::ZERO; - GeneralUtils::TryParse(argArray.at(0), argArray.at(1), argArray.at(2), vector); + const NiPoint3 vector = GeneralUtils::TryParse(argArray).value_or(NiPoint3Constant::ZERO); NiQuaternion rotation = NiQuaternion::FromEulerAngles(vector); targetEntity->SetRotation(rotation); @@ -239,14 +234,13 @@ void TriggerComponent::HandlePushObject(Entity* targetEntity, std::vectorGetComponent(); if (!phantomPhysicsComponent) { - Game::logger->LogDebug("TriggerComponent::HandlePushObject", "Phantom Physics component not found!"); + LOG_DEBUG("Phantom Physics component not found!"); return; } phantomPhysicsComponent->SetPhysicsEffectActive(true); phantomPhysicsComponent->SetEffectType(ePhysicsEffectType::PUSH); phantomPhysicsComponent->SetDirectionalMultiplier(1); - NiPoint3 direction = NiPoint3::ZERO; - GeneralUtils::TryParse(argArray.at(0), argArray.at(1), argArray.at(2), direction); + const NiPoint3 direction = GeneralUtils::TryParse(argArray).value_or(NiPoint3Constant::ZERO); phantomPhysicsComponent->SetDirection(direction); Game::entityManager->SerializeEntity(m_Parent); @@ -256,11 +250,11 @@ void TriggerComponent::HandlePushObject(Entity* targetEntity, std::vectorGetComponent(); if (!phantomPhysicsComponent) { - Game::logger->LogDebug("TriggerComponent::HandleRepelObject", "Phantom Physics component not found!"); + LOG_DEBUG("Phantom Physics component not found!"); return; } - float forceMultiplier; - GeneralUtils::TryParse(args, forceMultiplier); + const float forceMultiplier = GeneralUtils::TryParse(args).value_or(1.0f); + phantomPhysicsComponent->SetPhysicsEffectActive(true); phantomPhysicsComponent->SetEffectType(ePhysicsEffectType::REPULSE); phantomPhysicsComponent->SetDirectionalMultiplier(forceMultiplier); @@ -279,11 +273,10 @@ void TriggerComponent::HandleRepelObject(Entity* targetEntity, std::string args) void TriggerComponent::HandleSetTimer(Entity* targetEntity, std::vector argArray){ if (argArray.size() != 2) { - Game::logger->LogDebug("TriggerComponent::HandleSetTimer", "Not ehought variables!"); + LOG_DEBUG("Not enough variables!"); return; } - float time = 0.0; - GeneralUtils::TryParse(argArray.at(1), time); + const float time = GeneralUtils::TryParse(argArray.at(1)).value_or(0.0f); m_Parent->AddTimer(argArray.at(0), time); } @@ -299,7 +292,7 @@ void TriggerComponent::HandlePlayCinematic(Entity* targetEntity, std::vector= 2) { - GeneralUtils::TryParse(argArray.at(1), leadIn); + leadIn = GeneralUtils::TryParse(argArray.at(1)).value_or(leadIn); if (argArray.size() >= 3 && argArray.at(2) == "wait") { wait = eEndBehavior::WAIT; if (argArray.size() >= 4 && argArray.at(3) == "unlock") { @@ -320,7 +313,7 @@ void TriggerComponent::HandlePlayCinematic(Entity* targetEntity, std::vectorGetCharacter(); if (!character) { - Game::logger->LogDebug("TriggerComponent::HandleToggleBBB", "Character was not found!"); + LOG_DEBUG("Character was not found!"); return; } bool buildMode = !(character->GetBuildMode()); @@ -336,7 +329,7 @@ void TriggerComponent::HandleUpdateMission(Entity* targetEntity, std::vectorGetComponent(); if (!missionComponent){ - Game::logger->LogDebug("TriggerComponent::HandleUpdateMission", "Mission component not found!"); + LOG_DEBUG("Mission component not found!"); return; } missionComponent->Progress(eMissionTaskType::EXPLORE, 0, 0, argArray.at(4)); @@ -344,29 +337,32 @@ void TriggerComponent::HandleUpdateMission(Entity* targetEntity, std::vector argArray) { if (argArray.size() < 3) return; - int32_t effectID = 0; - if (!GeneralUtils::TryParse(argArray.at(1), effectID)) return; + const auto effectID = GeneralUtils::TryParse(argArray.at(1)); + if (!effectID) return; std::u16string effectType = GeneralUtils::UTF8ToUTF16(argArray.at(2)); + float priority = 1; - if (argArray.size() == 4) GeneralUtils::TryParse(argArray.at(3), priority); - GameMessages::SendPlayFXEffect(targetEntity, effectID, effectType, argArray.at(0), LWOOBJID_EMPTY, priority); + if (argArray.size() == 4) { + priority = GeneralUtils::TryParse(argArray.at(3)).value_or(priority); + } + + GameMessages::SendPlayFXEffect(targetEntity, effectID.value(), effectType, argArray.at(0), LWOOBJID_EMPTY, priority); } void TriggerComponent::HandleCastSkill(Entity* targetEntity, std::string args){ auto* skillComponent = targetEntity->GetComponent(); if (!skillComponent) { - Game::logger->LogDebug("TriggerComponent::HandleCastSkill", "Skill component not found!"); + LOG_DEBUG("Skill component not found!"); return; } - uint32_t skillId; - GeneralUtils::TryParse(args, skillId); + const uint32_t skillId = GeneralUtils::TryParse(args).value_or(0); skillComponent->CastSkill(skillId, targetEntity->GetObjectID()); } void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::vector argArray) { auto* phantomPhysicsComponent = targetEntity->GetComponent(); if (!phantomPhysicsComponent) { - Game::logger->LogDebug("TriggerComponent::HandleSetPhysicsVolumeEffect", "Phantom Physics component not found!"); + LOG_DEBUG("Phantom Physics component not found!"); return; } phantomPhysicsComponent->SetPhysicsEffectActive(true); @@ -381,17 +377,16 @@ void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::v phantomPhysicsComponent->SetEffectType(effectType); phantomPhysicsComponent->SetDirectionalMultiplier(std::stof(argArray.at(1))); if (argArray.size() > 4) { - NiPoint3 direction = NiPoint3::ZERO; - GeneralUtils::TryParse(argArray.at(2), argArray.at(3), argArray.at(4), direction); + const NiPoint3 direction = + GeneralUtils::TryParse(argArray.at(2), argArray.at(3), argArray.at(4)).value_or(NiPoint3Constant::ZERO); + phantomPhysicsComponent->SetDirection(direction); } if (argArray.size() > 5) { - uint32_t min; - GeneralUtils::TryParse(argArray.at(6), min); + const uint32_t min = GeneralUtils::TryParse(argArray.at(6)).value_or(0); phantomPhysicsComponent->SetMin(min); - uint32_t max; - GeneralUtils::TryParse(argArray.at(7), max); + const uint32_t max = GeneralUtils::TryParse(argArray.at(7)).value_or(0); phantomPhysicsComponent->SetMax(max); } @@ -401,7 +396,7 @@ void TriggerComponent::HandleSetPhysicsVolumeEffect(Entity* targetEntity, std::v void TriggerComponent::HandleSetPhysicsVolumeStatus(Entity* targetEntity, std::string args) { auto* phantomPhysicsComponent = targetEntity->GetComponent(); if (!phantomPhysicsComponent) { - Game::logger->LogDebug("TriggerComponent::HandleSetPhysicsVolumeEffect", "Phantom Physics component not found!"); + LOG_DEBUG("Phantom Physics component not found!"); return; } phantomPhysicsComponent->SetPhysicsEffectActive(args == "On"); @@ -438,6 +433,6 @@ void TriggerComponent::HandleActivatePhysics(Entity* targetEntity, std::string a } else if (args == "false"){ // TODO remove Phsyics entity if there is one } else { - Game::logger->LogDebug("TriggerComponent", "Invalid argument for ActivatePhysics Trigger: %s", args.c_str()); + LOG_DEBUG("Invalid argument for ActivatePhysics Trigger: %s", args.c_str()); } } diff --git a/dGame/dComponents/TriggerComponent.h b/dGame/dComponents/TriggerComponent.h index df65707f..5610f24a 100644 --- a/dGame/dComponents/TriggerComponent.h +++ b/dGame/dComponents/TriggerComponent.h @@ -5,9 +5,9 @@ #include "LUTriggers.h" #include "eReplicaComponentType.h" -class TriggerComponent : public Component { +class TriggerComponent final : public Component { public: - static const eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::TRIGGER; explicit TriggerComponent(Entity* parent, const std::string triggerInfo); diff --git a/dGame/dComponents/VendorComponent.cpp b/dGame/dComponents/VendorComponent.cpp index 245ea9b1..afa3d013 100644 --- a/dGame/dComponents/VendorComponent.cpp +++ b/dGame/dComponents/VendorComponent.cpp @@ -16,7 +16,7 @@ VendorComponent::VendorComponent(Entity* parent) : Component(parent) { RefreshInventory(true); } -void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { +void VendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_DirtyVendor); if (bIsInitialUpdate || m_DirtyVendor) { outBitStream->Write(m_HasStandardCostItems); @@ -41,24 +41,23 @@ void VendorComponent::RefreshInventory(bool isCreation) { return; } - auto* lootMatrixTable = CDClientManager::Instance().GetTable(); - const auto lootMatrices = lootMatrixTable->Query([=](CDLootMatrix entry) { return (entry.LootMatrixIndex == m_LootMatrixID); }); + auto* lootMatrixTable = CDClientManager::GetTable(); + const auto& lootMatrices = lootMatrixTable->GetMatrix(m_LootMatrixID); if (lootMatrices.empty()) return; - auto* lootTableTable = CDClientManager::Instance().GetTable(); - auto* itemComponentTable = CDClientManager::Instance().GetTable(); - auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto* lootTableTable = CDClientManager::GetTable(); + auto* itemComponentTable = CDClientManager::GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); for (const auto& lootMatrix : lootMatrices) { - int lootTableID = lootMatrix.LootTableIndex; - auto vendorItems = lootTableTable->Query([=](CDLootTable entry) { return (entry.LootTableIndex == lootTableID); }); + auto vendorItems = lootTableTable->GetTable(lootMatrix.LootTableIndex); if (lootMatrix.maxToDrop == 0 || lootMatrix.minToDrop == 0) { for (const auto& item : vendorItems) { if (!m_HasStandardCostItems || !m_HasMultiCostItems) { auto itemComponentID = compRegistryTable->GetByIDAndType(item.itemid, eReplicaComponentType::ITEM, -1); if (itemComponentID == -1) { - Game::logger->Log("VendorComponent", "Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); + LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); continue; } auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); @@ -78,7 +77,7 @@ void VendorComponent::RefreshInventory(bool isCreation) { if (!m_HasStandardCostItems || !m_HasMultiCostItems) { auto itemComponentID = compRegistryTable->GetByIDAndType(randomItem.itemid, eReplicaComponentType::ITEM, -1); if (itemComponentID == -1) { - Game::logger->Log("VendorComponent", "Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); + LOG("Attempted to add item %i with ItemComponent ID -1 to vendor %i inventory. Not adding item!", itemComponentID, m_Parent->GetLOT()); continue; } auto itemComponent = itemComponentTable->GetItemComponentByID(itemComponentID); @@ -102,10 +101,10 @@ void VendorComponent::RefreshInventory(bool isCreation) { } void VendorComponent::SetupConstants() { - auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); int componentID = compRegistryTable->GetByIDAndType(m_Parent->GetLOT(), eReplicaComponentType::VENDOR); - auto* vendorComponentTable = CDClientManager::Instance().GetTable(); + auto* vendorComponentTable = CDClientManager::GetTable(); std::vector vendorComps = vendorComponentTable->Query([=](CDVendorComponent entry) { return (entry.id == componentID); }); if (vendorComps.empty()) return; auto vendorData = vendorComps.at(0); diff --git a/dGame/dComponents/VendorComponent.h b/dGame/dComponents/VendorComponent.h index 4a9b582e..48b766d2 100644 --- a/dGame/dComponents/VendorComponent.h +++ b/dGame/dComponents/VendorComponent.h @@ -20,9 +20,11 @@ struct SoldItem { class VendorComponent : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; + static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::VENDOR; VendorComponent(Entity* parent); - void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags); + + void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; + void OnUse(Entity* originator) override; void RefreshInventory(bool isCreation = false); void SetupConstants(); diff --git a/dGame/dEntity/CMakeLists.txt b/dGame/dEntity/CMakeLists.txt index 4bb49799..d7d96e14 100644 --- a/dGame/dEntity/CMakeLists.txt +++ b/dGame/dEntity/CMakeLists.txt @@ -1,2 +1,7 @@ -set(DGAME_DENTITY_SOURCES "EntityCallbackTimer.cpp" - "EntityTimer.cpp" PARENT_SCOPE) +set(DGAME_DENTITY_SOURCES + "EntityCallbackTimer.cpp" + "EntityTimer.cpp") + +add_library(dEntity STATIC ${DGAME_DENTITY_SOURCES}) +target_include_directories(dEntity PUBLIC ".") +target_precompile_headers(dEntity REUSE_FROM dGameBase) diff --git a/dGame/dEntity/EntityCallbackTimer.cpp b/dGame/dEntity/EntityCallbackTimer.cpp index e07c1189..95222c4e 100644 --- a/dGame/dEntity/EntityCallbackTimer.cpp +++ b/dGame/dEntity/EntityCallbackTimer.cpp @@ -1,22 +1,10 @@ #include "EntityCallbackTimer.h" -EntityCallbackTimer::EntityCallbackTimer(float time, std::function callback) { +EntityCallbackTimer::EntityCallbackTimer(const float time, const std::function callback) { m_Time = time; m_Callback = callback; } -EntityCallbackTimer::~EntityCallbackTimer() { - -} - -std::function EntityCallbackTimer::GetCallback() { - return m_Callback; -} - -float EntityCallbackTimer::GetTime() { - return m_Time; -} - void EntityCallbackTimer::Update(float deltaTime) { m_Time -= deltaTime; } diff --git a/dGame/dEntity/EntityCallbackTimer.h b/dGame/dEntity/EntityCallbackTimer.h index 2a7e58f0..9b37476b 100644 --- a/dGame/dEntity/EntityCallbackTimer.h +++ b/dGame/dEntity/EntityCallbackTimer.h @@ -5,11 +5,11 @@ class EntityCallbackTimer { public: - EntityCallbackTimer(float time, std::function callback); - ~EntityCallbackTimer(); + EntityCallbackTimer(const float time, const std::function callback); + + std::function GetCallback() const { return m_Callback; }; - std::function GetCallback(); - float GetTime(); + float GetTime() const { return m_Time; }; void Update(float deltaTime); diff --git a/dGame/dEntity/EntityTimer.cpp b/dGame/dEntity/EntityTimer.cpp index 0363fc5b..8ec666b6 100644 --- a/dGame/dEntity/EntityTimer.cpp +++ b/dGame/dEntity/EntityTimer.cpp @@ -1,14 +1,10 @@ #include "EntityTimer.h" -EntityTimer::EntityTimer(std::string name, float time) { +EntityTimer::EntityTimer(const std::string& name, const float time) { m_Name = name; m_Time = time; } -EntityTimer::~EntityTimer() { - -} - std::string EntityTimer::GetName() { return m_Name; } diff --git a/dGame/dEntity/EntityTimer.h b/dGame/dEntity/EntityTimer.h index 9de0345d..d5c4a19e 100644 --- a/dGame/dEntity/EntityTimer.h +++ b/dGame/dEntity/EntityTimer.h @@ -4,8 +4,15 @@ class EntityTimer { public: - EntityTimer(std::string name, float time); - ~EntityTimer(); + EntityTimer(const std::string& name, const float time); + + bool operator==(const EntityTimer& other) const { + return m_Name == other.m_Name; + } + + bool operator==(const std::string& other) const { + return m_Name == other; + } std::string GetName(); float GetTime(); diff --git a/dGame/dGameMessages/CMakeLists.txt b/dGame/dGameMessages/CMakeLists.txt index 3c3cb53f..7373633c 100644 --- a/dGame/dGameMessages/CMakeLists.txt +++ b/dGame/dGameMessages/CMakeLists.txt @@ -1,4 +1,9 @@ -set(DGAME_DGAMEMESSAGES_SOURCES "GameMessageHandler.cpp" +set(DGAME_DGAMEMESSAGES_SOURCES + "GameMessageHandler.cpp" "GameMessages.cpp" "PropertyDataMessage.cpp" - "PropertySelectQueryProperty.cpp" PARENT_SCOPE) + "PropertySelectQueryProperty.cpp") + +add_library(dGameMessages STATIC ${DGAME_DGAMEMESSAGES_SOURCES}) +target_link_libraries(dGameMessages PUBLIC dDatabase) +target_precompile_headers(dGameMessages REUSE_FROM dGameBase) diff --git a/dGame/dGameMessages/EchoStartSkill.h b/dGame/dGameMessages/EchoStartSkill.h index f5dee816..389a81e0 100644 --- a/dGame/dGameMessages/EchoStartSkill.h +++ b/dGame/dGameMessages/EchoStartSkill.h @@ -13,13 +13,13 @@ public: bUsedMouse = false; fCasterLatency = 0.0f; iCastType = 0; - lastClickedPosit = NiPoint3::ZERO; + lastClickedPosit = NiPoint3Constant::ZERO; optionalTargetID = LWOOBJID_EMPTY; - originatorRot = NiQuaternion::IDENTITY; + originatorRot = NiQuaternionConstant::IDENTITY; uiSkillHandle = 0; } - EchoStartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternion::IDENTITY, uint32_t _uiSkillHandle = 0) { + EchoStartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternionConstant::IDENTITY, uint32_t _uiSkillHandle = 0) { bUsedMouse = _bUsedMouse; fCasterLatency = _fCasterLatency; iCastType = _iCastType; @@ -50,16 +50,16 @@ public: stream->Write(iCastType != 0); if (iCastType != 0) stream->Write(iCastType); - stream->Write(lastClickedPosit != NiPoint3::ZERO); - if (lastClickedPosit != NiPoint3::ZERO) stream->Write(lastClickedPosit); + stream->Write(lastClickedPosit != NiPoint3Constant::ZERO); + if (lastClickedPosit != NiPoint3Constant::ZERO) stream->Write(lastClickedPosit); stream->Write(optionalOriginatorID); stream->Write(optionalTargetID != LWOOBJID_EMPTY); if (optionalTargetID != LWOOBJID_EMPTY) stream->Write(optionalTargetID); - stream->Write(originatorRot != NiQuaternion::IDENTITY); - if (originatorRot != NiQuaternion::IDENTITY) stream->Write(originatorRot); + stream->Write(originatorRot != NiQuaternionConstant::IDENTITY); + if (originatorRot != NiQuaternionConstant::IDENTITY) stream->Write(originatorRot); uint32_t sBitStreamLength = sBitStream.length(); stream->Write(sBitStreamLength); diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 99592ed2..64790e31 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -5,9 +5,9 @@ #include "GameMessageHandler.h" #include "MissionComponent.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "dServer.h" -#include "../thirdparty/raknet/Source/RakNetworkFactory.h" +#include "RakNetworkFactory.h" #include #include "User.h" #include "UserManager.h" @@ -18,7 +18,6 @@ #include "Character.h" #include "ControllablePhysicsComponent.h" #include "dZoneManager.h" -#include "Player.h" #include "CppScripts.h" #include "CDClientDatabase.h" @@ -34,8 +33,11 @@ #include "eMissionTaskType.h" #include "eReplicaComponentType.h" #include "eConnectionType.h" - -using namespace std; +#include "eGameMessageType.h" +#include "ePlayerFlag.h" +#include "dConfig.h" +#include "GhostComponent.h" +#include "StringifiedEnum.h" void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID) { @@ -47,11 +49,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System User* usr = UserManager::Instance()->GetUser(sysAddr); if (!entity) { - Game::logger->Log("GameMessageHandler", "Failed to find associated entity (%llu), aborting GM (%X)!", objectID, messageID); + LOG("Failed to find associated entity (%llu), aborting GM: %4i, %s!", objectID, messageID, StringifiedEnum::ToString(messageID).data()); return; } - if (messageID != eGameMessageType::READY_FOR_UPDATES) Game::logger->LogDebug("GameMessageHandler", "received game message ID: %i", messageID); + if (messageID != eGameMessageType::READY_FOR_UPDATES) LOG_DEBUG("Received GM with ID and name: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data()); switch (messageID) { @@ -106,9 +108,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System GameMessages::SendRestoreToPostLoadStats(entity, sysAddr); entity->SetPlayerReadyForUpdates(); - auto* player = dynamic_cast(entity); - if (player != nullptr) { - player->ConstructLimboEntities(); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent != nullptr) { + ghostComponent->ConstructLimboEntities(); } InventoryComponent* inv = entity->GetComponent(); @@ -135,14 +137,14 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System Entity* zoneControl = Game::entityManager->GetZoneControlEntity(); for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) { - script->OnPlayerLoaded(zoneControl, player); + script->OnPlayerLoaded(zoneControl, entity); } std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); for (Entity* scriptEntity : scriptedActs) { if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) { - script->OnPlayerLoaded(scriptEntity, player); + script->OnPlayerLoaded(scriptEntity, entity); } } } @@ -167,12 +169,19 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System character->OnZoneLoad(); } - Game::logger->Log("GameMessageHandler", "Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID()); + LOG("Player %s (%llu) loaded.", entity->GetCharacter()->GetName().c_str(), entity->GetObjectID()); // After we've done our thing, tell the client they're ready GameMessages::SendPlayerReady(entity, sysAddr); GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr); + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" + || !entity->GetCharacter() + || !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { + if (!entity) return; + GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr); + }); break; } @@ -187,8 +196,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System } case eGameMessageType::MISSION_DIALOGUE_CANCELLED: { - //This message is pointless for our implementation, as the client just carries on after - //rejecting a mission offer. We dont need to do anything. This is just here to remove a warning in our logs :) + // This message is pointless for our implementation, as the client just carries on after + // rejecting a mission offer. We dont need to do anything. This is just here to remove a warning in our logs :) break; } @@ -244,13 +253,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System case eGameMessageType::REQUEST_RESURRECT: { GameMessages::SendResurrect(entity); - /*auto* dest = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(4); - dest->SetArmor(0); - dest->SetImagination(6); - Game::entityManager->SerializeEntity(entity); - }*/ break; } case eGameMessageType::GET_HOT_PROPERTY_DATA: { @@ -267,7 +269,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System auto* skill_component = entity->GetComponent(); if (skill_component != nullptr) { - auto* bs = new RakNet::BitStream((unsigned char*)message.sBitStream.c_str(), message.sBitStream.size(), false); + auto* bs = new RakNet::BitStream(reinterpret_cast(const_cast(message.sBitStream.c_str())), message.sBitStream.size(), false); skill_component->SyncPlayerProjectile(message.i64LocalID, bs, message.i64TargetID); @@ -288,13 +290,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System comp->Progress(eMissionTaskType::USE_SKILL, startSkill.skillID); } - CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable(); + CDSkillBehaviorTable* skillTable = CDClientManager::GetTable(); unsigned int behaviorId = skillTable->GetSkillByID(startSkill.skillID).behaviorID; bool success = false; if (behaviorId > 0) { - RakNet::BitStream* bs = new RakNet::BitStream((unsigned char*)startSkill.sBitStream.c_str(), startSkill.sBitStream.size(), false); + RakNet::BitStream* bs = new RakNet::BitStream(reinterpret_cast(const_cast(startSkill.sBitStream.c_str())), startSkill.sBitStream.size(), false); auto* skillComponent = entity->GetComponent(); @@ -315,7 +317,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System if (success) { //Broadcast our startSkill: RakNet::BitStream bitStreamLocal; - PacketUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); + BitStreamUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); bitStreamLocal.Write(entity->GetObjectID()); EchoStartSkill echoStartSkill; @@ -337,26 +339,21 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System case eGameMessageType::SYNC_SKILL: { RakNet::BitStream bitStreamLocal; - PacketUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); + BitStreamUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); bitStreamLocal.Write(entity->GetObjectID()); - //bitStreamLocal.Write((unsigned short)eGameMessageType::ECHO_SYNC_SKILL); - //bitStreamLocal.Write(inStream); SyncSkill sync = SyncSkill(inStream); // inStream replaced &bitStream - //sync.Serialize(&bitStreamLocal); - ostringstream buffer; + std::ostringstream buffer; for (unsigned int k = 0; k < sync.sBitStream.size(); k++) { char s; s = sync.sBitStream.at(k); - buffer << setw(2) << hex << setfill('0') << (int)s << " "; + buffer << std::setw(2) << std::hex << std::setfill('0') << static_cast(s) << " "; } - //cout << buffer.str() << endl; - if (usr != nullptr) { - RakNet::BitStream* bs = new RakNet::BitStream((unsigned char*)sync.sBitStream.c_str(), sync.sBitStream.size(), false); + RakNet::BitStream* bs = new RakNet::BitStream(reinterpret_cast(const_cast(sync.sBitStream.c_str())), sync.sBitStream.size(), false); auto* skillComponent = entity->GetComponent(); @@ -425,7 +422,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System break; case eGameMessageType::REBUILD_CANCEL: - GameMessages::HandleRebuildCancel(inStream, entity); + GameMessages::HandleQuickBuildCancel(inStream, entity); break; case eGameMessageType::MATCH_REQUEST: @@ -693,8 +690,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System case eGameMessageType::CANCEL_DONATION_ON_PLAYER: GameMessages::HandleCancelDonationOnPlayer(inStream, entity); break; + case eGameMessageType::REQUEST_VENDOR_STATUS_UPDATE: + GameMessages::SendVendorStatusUpdate(entity, sysAddr, true); + break; default: - Game::logger->LogDebug("GameMessageHandler", "Unknown game message ID: %i", messageID); + LOG_DEBUG("Received Unknown GM with ID: %4i, %s", messageID, StringifiedEnum::ToString(messageID).data()); break; } } diff --git a/dGame/dGameMessages/GameMessageHandler.h b/dGame/dGameMessages/GameMessageHandler.h index 8b6685cb..aed3d496 100644 --- a/dGame/dGameMessages/GameMessageHandler.h +++ b/dGame/dGameMessages/GameMessageHandler.h @@ -16,11 +16,10 @@ #include "Entity.h" #include "EntityManager.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "GameMessages.h" -#include "../dDatabase/CDClientDatabase.h" - -enum class eGameMessageType : uint16_t; +#include "CDClientDatabase.h" +#include "eGameMessageType.h" namespace GameMessageHandler { void HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID); diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 19557019..d0a7dfa2 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1,30 +1,29 @@ #include "GameMessages.h" #include "User.h" #include "Entity.h" -#include "PacketUtils.h" +#include "BitStreamUtils.h" #include "BitStream.h" #include "Game.h" #include "SlashCommandHandler.h" #include "NiPoint3.h" #include "NiQuaternion.h" -#include "dLogger.h" +#include "Logger.h" #include "GeneralUtils.h" #include "Character.h" #include "EntityManager.h" #include "Database.h" #include "dServer.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "CppScripts.h" #include "UserManager.h" #include "ZoneInstanceManager.h" #include "WorldPackets.h" #include "Item.h" #include "ZCompression.h" -#include "Player.h" #include "dConfig.h" #include "TeamManager.h" #include "ChatPackets.h" -#include "RocketLaunchLupComponent.h" +#include "MultiZoneEntranceComponent.h" #include "eUnequippableActiveType.h" #include "LeaderboardManager.h" #include "Amf3.h" @@ -41,13 +40,14 @@ #include "eControlScheme.h" #include "eStateChangeType.h" #include "eConnectionType.h" +#include "ePlayerFlag.h" #include #include #include #include "RakString.h" -#include "../thirdparty/cpp-httplib/httplib.h" //sorry not sorry. +#include "httplib.h" //sorry not sorry. //CDB includes: #include "CDClientManager.h" @@ -60,7 +60,7 @@ #include "MissionComponent.h" #include "DestroyableComponent.h" #include "ScriptComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "VendorComponent.h" #include "InventoryComponent.h" #include "RocketLaunchpadControlComponent.h" @@ -68,7 +68,7 @@ #include "MovingPlatformComponent.h" #include "PetComponent.h" #include "ModuleAssemblyComponent.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "RenderComponent.h" #include "PossessableComponent.h" #include "PossessorComponent.h" @@ -76,6 +76,7 @@ #include "RailActivatorComponent.h" #include "LevelProgressionComponent.h" #include "DonationVendorComponent.h" +#include "GhostComponent.h" // Message includes: #include "dZoneManager.h" @@ -92,7 +93,9 @@ #include "eReplicaComponentType.h" #include "eClientMessageType.h" #include "eGameMessageType.h" +#include "ePetAbilityType.h" #include "ActivityManager.h" +#include "PlayerManager.h" #include "CDComponentsRegistryTable.h" #include "CDObjectsTable.h" @@ -108,7 +111,7 @@ void GameMessages::SendFireEventClientSide(const LWOOBJID& objectID, const Syste uint32_t argSize = args.size(); bitStream.Write(argSize); for (uint32_t k = 0; k < argSize; k++) { - bitStream.Write(static_cast(args[k])); + bitStream.Write(args[k]); } bitStream.Write(object); bitStream.Write0(); @@ -154,7 +157,7 @@ void GameMessages::SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, c void GameMessages::SendPlayAnimation(Entity* entity, const std::u16string& animationName, float fPriority, float fScale) { if (!entity) { - Game::logger->Log("SendPlayAnimation", "Trying to play animation, but entity var is nullptr!"); + LOG("Trying to play animation, but entity var is nullptr!"); return; } @@ -172,7 +175,7 @@ void GameMessages::SendPlayAnimation(Entity* entity, const std::u16string& anima bitStream.Write(eGameMessageType::PLAY_ANIMATION); bitStream.Write(animationIDLength); - PacketUtils::WriteWString(bitStream, animationName, animationIDLength); + bitStream.Write(LUWString(animationName, animationIDLength)); bitStream.Write(bExpectAnimToExist); @@ -218,13 +221,13 @@ void GameMessages::SendInvalidZoneTransferList(Entity* entity, const SystemAddre uint32_t CustomerFeedbackURLLength = feedbackURL.size(); bitStream.Write(CustomerFeedbackURLLength); for (uint32_t k = 0; k < CustomerFeedbackURLLength; k++) { - bitStream.Write(static_cast(feedbackURL[k])); + bitStream.Write(feedbackURL[k]); } uint32_t InvalidMapTransferListLength = invalidMapTransferList.size(); bitStream.Write(InvalidMapTransferListLength); for (uint32_t k = 0; k < InvalidMapTransferListLength; k++) { - bitStream.Write(static_cast(invalidMapTransferList[k])); + bitStream.Write(invalidMapTransferList[k]); } bitStream.Write(feedbackOnExit); @@ -315,24 +318,17 @@ void GameMessages::SendPlayNDAudioEmitter(Entity* entity, const SystemAddress& s CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLAY_ND_AUDIO_EMITTER); + bitStream.Write(eGameMessageType::PLAY_ND_AUDIO_EMITTER); bitStream.Write0(); bitStream.Write0(); uint32_t length = audioGUID.size(); bitStream.Write(length); for (uint32_t k = 0; k < length; k++) { - bitStream.Write(static_cast(audioGUID[k])); + bitStream.Write(audioGUID[k]); } - //PacketUtils::WriteString(bitStream, audioGUID, audioGUID.size()); - - //bitStream.Write(uint32_t(audioGUID.size())); - //for (char character : audioGUID) { - // bitStream.Write(character); - //} - - bitStream.Write(uint32_t(0)); + bitStream.Write(0); bitStream.Write0(); bitStream.Write0(); @@ -349,6 +345,19 @@ void GameMessages::SendStartPathing(Entity* entity) { SEND_PACKET_BROADCAST; } +void GameMessages::SendResetMissions(Entity* entity, const SystemAddress& sysAddr, const int32_t missionid) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(entity->GetObjectID()); + bitStream.Write(eGameMessageType::RESET_MISSIONS); + + bitStream.Write(missionid != -1); + if (missionid != -1) bitStream.Write(missionid); + + SEND_PACKET; +} + void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAddr, eMovementPlatformState movementState, bool bStopAtDesiredWaypoint, int iIndex, int iDesiredWaypointIndex, int nextIndex) { @@ -358,7 +367,7 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd const auto lot = entity->GetLOT(); bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLATFORM_RESYNC); + bitStream.Write(eGameMessageType::PLATFORM_RESYNC); auto* movingPlatformComponent = entity->GetComponent(); if (!movingPlatformComponent) return; @@ -411,7 +420,7 @@ void GameMessages::SendChatModeUpdate(const LWOOBJID& objectID, eGameMasterLevel CBITSTREAM; CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::UPDATE_CHAT_MODE); + bitStream.Write(eGameMessageType::UPDATE_CHAT_MODE); bitStream.Write(level); SEND_PACKET_BROADCAST; } @@ -420,7 +429,7 @@ void GameMessages::SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLev CBITSTREAM; CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::SET_GM_LEVEL); + bitStream.Write(eGameMessageType::SET_GM_LEVEL); bitStream.Write1(); bitStream.Write(level); SEND_PACKET_BROADCAST; @@ -451,9 +460,9 @@ void GameMessages::SendAddItemToInventoryClientSync(Entity* entity, const System bitStream.Write(extraInfo.name.size()); if (extraInfo.name.size() > 0) { for (uint32_t i = 0; i < extraInfo.name.size(); ++i) { - bitStream.Write(static_cast(extraInfo.name[i])); + bitStream.Write(extraInfo.name[i]); } - bitStream.Write(static_cast(0x00)); + bitStream.Write(0x00); } bitStream.Write(item->GetLot()); @@ -490,7 +499,7 @@ void GameMessages::SendNotifyClientFlagChange(const LWOOBJID& objectID, uint32_t CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::NOTIFY_CLIENT_FLAG_CHANGE); + bitStream.Write(eGameMessageType::NOTIFY_CLIENT_FLAG_CHANGE); bitStream.Write(bFlag); bitStream.Write(iFlagID); @@ -502,7 +511,7 @@ void GameMessages::SendChangeObjectWorldState(const LWOOBJID& objectID, eObjectW CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::CHANGE_OBJECT_WORLD_STATE); + bitStream.Write(eGameMessageType::CHANGE_OBJECT_WORLD_STATE); bitStream.Write(state); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST @@ -557,11 +566,11 @@ void GameMessages::SendNotifyMissionTask(Entity* entity, const SystemAddress& sy CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::NOTIFY_MISSION_TASK); + bitStream.Write(eGameMessageType::NOTIFY_MISSION_TASK); bitStream.Write(missionID); bitStream.Write(taskMask); - bitStream.Write((unsigned char)updates.size()); + bitStream.Write(updates.size()); for (uint32_t i = 0; i < updates.size(); ++i) { bitStream.Write(updates[i]); @@ -575,7 +584,7 @@ void GameMessages::SendModifyLEGOScore(Entity* entity, const SystemAddress& sysA CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::MODIFY_LEGO_SCORE); + bitStream.Write(eGameMessageType::MODIFY_LEGO_SCORE); bitStream.Write(score); bitStream.Write(sourceType != eLootSourceType::NONE); @@ -589,14 +598,33 @@ void GameMessages::SendUIMessageServerToSingleClient(Entity* entity, const Syste CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::UI_MESSAGE_SERVER_TO_SINGLE_CLIENT); + bitStream.Write(eGameMessageType::UI_MESSAGE_SERVER_TO_SINGLE_CLIENT); bitStream.Write(args); uint32_t strMessageNameLength = message.size(); bitStream.Write(strMessageNameLength); for (uint32_t k = 0; k < strMessageNameLength; k++) { - bitStream.Write(static_cast(message[k])); + bitStream.Write(message[k]); + } + + SEND_PACKET; +} + +void GameMessages::SendUIMessageServerToSingleClient(const std::string& message, AMFBaseValue& args, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + LWOOBJID empty = 0; + bitStream.Write(empty); + bitStream.Write(eGameMessageType::UI_MESSAGE_SERVER_TO_ALL_CLIENTS); // This is intentional to allow the server to send a ui message to a client via their system address. + + bitStream.Write(args); + uint32_t strMessageNameLength = message.size(); + bitStream.Write(strMessageNameLength); + + for (uint32_t k = 0; k < strMessageNameLength; k++) { + bitStream.Write(message[k]); } SEND_PACKET; @@ -608,14 +636,14 @@ void GameMessages::SendUIMessageServerToAllClients(const std::string& message, A LWOOBJID empty = 0; bitStream.Write(empty); - bitStream.Write((uint16_t)eGameMessageType::UI_MESSAGE_SERVER_TO_ALL_CLIENTS); + bitStream.Write(eGameMessageType::UI_MESSAGE_SERVER_TO_ALL_CLIENTS); bitStream.Write(args); uint32_t strMessageNameLength = message.size(); bitStream.Write(strMessageNameLength); for (uint32_t k = 0; k < strMessageNameLength; k++) { - bitStream.Write(static_cast(message[k])); + bitStream.Write(message[k]); } SEND_PACKET_BROADCAST; @@ -626,11 +654,11 @@ void GameMessages::SendPlayEmbeddedEffectOnAllClientsNearObject(Entity* entity, CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLAY_EMBEDDED_EFFECT_ON_ALL_CLIENTS_NEAR_OBJECT); + bitStream.Write(eGameMessageType::PLAY_EMBEDDED_EFFECT_ON_ALL_CLIENTS_NEAR_OBJECT); - bitStream.Write(static_cast(effectName.length())); + bitStream.Write(effectName.length()); for (uint32_t k = 0; k < effectName.length(); k++) { - bitStream.Write(static_cast(effectName[k])); + bitStream.Write(effectName[k]); } bitStream.Write(fromObjectID); bitStream.Write(radius); @@ -647,7 +675,7 @@ void GameMessages::SendPlayFXEffect(const LWOOBJID& entity, int32_t effectID, co CMSGHEADER; bitStream.Write(entity); - bitStream.Write((uint16_t)eGameMessageType::PLAY_FX_EFFECT); + bitStream.Write(eGameMessageType::PLAY_FX_EFFECT); bitStream.Write(effectID != -1); if (effectID != -1) bitStream.Write(effectID); @@ -695,22 +723,22 @@ void GameMessages::SendBroadcastTextToChatbox(Entity* entity, const SystemAddres CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::BROADCAST_TEXT_TO_CHATBOX); + bitStream.Write(eGameMessageType::BROADCAST_TEXT_TO_CHATBOX); LWONameValue attribs; attribs.name = attrs; attribs.length = attrs.size(); - bitStream.Write(static_cast(attribs.length)); + bitStream.Write(attribs.length); for (uint32_t i = 0; i < attribs.length; ++i) { - bitStream.Write(static_cast(attribs.name[i])); + bitStream.Write(attribs.name[i]); } - bitStream.Write(static_cast(0x00)); // Null Terminator + bitStream.Write(0x00); // Null Terminator uint32_t wsTextLength = wsText.size(); bitStream.Write(wsTextLength); for (uint32_t k = 0; k < wsTextLength; k++) { - bitStream.Write(static_cast(wsText[k])); + bitStream.Write(wsText[k]); } SEND_PACKET_BROADCAST; @@ -728,7 +756,7 @@ void GameMessages::SendSetCurrency(Entity* entity, int64_t currency, int lootTyp bitStream.Write(lootType != LOOTTYPE_NONE); if (lootType != LOOTTYPE_NONE) bitStream.Write(lootType); - bitStream.Write(NiPoint3::ZERO); + bitStream.Write(NiPoint3Constant::ZERO); bitStream.Write(sourceLOT != LOT_NULL); if (sourceLOT != LOT_NULL) bitStream.Write(sourceLOT); @@ -746,12 +774,12 @@ void GameMessages::SendSetCurrency(Entity* entity, int64_t currency, int lootTyp SEND_PACKET; } -void GameMessages::SendRebuildNotifyState(Entity* entity, eRebuildState prevState, eRebuildState state, const LWOOBJID& playerID) { +void GameMessages::SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID) { CBITSTREAM; CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::REBUILD_NOTIFY_STATE); + bitStream.Write(eGameMessageType::REBUILD_NOTIFY_STATE); bitStream.Write(prevState); bitStream.Write(state); @@ -760,12 +788,12 @@ void GameMessages::SendRebuildNotifyState(Entity* entity, eRebuildState prevStat SEND_PACKET_BROADCAST; } -void GameMessages::SendEnableRebuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID) { +void GameMessages::SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID) { CBITSTREAM; CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::ENABLE_REBUILD); + bitStream.Write(eGameMessageType::ENABLE_REBUILD); bitStream.Write(enable); bitStream.Write(fail); @@ -785,7 +813,7 @@ void GameMessages::SendTerminateInteraction(const LWOOBJID& objectID, eTerminate CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::TERMINATE_INTERACTION); + bitStream.Write(eGameMessageType::TERMINATE_INTERACTION); bitStream.Write(terminator); bitStream.Write(type); @@ -821,7 +849,7 @@ void GameMessages::SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOB bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::DIE); + bitStream.Write(eGameMessageType::DIE); bitStream.Write(bClientDeath); bitStream.Write(bSpawnLoot); @@ -832,7 +860,7 @@ void GameMessages::SendDie(Entity* entity, const LWOOBJID& killerID, const LWOOB uint32_t deathTypeLength = deathType.size(); bitStream.Write(deathTypeLength); for (uint32_t k = 0; k < deathTypeLength; k++) { - bitStream.Write(static_cast(deathType[k])); + bitStream.Write(deathType[k]); } bitStream.Write(directionRelative_AngleXZ); @@ -938,13 +966,6 @@ void GameMessages::SendResurrect(Entity* entity) { } }); - - auto cont = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (cont && entity->GetLOT() == 1) { - cont->SetPosition(entity->GetRespawnPosition()); - cont->SetRotation(entity->GetRespawnRotation()); - } - CBITSTREAM; CMSGHEADER; @@ -962,7 +983,7 @@ void GameMessages::SendStop2DAmbientSound(Entity* entity, bool force, std::strin CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::STOP2_D_AMBIENT_SOUND); + bitStream.Write(eGameMessageType::STOP2_D_AMBIENT_SOUND); uint32_t audioGUIDSize = audioGUID.size(); @@ -970,7 +991,7 @@ void GameMessages::SendStop2DAmbientSound(Entity* entity, bool force, std::strin bitStream.Write(audioGUIDSize); for (uint32_t k = 0; k < audioGUIDSize; k++) { - bitStream.Write(static_cast(audioGUID[k])); + bitStream.Write(audioGUID[k]); } bitStream.Write(result); @@ -985,13 +1006,12 @@ void GameMessages::SendPlay2DAmbientSound(Entity* entity, std::string audioGUID, CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLAY2_D_AMBIENT_SOUND); - + bitStream.Write(eGameMessageType::PLAY2_D_AMBIENT_SOUND); uint32_t audioGUIDSize = audioGUID.size(); bitStream.Write(audioGUIDSize); for (uint32_t k = 0; k < audioGUIDSize; k++) { - bitStream.Write(static_cast(audioGUID[k])); + bitStream.Write(audioGUID[k]); } bitStream.Write(result); @@ -1004,7 +1024,7 @@ void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress& CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::SCRIPT_NETWORK_VAR_UPDATE); + bitStream.Write(eGameMessageType::SCRIPT_NETWORK_VAR_UPDATE); // FIXME: this is a bad place to need to do a conversion because we have no clue whether data is utf8 or plain ascii // an this has performance implications @@ -1013,16 +1033,16 @@ void GameMessages::SendSetNetworkScriptVar(Entity* entity, const SystemAddress& bitStream.Write(dataSize); for (auto value : u16Data) { - bitStream.Write(uint16_t(value)); + bitStream.Write(value); } - if (dataSize > 0) bitStream.Write(uint16_t(0)); + if (dataSize > 0) bitStream.Write(0); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; SEND_PACKET; } void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos, int count) { - if (Game::config->GetValue("disable_drops") == "1") { + if (Game::config->GetValue("disable_drops") == "1" || !entity) { return; } @@ -1032,7 +1052,7 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LWOOBJID owner = entity->GetObjectID(); if (item != LOT_NULL && item != 0) { - lootID = ObjectIDManager::Instance()->GenerateObjectID(); + lootID = ObjectIDManager::GenerateObjectID(); Loot::Info info; info.id = lootID; @@ -1045,7 +1065,7 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, entity->RegisterCoinDrop(currency); } - if (spawnPos != NiPoint3::ZERO) { + if (spawnPos != NiPoint3Constant::ZERO) { bUsePosition = true; //Calculate where the loot will go: @@ -1067,8 +1087,8 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, bitStream.Write(bUsePosition); - bitStream.Write(finalPosition != NiPoint3::ZERO); - if (finalPosition != NiPoint3::ZERO) bitStream.Write(finalPosition); + bitStream.Write(finalPosition != NiPoint3Constant::ZERO); + if (finalPosition != NiPoint3Constant::ZERO) bitStream.Write(finalPosition); bitStream.Write(currency); bitStream.Write(item); @@ -1076,14 +1096,14 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, bitStream.Write(owner); bitStream.Write(sourceID); - bitStream.Write(spawnPos != NiPoint3::ZERO); - if (spawnPos != NiPoint3::ZERO) bitStream.Write(spawnPos); + bitStream.Write(spawnPos != NiPoint3Constant::ZERO); + if (spawnPos != NiPoint3Constant::ZERO) bitStream.Write(spawnPos); auto* team = TeamManager::Instance()->GetTeam(owner); // Currency and powerups should not sync if (team != nullptr && currency == 0) { - CDObjectsTable* objectsTable = CDClientManager::Instance().GetTable(); + CDObjectsTable* objectsTable = CDClientManager::GetTable(); const CDObjects& object = objectsTable->GetByID(item); @@ -1130,29 +1150,27 @@ void GameMessages::SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPo CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLAYER_REACHED_RESPAWN_CHECKPOINT); + bitStream.Write(eGameMessageType::PLAYER_REACHED_RESPAWN_CHECKPOINT); bitStream.Write(position.x); bitStream.Write(position.y); bitStream.Write(position.z); - auto con = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (con) { - auto rot = con->GetRotation(); - bitStream.Write(rot.x); - bitStream.Write(rot.y); - bitStream.Write(rot.z); - bitStream.Write(rot.w); - } + const bool bIsNotIdentity = rotation != NiQuaternionConstant::IDENTITY; + bitStream.Write(bIsNotIdentity); - //bitStream.Write(position); - //bitStream.Write(rotation); + if (bIsNotIdentity) { + bitStream.Write(rotation.w); + bitStream.Write(rotation.x); + bitStream.Write(rotation.y); + bitStream.Write(rotation.z); + } SystemAddress sysAddr = entity->GetSystemAddress(); SEND_PACKET; } -void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, int slotID) { +void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, BehaviorSlot slotID) { int AICombatWeight = 0; bool bFromSkillSet = false; int castType = 0; @@ -1164,7 +1182,7 @@ void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, int slotID) { CMSGHEADER; bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::ADD_SKILL); + bitStream.Write(eGameMessageType::ADD_SKILL); bitStream.Write(AICombatWeight != 0); if (AICombatWeight != 0) bitStream.Write(AICombatWeight); @@ -1182,8 +1200,8 @@ void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, int slotID) { bitStream.Write(skillID); - bitStream.Write(slotID != -1); - if (slotID != -1) bitStream.Write(slotID); + bitStream.Write(slotID != BehaviorSlot::Invalid); + if (slotID != BehaviorSlot::Invalid) bitStream.Write(slotID); bitStream.Write(temporary); @@ -1333,8 +1351,8 @@ void GameMessages::SendRemoveItemFromInventory(Entity* entity, const SystemAddre bitStream.Write(eInvType); bitStream.Write1(); bitStream.Write(eLootTypeSource); - bitStream.Write(static_cast(0)); //extra info - //bitStream.Write(static_cast(0)); //extra info + bitStream.Write(0); //extra info + //bitStream.Write(0); //extra info bitStream.Write(forceDeletion); bitStream.Write0(); bitStream.Write1(); @@ -1457,11 +1475,11 @@ void GameMessages::SendMatchUpdate(Entity* entity, const SystemAddress& sysAddr, bitStream.Write(entity->GetObjectID()); bitStream.Write(eGameMessageType::MATCH_UPDATE); - bitStream.Write(uint32_t(data.size())); + bitStream.Write(data.size()); for (char character : data) { - bitStream.Write(uint16_t(character)); + bitStream.Write(character); } - if (data.size() > 0) bitStream.Write(uint16_t(0)); + if (data.size() > 0) bitStream.Write(0); bitStream.Write(type); SEND_PACKET; @@ -1545,7 +1563,7 @@ void GameMessages::NotifyLevelRewards(LWOOBJID objectID, const SystemAddress& sy CMSGHEADER; bitStream.Write(objectID); - bitStream.Write((uint16_t)eGameMessageType::NOTIFY_LEVEL_REWARDS); + bitStream.Write(eGameMessageType::NOTIFY_LEVEL_REWARDS); bitStream.Write(level); bitStream.Write(sending_rewards); @@ -1614,8 +1632,8 @@ void GameMessages::SendNotifyClientShootingGalleryScore(LWOOBJID objectId, const void GameMessages::HandleUpdateShootingGalleryRotation(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { float angle = 0.0f; - NiPoint3 facing = NiPoint3::ZERO; - NiPoint3 muzzlePos = NiPoint3::ZERO; + NiPoint3 facing = NiPoint3Constant::ZERO; + NiPoint3 muzzlePos = NiPoint3Constant::ZERO; inStream->Read(angle); inStream->Read(facing); inStream->Read(muzzlePos); @@ -1624,7 +1642,7 @@ void GameMessages::HandleUpdateShootingGalleryRotation(RakNet::BitStream* inStre void GameMessages::HandleActivitySummaryLeaderboardData(RakNet::BitStream* instream, Entity* entity, const SystemAddress& sysAddr) { - Game::logger->Log("AGS", "We got mail!"); + LOG("We got mail!"); } void GameMessages::SendActivitySummaryLeaderboardData(const LWOOBJID& objectID, const Leaderboard* leaderboard, const SystemAddress& sysAddr) { @@ -1681,7 +1699,7 @@ void GameMessages::HandleActivityStateChangeRequest(RakNet::BitStream* inStream, auto* assosiate = Game::entityManager->GetEntity(objectID); - Game::logger->Log("Activity State Change", "%s [%i, %i] from %i to %i", GeneralUtils::UTF16ToWTF8(stringValue).c_str(), value1, value2, entity->GetLOT(), assosiate != nullptr ? assosiate->GetLOT() : 0); + LOG("%s [%i, %i] from %i to %i", GeneralUtils::UTF16ToWTF8(stringValue).c_str(), value1, value2, entity->GetLOT(), assosiate != nullptr ? assosiate->GetLOT() : 0); std::vector scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SHOOTING_GALLERY); for (Entity* scriptEntity : scriptedActs) { @@ -1715,8 +1733,6 @@ void GameMessages::SendStartCelebrationEffect(Entity* entity, const SystemAddres bitStream.Write(0); //subtext SEND_PACKET; - - //PacketUtils::SavePacket("StartCelebrationEffect.bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } @@ -1731,7 +1747,7 @@ void GameMessages::SendSetRailMovement(const LWOOBJID& objectID, bool pathGoForw bitStream.Write(pathGoForward); - bitStream.Write(uint32_t(pathName.size())); + bitStream.Write(pathName.size()); for (auto character : pathName) { bitStream.Write(character); } @@ -1770,14 +1786,14 @@ void GameMessages::SendStartRailMovement(const LWOOBJID& objectID, std::u16strin bitStream.Write(cameraLocked); bitStream.Write(collisionEnabled); - bitStream.Write(uint32_t(loopSound.size())); + bitStream.Write(loopSound.size()); for (auto character : loopSound) { bitStream.Write(character); } bitStream.Write(goForward); - bitStream.Write(uint32_t(pathName.size())); + bitStream.Write(pathName.size()); for (auto character : pathName) { bitStream.Write(character); } @@ -1800,12 +1816,12 @@ void GameMessages::SendStartRailMovement(const LWOOBJID& objectID, std::u16strin bitStream.Write(railActivatorObjectID); } - bitStream.Write(uint32_t(startSound.size())); + bitStream.Write(startSound.size()); for (auto character : startSound) { bitStream.Write(character); } - bitStream.Write(uint32_t(stopSound.size())); + bitStream.Write(stopSound.size()); for (auto character : stopSound) { bitStream.Write(character); } @@ -1823,7 +1839,7 @@ void GameMessages::SendNotifyClientObject(const LWOOBJID& objectID, std::u16stri bitStream.Write(objectID); bitStream.Write(eGameMessageType::NOTIFY_CLIENT_OBJECT); - bitStream.Write(uint32_t(name.size())); + bitStream.Write(name.size()); for (auto character : name) { bitStream.Write(character); } @@ -1834,7 +1850,7 @@ void GameMessages::SendNotifyClientObject(const LWOOBJID& objectID, std::u16stri bitStream.Write(paramObj); - bitStream.Write(uint32_t(paramStr.size())); + bitStream.Write(paramStr.size()); for (auto character : paramStr) { bitStream.Write(character); } @@ -1852,7 +1868,7 @@ void GameMessages::SendNotifyClientZoneObject(const LWOOBJID& objectID, const st bitStream.Write(objectID); bitStream.Write(eGameMessageType::NOTIFY_CLIENT_ZONE_OBJECT); - bitStream.Write(uint32_t(name.size())); + bitStream.Write(name.size()); for (const auto& character : name) { bitStream.Write(character); } @@ -1861,7 +1877,7 @@ void GameMessages::SendNotifyClientZoneObject(const LWOOBJID& objectID, const st bitStream.Write(param2); bitStream.Write(paramObj); - bitStream.Write(uint32_t(paramStr.size())); + bitStream.Write(paramStr.size()); for (const auto& character : paramStr) { bitStream.Write(character); } @@ -1878,9 +1894,9 @@ void GameMessages::SendNotifyClientFailedPrecondition(LWOOBJID objectId, const S bitStream.Write(objectId); bitStream.Write(eGameMessageType::NOTIFY_CLIENT_FAILED_PRECONDITION); - bitStream.Write(uint32_t(failedReason.size())); + bitStream.Write(failedReason.size()); for (uint16_t character : failedReason) { - bitStream.Write(uint16_t(character)); + bitStream.Write(character); } bitStream.Write(preconditionID); @@ -1939,7 +1955,6 @@ void GameMessages::SendBBBSaveResponse(const LWOOBJID& objectId, const LWOOBJID& bitStream.Write(buffer[i]); SEND_PACKET; - PacketUtils::SavePacket("eGameMessageType::BBB_SAVE_RESPONSE.bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } // Property @@ -1975,7 +1990,7 @@ void GameMessages::SendDownloadPropertyData(const LWOOBJID objectId, const Prope data.Serialize(bitStream); - Game::logger->Log("SendDownloadPropertyData", "(%llu) sending property data (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); + LOG("(%llu) sending property data (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; SEND_PACKET; @@ -2004,7 +2019,7 @@ void GameMessages::SendLockNodeRotation(Entity* entity, std::string nodeName) { bitStream.Write(entity->GetObjectID()); bitStream.Write(eGameMessageType::LOCK_NODE_ROTATION); - bitStream.Write(uint32_t(nodeName.size())); + bitStream.Write(nodeName.size()); for (char character : nodeName) { bitStream.Write(character); } @@ -2039,14 +2054,14 @@ void GameMessages::SendGetModelsOnProperty(LWOOBJID objectId, std::map(models.size())); + bitStream.Write(models.size()); for (const auto& pair : models) { bitStream.Write(pair.first); bitStream.Write(pair.second); } - Game::logger->Log("SendGetModelsOnProperty", "Sending property models to (%llu) (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); + LOG("Sending property models to (%llu) (%d)", objectId, sysAddr == UNASSIGNED_SYSTEM_ADDRESS); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; SEND_PACKET; @@ -2074,8 +2089,8 @@ void GameMessages::SendPlaceModelResponse(LWOOBJID objectId, const SystemAddress bitStream.Write(objectId); bitStream.Write(eGameMessageType::PLACE_MODEL_RESPONSE); - bitStream.Write(position != NiPoint3::ZERO); - if (position != NiPoint3::ZERO) { + bitStream.Write(position != NiPoint3Constant::ZERO); + if (position != NiPoint3Constant::ZERO) { bitStream.Write(position); } @@ -2089,8 +2104,8 @@ void GameMessages::SendPlaceModelResponse(LWOOBJID objectId, const SystemAddress bitStream.Write(response); } - bitStream.Write(rotation != NiQuaternion::IDENTITY); - if (rotation != NiQuaternion::IDENTITY) { + bitStream.Write(rotation != NiQuaternionConstant::IDENTITY); + if (rotation != NiQuaternionConstant::IDENTITY) { bitStream.Write(response); } @@ -2143,7 +2158,7 @@ void GameMessages::HandleSetPropertyAccess(RakNet::BitStream* inStream, Entity* inStream->Read(renewIsDefault); if (renewIsDefault != 0) inStream->Read(renew); - Game::logger->Log("GameMessages", "Set privacy option to: %i", accessType); + LOG("Set privacy option to: %i", accessType); if (PropertyManagementComponent::Instance() == nullptr) return; @@ -2162,13 +2177,13 @@ void GameMessages::HandleUnUseModel(RakNet::BitStream* inStream, Entity* entity, if (item) { inventoryComponent->MoveItemToInventory(item, eInventoryType::MODELS, 1); } else { - Game::logger->Log("GameMessages", "item id %llu not found in MODELS_IN_BBB inventory, likely because it does not exist", objIdToAddToInventory); + LOG("item id %llu not found in MODELS_IN_BBB inventory, likely because it does not exist", objIdToAddToInventory); } } if (unknown) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); bitStream.Write(LWOOBJID_EMPTY); //always zero so that a check on the client passes bitStream.Write(eBlueprintSaveResponseType::PlacementFailed); // Sending a non-zero error code here prevents the client from deleting its in progress build for some reason? bitStream.Write(0); @@ -2209,7 +2224,7 @@ void GameMessages::HandleUpdatePropertyOrModelForFilterCheck(RakNet::BitStream* } void GameMessages::HandleQueryPropertyData(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - Game::logger->Log("HandleQueryPropertyData", "Entity (%i) requesting data", entity->GetLOT()); + LOG("Entity (%i) requesting data", entity->GetLOT()); /* auto entites = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::PROPERTY_VENDOR); @@ -2242,7 +2257,7 @@ void GameMessages::HandleSetBuildMode(RakNet::BitStream* inStream, Entity* entit bool modePaused{}; int modeValue = 1; LWOOBJID playerId{}; - NiPoint3 startPosition = NiPoint3::ZERO; + NiPoint3 startPosition = NiPoint3Constant::ZERO; inStream->Read(start); @@ -2261,13 +2276,13 @@ void GameMessages::HandleSetBuildMode(RakNet::BitStream* inStream, Entity* entit auto* player = Game::entityManager->GetEntity(playerId); - if (startPosition == NiPoint3::ZERO) { + if (startPosition == NiPoint3Constant::ZERO) { startPosition = player->GetPosition(); } player->GetCharacter()->SetBuildMode(start); - Game::logger->Log("GameMessages", "Sending build mode confirm (%i): (%d) (%i) (%d) (%i) (%llu)", entity->GetLOT(), start, distanceType, modePaused, modeValue, playerId); + LOG("Sending build mode confirm (%i): (%d) (%i) (%d) (%i) (%llu)", entity->GetLOT(), start, distanceType, modePaused, modeValue, playerId); SendSetBuildModeConfirmed(entity->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS, start, false, modePaused, modeValue, playerId, startPosition); } @@ -2303,7 +2318,7 @@ void GameMessages::HandleStartBuildingWithItem(RakNet::BitStream* inStream, Enti sourceType = 4; } - Game::logger->Log("GameMessages", "Handling start building with item (%i): (%d) (%d) (%i) (%llu) (%i) (%i) (%llu) (%i) (%i)", entity->GetLOT(), firstTime, success, sourceBag, sourceId, sourceLot, sourceType, targetId, targetLot, targetType); + LOG("Handling start building with item (%i): (%d) (%d) (%i) (%llu) (%i) (%i) (%llu) (%i) (%i)", entity->GetLOT(), firstTime, success, sourceBag, sourceId, sourceLot, sourceType, targetId, targetLot, targetType); auto* user = UserManager::Instance()->GetUser(sysAddr); @@ -2355,13 +2370,13 @@ void GameMessages::HandlePlacePropertyModel(RakNet::BitStream* inStream, Entity* inStream->Read(model); - PropertyManagementComponent::Instance()->UpdateModelPosition(model, NiPoint3::ZERO, NiQuaternion::IDENTITY); + PropertyManagementComponent::Instance()->UpdateModelPosition(model, NiPoint3Constant::ZERO, NiQuaternionConstant::IDENTITY); } void GameMessages::HandleUpdatePropertyModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { LWOOBJID model; NiPoint3 position; - NiQuaternion rotation = NiQuaternion::IDENTITY; + NiQuaternion rotation = NiQuaternionConstant::IDENTITY; inStream->Read(model); inStream->Read(position); @@ -2393,7 +2408,7 @@ void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity* LWOOBJID previousItemID = LWOOBJID_EMPTY; inStream->Read(previousItemID); - Game::logger->Log("BBB", "Load item request for: %lld", previousItemID); + LOG("Load item request for: %lld", previousItemID); LWOOBJID newId = previousItemID; auto* inventoryComponent = entity->GetComponent(); if (inventoryComponent) { @@ -2410,7 +2425,7 @@ void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity* if (movedItem) newId = movedItem->GetId(); } } else { - Game::logger->Log("GameMessages", "item id %llu not found in MODELS inventory, likely because it does not exist", previousItemID); + LOG("item id %llu not found in MODELS inventory, likely because it does not exist", previousItemID); } } @@ -2420,8 +2435,8 @@ void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity* void GameMessages::SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId) { CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_LOAD_RESPONSE_ITEMID); - bitStream.Write(static_cast(success)); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_LOAD_RESPONSE_ITEMID); + bitStream.Write(success); bitStream.Write(oldItemId); bitStream.Write(newItemId); SEND_PACKET; @@ -2516,15 +2531,9 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent inStream->Read(sd0Size); std::shared_ptr sd0Data(new char[sd0Size]); - if (sd0Data == nullptr) { - return; - } + if (sd0Data == nullptr) return; - for (uint32_t i = 0; i < sd0Size; ++i) { - uint8_t c; - inStream->Read(c); - sd0Data[i] = c; - } + inStream->ReadAlignedBytes(reinterpret_cast(sd0Data.get()), sd0Size); uint32_t timeTaken; inStream->Read(timeTaken); @@ -2548,7 +2557,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent //int32_t size = ZCompression::Decompress(inData, lxfmlSize, outData, 327680, error); //if (size == -1) { - // Game::logger->Log("GameMessages", "Failed to decompress LXFML: (%i)", error); + // LOG("Failed to decompress LXFML: (%i)", error); // return; //} // @@ -2558,164 +2567,109 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent //We runs this in async because the http library here is blocking, meaning it'll halt the thread. //But we don't want the server to go unresponsive, because then the client would disconnect. - auto returnVal = std::async(std::launch::async, [&]() { - //We need to get a new ID for our model first: - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t newID) { - LWOOBJID newIDL = newID; - GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER); - GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT); + //We need to get a new ID for our model first: + ObjectIDManager::RequestPersistentID([=](uint32_t newID) { + if (!entity || !entity->GetCharacter() || !entity->GetCharacter()->GetParentUser()) return; + LWOOBJID newIDL = newID; + GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER); + GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT); - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t blueprintIDSmall) { - blueprintIDSmall = ObjectIDManager::Instance()->GenerateRandomObjectID(); - LWOOBJID blueprintID = blueprintIDSmall; - GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); - GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); + uint32_t blueprintIDSmall = ObjectIDManager::GenerateRandomObjectID(); + LWOOBJID blueprintID = blueprintIDSmall; + GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); + GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); - //We need to get the propertyID: (stolen from Wincent's propertyManagementComp) - const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); + //We need to get the propertyID: (stolen from Wincent's propertyManagementComp) + const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); - const auto zoneId = worldId.GetMapID(); - const auto cloneId = worldId.GetCloneID(); + const auto zoneId = worldId.GetMapID(); + const auto cloneId = worldId.GetCloneID(); - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT id FROM PropertyTemplate WHERE mapID = ?;"); - query.bind(1, (int)zoneId); + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); + LWOOBJID propertyId = LWOOBJID_EMPTY; + if (propertyInfo) propertyId = propertyInfo->id; - auto result = query.execQuery(); + //Insert into ugc: + std::string str(sd0Data.get(), sd0Size); + std::istringstream sd0DataStream(str); + Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetCharacter()->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID()); - if (result.eof() || result.fieldIsNull(0)) return; + //Insert into the db as a BBB model: + IPropertyContents::Model model; + model.id = newIDL; + model.ugcId = blueprintIDSmall; + model.position = NiPoint3Constant::ZERO; + model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f); + model.lot = 14; + Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name"); - int templateId = result.getIntField(0); + /* + Commented out until UGC server would be updated to use a sd0 file instead of lxfml stream. + (or you uncomment the lxfml decomp stuff above) + */ - result.finalize(); + ////Send off to UGC for processing, if enabled: + //if (Game::config->GetValue("ugc_remote") == "1") { + // std::string ugcIP = Game::config->GetValue("ugc_ip"); + // int ugcPort = std::stoi(Game::config->GetValue("ugc_port")); - auto* propertyLookup = Database::CreatePreppedStmt("SELECT * FROM properties WHERE template_id = ? AND clone_id = ?;"); + // httplib::Client cli(ugcIP, ugcPort); //connect to UGC HTTP server using our config above ^ - propertyLookup->setInt(1, templateId); - propertyLookup->setInt64(2, cloneId); + // //Send out a request: + // std::string request = "/3dservices/UGCC150/150" + std::to_string(blueprintID) + ".lxfml"; + // cli.Put(request.c_str(), lxfml.c_str(), "text/lxfml"); - auto* propertyEntry = propertyLookup->executeQuery(); - uint64_t propertyId = 0; + // //When the "put" above returns, it means that the UGC HTTP server is done processing our model & + // //the nif, hkx and checksum files are ready to be downloaded from cache. + //} - if (propertyEntry->next()) { - propertyId = propertyEntry->getUInt64(1); - } + //Tell the client their model is saved: (this causes us to actually pop out of our current state): + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); + bitStream.Write(localId); + bitStream.Write(eBlueprintSaveResponseType::EverythingWorked); + bitStream.Write(1); + bitStream.Write(blueprintID); - delete propertyEntry; - delete propertyLookup; + bitStream.Write(sd0Size); - //Insert into ugc: - auto ugcs = Database::CreatePreppedStmt("INSERT INTO `ugc`(`id`, `account_id`, `character_id`, `is_optimized`, `lxfml`, `bake_ao`, `filename`) VALUES (?,?,?,?,?,?,?)"); - ugcs->setUInt(1, blueprintIDSmall); - ugcs->setInt(2, entity->GetParentUser()->GetAccountID()); - ugcs->setInt(3, entity->GetCharacter()->GetID()); - ugcs->setInt(4, 0); + bitStream.WriteAlignedBytes(reinterpret_cast(sd0Data.get()), sd0Size); - //whacky stream biz - std::string s(sd0Data.get(), sd0Size); - std::istringstream iss(s); - std::istream& stream = iss; + SEND_PACKET; - ugcs->setBlob(5, &iss); - ugcs->setBoolean(6, false); - ugcs->setString(7, "weedeater.lxfml"); - ugcs->execute(); - delete ugcs; + //Now we have to construct this object: - //Insert into the db as a BBB model: - auto* stmt = Database::CreatePreppedStmt("INSERT INTO `properties_contents` VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); - stmt->setUInt64(1, newIDL); - stmt->setUInt64(2, propertyId); - stmt->setUInt(3, blueprintIDSmall); - stmt->setUInt(4, 14); // 14 is the lot the BBB models use - stmt->setDouble(5, 0.0f); // x - stmt->setDouble(6, 0.0f); // y - stmt->setDouble(7, 0.0f); // z - stmt->setDouble(8, 0.0f); // rx - stmt->setDouble(9, 0.0f); // ry - stmt->setDouble(10, 0.0f); // rz - stmt->setDouble(11, 0.0f); // rw - stmt->setString(12, "Objects_14_name"); // Model name. TODO make this customizable - stmt->setString(13, ""); // Model description. TODO implement this. - stmt->setDouble(14, 0); // behavior 1. TODO implement this. - stmt->setDouble(15, 0); // behavior 2. TODO implement this. - stmt->setDouble(16, 0); // behavior 3. TODO implement this. - stmt->setDouble(17, 0); // behavior 4. TODO implement this. - stmt->setDouble(18, 0); // behavior 5. TODO implement this. - stmt->execute(); - delete stmt; + EntityInfo info; + info.lot = 14; + info.pos = {}; + info.rot = {}; + info.spawner = nullptr; + info.spawnerID = entity->GetObjectID(); + info.spawnerNodeID = 0; - /* - Commented out until UGC server would be updated to use a sd0 file instead of lxfml stream. - (or you uncomment the lxfml decomp stuff above) - */ + LDFBaseData* ldfBlueprintID = new LDFData(u"blueprintid", blueprintID); + LDFBaseData* componentWhitelist = new LDFData(u"componentWhitelist", 1); + LDFBaseData* modelType = new LDFData(u"modelType", 2); + LDFBaseData* propertyObjectID = new LDFData(u"propertyObjectID", true); + LDFBaseData* userModelID = new LDFData(u"userModelID", newIDL); - ////Send off to UGC for processing, if enabled: - //if (Game::config->GetValue("ugc_remote") == "1") { - // std::string ugcIP = Game::config->GetValue("ugc_ip"); - // int ugcPort = std::stoi(Game::config->GetValue("ugc_port")); + info.settings.push_back(ldfBlueprintID); + info.settings.push_back(componentWhitelist); + info.settings.push_back(modelType); + info.settings.push_back(propertyObjectID); + info.settings.push_back(userModelID); - // httplib::Client cli(ugcIP, ugcPort); //connect to UGC HTTP server using our config above ^ + Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); + if (newEntity) { + Game::entityManager->ConstructEntity(newEntity); - // //Send out a request: - // std::string request = "/3dservices/UGCC150/150" + std::to_string(blueprintID) + ".lxfml"; - // cli.Put(request.c_str(), lxfml.c_str(), "text/lxfml"); + //Make sure the propMgmt doesn't delete our model after the server dies + //Trying to do this after the entity is constructed. Shouldn't really change anything but + //there was an issue with builds not appearing since it was placed above ConstructEntity. + PropertyManagementComponent::Instance()->AddModel(newEntity->GetObjectID(), newIDL); + } - // //When the "put" above returns, it means that the UGC HTTP server is done processing our model & - // //the nif, hkx and checksum files are ready to be downloaded from cache. - //} - - //Tell the client their model is saved: (this causes us to actually pop out of our current state): - CBITSTREAM; - PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_SAVE_RESPONSE); - bitStream.Write(localId); - bitStream.Write(eBlueprintSaveResponseType::EverythingWorked); - bitStream.Write(1); - bitStream.Write(blueprintID); - - bitStream.Write(sd0Size); - - for (size_t i = 0; i < sd0Size; ++i) { - bitStream.Write(sd0Data[i]); - } - - SEND_PACKET; - - //Now we have to construct this object: - - EntityInfo info; - info.lot = 14; - info.pos = {}; - info.rot = {}; - info.spawner = nullptr; - info.spawnerID = entity->GetObjectID(); - info.spawnerNodeID = 0; - - LDFBaseData* ldfBlueprintID = new LDFData(u"blueprintid", blueprintID); - LDFBaseData* componentWhitelist = new LDFData(u"componentWhitelist", 1); - LDFBaseData* modelType = new LDFData(u"modelType", 2); - LDFBaseData* propertyObjectID = new LDFData(u"propertyObjectID", true); - LDFBaseData* userModelID = new LDFData(u"userModelID", newIDL); - - info.settings.push_back(ldfBlueprintID); - info.settings.push_back(componentWhitelist); - info.settings.push_back(modelType); - info.settings.push_back(propertyObjectID); - info.settings.push_back(userModelID); - - Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr); - if (newEntity) { - Game::entityManager->ConstructEntity(newEntity); - - //Make sure the propMgmt doesn't delete our model after the server dies - //Trying to do this after the entity is constructed. Shouldn't really change anything but - //there was an issue with builds not appearing since it was placed above ConstructEntity. - PropertyManagementComponent::Instance()->AddModel(newEntity->GetObjectID(), newIDL); - } - - }); - }); }); } @@ -2747,7 +2701,7 @@ void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entit filterText.push_back(c); } - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); auto* entranceComponent = entity->GetComponent(); @@ -2774,7 +2728,7 @@ void GameMessages::HandleEnterProperty(RakNet::BitStream* inStream, Entity* enti inStream->Read(index); inStream->Read(returnToZone); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); auto* entranceComponent = entity->GetComponent(); if (entranceComponent != nullptr) { @@ -2782,9 +2736,9 @@ void GameMessages::HandleEnterProperty(RakNet::BitStream* inStream, Entity* enti return; } - auto rocketLaunchLupComponent = entity->GetComponent(); - if (rocketLaunchLupComponent != nullptr) { - rocketLaunchLupComponent->OnSelectWorld(player, index); + auto multiZoneEntranceComponent = entity->GetComponent(); + if (multiZoneEntranceComponent != nullptr) { + multiZoneEntranceComponent->OnSelectWorld(player, index); } } @@ -2957,14 +2911,14 @@ void GameMessages::SendSetStunned(LWOOBJID objectId, eStateChangeType stateChang } void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, const SystemAddress& sysAddr, - LWOOBJID originator, - bool bImmuneToStunAttack, - bool bImmuneToStunEquip, - bool bImmuneToStunInteract, - bool bImmuneToStunJump, - bool bImmuneToStunMove, - bool bImmuneToStunTurn, - bool bImmuneToStunUseItem) { + LWOOBJID originator, + bool bImmuneToStunAttack, + bool bImmuneToStunEquip, + bool bImmuneToStunInteract, + bool bImmuneToStunJump, + bool bImmuneToStunMove, + bool bImmuneToStunTurn, + bool bImmuneToStunUseItem) { CBITSTREAM; CMSGHEADER; @@ -2989,15 +2943,15 @@ void GameMessages::SendSetStunImmunity(LWOOBJID target, eStateChangeType state, } void GameMessages::SendSetStatusImmunity(LWOOBJID objectId, eStateChangeType state, const SystemAddress& sysAddr, - bool bImmuneToBasicAttack, - bool bImmuneToDamageOverTime, - bool bImmuneToKnockback, - bool bImmuneToInterrupt, - bool bImmuneToSpeed, - bool bImmuneToImaginationGain, - bool bImmuneToImaginationLoss, - bool bImmuneToQuickbuildInterrupt, - bool bImmuneToPullToPoint) { + bool bImmuneToBasicAttack, + bool bImmuneToDamageOverTime, + bool bImmuneToKnockback, + bool bImmuneToInterrupt, + bool bImmuneToSpeed, + bool bImmuneToImaginationGain, + bool bImmuneToImaginationLoss, + bool bImmuneToQuickbuildInterrupt, + bool bImmuneToPullToPoint) { CBITSTREAM; CMSGHEADER; @@ -3108,7 +3062,7 @@ void GameMessages::SendNotifyObject(LWOOBJID objectId, LWOOBJID objIDSender, std bitStream.Write(eGameMessageType::NOTIFY_OBJECT); bitStream.Write(objIDSender); - bitStream.Write(static_cast(name.size())); + bitStream.Write(name.size()); for (const auto character : name) { bitStream.Write(character); } @@ -3165,7 +3119,7 @@ void GameMessages::SendServerTradeInvite(LWOOBJID objectId, bool bNeedInvitePopU bitStream.Write(bNeedInvitePopUp); bitStream.Write(i64Requestor); - bitStream.Write(static_cast(wsName.size())); + bitStream.Write(wsName.size()); for (const auto character : wsName) { bitStream.Write(character); } @@ -3183,7 +3137,7 @@ void GameMessages::SendServerTradeInitialReply(LWOOBJID objectId, LWOOBJID i64In bitStream.Write(i64Invitee); bitStream.Write(resultType); - bitStream.Write(static_cast(wsName.size())); + bitStream.Write(wsName.size()); for (const auto character : wsName) { bitStream.Write(character); } @@ -3201,7 +3155,7 @@ void GameMessages::SendServerTradeFinalReply(LWOOBJID objectId, bool bResult, LW bitStream.Write(bResult); bitStream.Write(i64Invitee); - bitStream.Write(static_cast(wsName.size())); + bitStream.Write(wsName.size()); for (const auto character : wsName) { bitStream.Write(character); } @@ -3243,7 +3197,7 @@ void GameMessages::SendServerTradeUpdate(LWOOBJID objectId, uint64_t coins, cons bitStream.Write(false); bitStream.Write(coins); - bitStream.Write(static_cast(items.size())); + bitStream.Write(items.size()); for (const auto& item : items) { bitStream.Write(item.itemId); @@ -3297,7 +3251,7 @@ void GameMessages::HandleClientTradeRequest(RakNet::BitStream* inStream, Entity* return; } - Game::logger->Log("GameMessages", "Trade request to (%llu)", i64Invitee); + LOG("Trade request to (%llu)", i64Invitee); auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID()); @@ -3326,7 +3280,7 @@ void GameMessages::HandleClientTradeCancel(RakNet::BitStream* inStream, Entity* if (trade == nullptr) return; - Game::logger->Log("GameMessages", "Trade canceled from (%llu)", entity->GetObjectID()); + LOG("Trade canceled from (%llu)", entity->GetObjectID()); TradingManager::Instance()->CancelTrade(trade->GetTradeId()); } @@ -3334,7 +3288,7 @@ void GameMessages::HandleClientTradeCancel(RakNet::BitStream* inStream, Entity* void GameMessages::HandleClientTradeAccept(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { bool bFirst = inStream->ReadBit(); - Game::logger->Log("GameMessages", "Trade accepted from (%llu) -> (%d)", entity->GetObjectID(), bFirst); + LOG("Trade accepted from (%llu) -> (%d)", entity->GetObjectID(), bFirst); auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID()); @@ -3350,7 +3304,7 @@ void GameMessages::HandleClientTradeUpdate(RakNet::BitStream* inStream, Entity* inStream->Read(currency); inStream->Read(itemCount); - Game::logger->Log("GameMessages", "Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount); + LOG("Trade update from (%llu) -> (%llu), (%i)", entity->GetObjectID(), currency, itemCount); std::vector items{}; @@ -3398,7 +3352,7 @@ void GameMessages::HandleClientTradeUpdate(RakNet::BitStream* inStream, Entity* items.push_back({ itemId, lot, unknown2 }); - Game::logger->Log("GameMessages", "Trade item from (%llu) -> (%llu)/(%llu), (%i), (%llu), (%i), (%i)", entity->GetObjectID(), itemId, itemId2, lot, unknown1, unknown2, unknown3); + LOG("Trade item from (%llu) -> (%llu)/(%llu), (%i), (%llu), (%i), (%i)", entity->GetObjectID(), itemId, itemId2, lot, unknown1, unknown2, unknown3); } auto* trade = TradingManager::Instance()->GetPlayerTrade(entity->GetObjectID()); @@ -3426,7 +3380,7 @@ void GameMessages::SendNotifyPetTamingMinigame(LWOOBJID objectId, LWOOBJID petId bitStream.Write(petsDestPos); bitStream.Write(telePos); - const bool hasDefault = teleRot != NiQuaternion::IDENTITY; + const bool hasDefault = teleRot != NiQuaternionConstant::IDENTITY; bitStream.Write(hasDefault); if (hasDefault) bitStream.Write(teleRot); @@ -3452,7 +3406,7 @@ void GameMessages::SendNotifyPetTamingPuzzleSelected(LWOOBJID objectId, const st bitStream.Write(objectId); bitStream.Write(eGameMessageType::NOTIFY_TAMING_PUZZLE_SELECTED); - bitStream.Write(static_cast(bricks.size())); + bitStream.Write(bricks.size()); for (const auto& brick : bricks) { bitStream.Write(brick.designerID); bitStream.Write(brick.materialID); @@ -3501,7 +3455,7 @@ void GameMessages::SendAddPetToPlayer(LWOOBJID objectId, int32_t iElementalType, bitStream.Write(eGameMessageType::ADD_PET_TO_PLAYER); bitStream.Write(iElementalType); - bitStream.Write(static_cast(name.size())); + bitStream.Write(name.size()); for (const auto character : name) { bitStream.Write(character); } @@ -3571,14 +3525,14 @@ void GameMessages::SendClientExitTamingMinigame(LWOOBJID objectId, bool bVolunta SEND_PACKET; } -void GameMessages::SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr) { +void GameMessages::SendShowPetActionButton(const LWOOBJID objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; bitStream.Write(objectId); bitStream.Write(eGameMessageType::SHOW_PET_ACTION_BUTTON); - bitStream.Write(buttonLabel); + bitStream.Write(petAbility); bitStream.Write(bShow); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; @@ -3635,7 +3589,7 @@ void GameMessages::SendSetPetName(LWOOBJID objectId, std::u16string name, LWOOBJ bitStream.Write(objectId); bitStream.Write(eGameMessageType::SET_PET_NAME); - bitStream.Write(static_cast(name.size())); + bitStream.Write(name.size()); for (const auto character : name) { bitStream.Write(character); } @@ -3674,12 +3628,12 @@ void GameMessages::SendPetNameChanged(LWOOBJID objectId, int32_t moderationStatu bitStream.Write(moderationStatus); - bitStream.Write(static_cast(name.size())); + bitStream.Write(name.size()); for (const auto character : name) { bitStream.Write(character); } - bitStream.Write(static_cast(ownerName.size())); + bitStream.Write(ownerName.size()); for (const auto character : ownerName) { bitStream.Write(character); } @@ -3841,7 +3795,7 @@ void GameMessages::HandleMessageBoxResponse(RakNet::BitStream* inStream, Entity* userData.push_back(character); } - Game::logger->Log("HandleMessageBoxResponse", "Button: %d; LOT: %u identifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(identifier).c_str(), GeneralUtils::UTF16ToWTF8(userData).c_str()); + LOG("Button: %d; LOT: %u identifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(identifier).c_str(), GeneralUtils::UTF16ToWTF8(userData).c_str()); auto* user = UserManager::Instance()->GetUser(sysAddr); @@ -3897,7 +3851,7 @@ void GameMessages::HandleChoiceBoxRespond(RakNet::BitStream* inStream, Entity* e identifier.push_back(character); } - Game::logger->Log("HandleChoiceBoxRespond", "Button: %d; LOT: %u buttonIdentifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(buttonIdentifier).c_str(), GeneralUtils::UTF16ToWTF8(identifier).c_str()); + LOG("Button: %d; LOT: %u buttonIdentifier: %s; userData: %s", iButton, entity->GetLOT(), GeneralUtils::UTF16ToWTF8(buttonIdentifier).c_str(), GeneralUtils::UTF16ToWTF8(identifier).c_str()); auto* user = UserManager::Instance()->GetUser(sysAddr); @@ -3957,19 +3911,19 @@ void GameMessages::SendDisplayMessageBox(LWOOBJID objectId, bool bShow, LWOOBJID bitStream.Write(bShow); bitStream.Write(callbackClient); - bitStream.Write(static_cast(identifier.size())); + bitStream.Write(identifier.size()); for (const auto character : identifier) { bitStream.Write(character); } bitStream.Write(imageID); - bitStream.Write(static_cast(text.size())); + bitStream.Write(text.size()); for (const auto character : text) { bitStream.Write(character); } - bitStream.Write(static_cast(userData.size())); + bitStream.Write(userData.size()); for (const auto character : userData) { bitStream.Write(character); } @@ -3986,7 +3940,7 @@ void GameMessages::SendDisplayChatBubble(LWOOBJID objectId, const std::u16string bitStream.Write(objectId); bitStream.Write(eGameMessageType::DISPLAY_CHAT_BUBBLE); - bitStream.Write(static_cast(text.size())); + bitStream.Write(text.size()); for (const auto character : text) { bitStream.Write(character); } @@ -4077,10 +4031,10 @@ void GameMessages::HandleAcknowledgePossession(RakNet::BitStream* inStream, Enti void GameMessages::HandleModuleAssemblyQueryData(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { auto* moduleAssemblyComponent = entity->GetComponent(); - Game::logger->Log("HandleModuleAssemblyQueryData", "Got Query from %i", entity->GetLOT()); + LOG("Got Query from %i", entity->GetLOT()); if (moduleAssemblyComponent != nullptr) { - Game::logger->Log("HandleModuleAssemblyQueryData", "Returning assembly %s", GeneralUtils::UTF16ToWTF8(moduleAssemblyComponent->GetAssemblyPartsLOTs()).c_str()); + LOG("Returning assembly %s", GeneralUtils::UTF16ToWTF8(moduleAssemblyComponent->GetAssemblyPartsLOTs()).c_str()); SendModuleAssemblyDBDataForClient(entity->GetObjectID(), moduleAssemblyComponent->GetSubKey(), moduleAssemblyComponent->GetAssemblyPartsLOTs(), UNASSIGNED_SYSTEM_ADDRESS); } @@ -4163,7 +4117,7 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, auto* racingControlComponent = zoneController->GetComponent(); - Game::logger->Log("HandleRequestDie", "Got die request: %i", entity->GetLOT()); + LOG("Got die request: %i", entity->GetLOT()); if (racingControlComponent != nullptr) { auto* possessableComponent = entity->GetComponent(); @@ -4177,6 +4131,12 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity, } racingControlComponent->OnRequestDie(entity); + } else { + auto* destroyableComponent = entity->GetComponent(); + + if (!destroyableComponent) return; + + destroyableComponent->Smash(killerID, killType, deathType); } } @@ -4206,7 +4166,7 @@ void GameMessages::HandleRacingPlayerInfoResetFinished(RakNet::BitStream* inStre auto* racingControlComponent = zoneController->GetComponent(); - Game::logger->Log("HandleRacingPlayerInfoResetFinished", "Got finished: %i", entity->GetLOT()); + LOG("Got finished: %i", entity->GetLOT()); if (racingControlComponent != nullptr) { racingControlComponent->OnRacingPlayerInfoResetFinished(player); @@ -4233,27 +4193,19 @@ void GameMessages::HandleUpdatePropertyPerformanceCost(RakNet::BitStream* inStre if (performanceCost == 0.0f) return; auto zone = Game::zoneManager->GetZone(); - const auto& worldId = zone->GetZoneID(); - const auto cloneId = worldId.GetCloneID(); - const auto zoneId = worldId.GetMapID(); + if (!zone) { + LOG("If you see this message, something is very wrong."); + return; + } - auto updatePerformanceCostQuery = Database::CreatePreppedStmt("UPDATE properties SET performance_cost = ? WHERE clone_id = ? AND zone_id = ?"); - - updatePerformanceCostQuery->setDouble(1, performanceCost); - updatePerformanceCostQuery->setInt(2, cloneId); - updatePerformanceCostQuery->setInt(3, zoneId); - - updatePerformanceCostQuery->executeUpdate(); - - delete updatePerformanceCostQuery; - updatePerformanceCostQuery = nullptr; + Database::Get()->UpdatePerformanceCost(zone->GetZoneID(), performanceCost); } void GameMessages::HandleVehicleNotifyHitImaginationServer(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { LWOOBJID pickupObjID = LWOOBJID_EMPTY; LWOOBJID pickupSpawnerID = LWOOBJID_EMPTY; int32_t pickupSpawnerIndex = -1; - NiPoint3 vehiclePosition = NiPoint3::ZERO; + NiPoint3 vehiclePosition = NiPoint3Constant::ZERO; if (inStream->ReadBit()) inStream->Read(pickupObjID); if (inStream->ReadBit()) inStream->Read(pickupSpawnerID); @@ -4296,7 +4248,7 @@ void GameMessages::SendModuleAssemblyDBDataForClient(LWOOBJID objectId, LWOOBJID bitStream.Write(assemblyID); - bitStream.Write(static_cast(data.size())); + bitStream.Write(data.size()); for (auto character : data) { bitStream.Write(character); } @@ -4441,7 +4393,7 @@ void GameMessages::SendNotifyRacingClient(LWOOBJID objectId, int32_t eventType, bitStream.Write(paramObj); - bitStream.Write(static_cast(paramStr.size())); + bitStream.Write(paramStr.size()); for (auto character : paramStr) { bitStream.Write(character); } @@ -4541,7 +4493,7 @@ void GameMessages::SendVehicleNotifyFinishedRace(LWOOBJID objectId, const System void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, bool addImmunity, bool cancelOnDamaged, bool cancelOnDeath, bool cancelOnLogout, - bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, + bool cancelOnRemoveBuff, bool cancelOnUi, bool cancelOnUnequip, bool cancelOnZone, bool addedByTeammate, bool applyOnTeammates, const SystemAddress& sysAddr) { CBITSTREAM; CMSGHEADER; @@ -4549,27 +4501,29 @@ void GameMessages::SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uin bitStream.Write(objectID); bitStream.Write(eGameMessageType::ADD_BUFF); - bitStream.Write(false); // Added by teammate - bitStream.Write(false); // Apply on teammates - bitStream.Write(false); // Cancel on damage absorb ran out + bitStream.Write(addedByTeammate); // Added by teammate + bitStream.Write(applyOnTeammates); // Apply on teammates bitStream.Write(cancelOnDamaged); bitStream.Write(cancelOnDeath); bitStream.Write(cancelOnLogout); + bitStream.Write(false); // Cancel on move bitStream.Write(cancelOnRemoveBuff); - bitStream.Write(cancelOnUi); bitStream.Write(cancelOnUnequip); bitStream.Write(cancelOnZone); + bitStream.Write(false); // Ignore immunities bitStream.Write(addImmunity); bitStream.Write(false); // Use ref count - bitStream.Write(buffID); - bitStream.Write(msDuration); + bitStream.Write(casterID != LWOOBJID_EMPTY); + if (casterID != LWOOBJID_EMPTY) bitStream.Write(casterID); - bitStream.Write(casterID); - bitStream.Write(casterID); + bitStream.Write(buffID); + + bitStream.Write(msDuration != 0); + if (msDuration != 0) bitStream.Write(msDuration); if (sysAddr == UNASSIGNED_SYSTEM_ADDRESS) SEND_PACKET_BROADCAST; SEND_PACKET; @@ -4634,7 +4588,7 @@ void GameMessages::SendShowActivityCountdown(LWOOBJID objectId, bool bPlayAdditi bitStream.Write(bPlayCountdownSound); - bitStream.Write(static_cast(sndName.size())); + bitStream.Write(sndName.size()); for (auto character : sndName) { bitStream.Write(character); } @@ -4655,10 +4609,11 @@ void GameMessages::HandleToggleGhostReferenceOverride(RakNet::BitStream* inStrea inStream->Read(bOverride); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (player != nullptr) { - player->SetGhostOverride(bOverride); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverride(bOverride); Game::entityManager->UpdateGhosting(player); } @@ -4670,10 +4625,11 @@ void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream* inStream, inStream->Read(position); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (player != nullptr) { - player->SetGhostOverridePoint(position); + auto* ghostComponent = entity->GetComponent(); + if (ghostComponent) ghostComponent->SetGhostOverridePoint(position); Game::entityManager->UpdateGhosting(player); } @@ -4681,7 +4637,7 @@ void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream* inStream, void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { - bool bConfirmed{}; // this doesnt even do anything, thanks ND! + bool bConfirmed{}; // This doesn't appear to do anything. Further research is needed. bool countIsDefault{}; int count = 1; LOT item; @@ -4713,12 +4669,12 @@ void GameMessages::HandleBuyFromVendor(RakNet::BitStream* inStream, Entity* enti if (!inv) return; if (!isCommendationVendor && !vend->SellsItem(item)) { - Game::logger->Log("GameMessages", "User %llu %s tried to buy an item %i from a vendor when they do not sell said item", player->GetObjectID(), user->GetUsername().c_str(), item); + LOG("User %llu %s tried to buy an item %i from a vendor when they do not sell said item", player->GetObjectID(), user->GetUsername().c_str(), item); return; } - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - CDItemComponentTable* itemComponentTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); + CDItemComponentTable* itemComponentTable = CDClientManager::GetTable(); int itemCompID = compRegistryTable->GetByIDAndType(item, eReplicaComponentType::ITEM); CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID); @@ -4809,8 +4765,8 @@ void GameMessages::HandleSellToVendor(RakNet::BitStream* inStream, Entity* entit Item* item = inv->FindItemById(iObjID); if (!item) return; - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - CDItemComponentTable* itemComponentTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); + CDItemComponentTable* itemComponentTable = CDClientManager::GetTable(); int itemCompID = compRegistryTable->GetByIDAndType(item->GetLot(), eReplicaComponentType::ITEM); CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID); @@ -4859,8 +4815,8 @@ void GameMessages::HandleBuybackFromVendor(RakNet::BitStream* inStream, Entity* Item* item = inv->FindItemById(iObjID); if (!item) return; - CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance().GetTable(); - CDItemComponentTable* itemComponentTable = CDClientManager::Instance().GetTable(); + CDComponentsRegistryTable* compRegistryTable = CDClientManager::GetTable(); + CDItemComponentTable* itemComponentTable = CDClientManager::GetTable(); int itemCompID = compRegistryTable->GetByIDAndType(item->GetLot(), eReplicaComponentType::ITEM); CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID); @@ -4934,7 +4890,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity inStream->Read(senderID); auto* sender = Game::entityManager->GetEntity(senderID); - auto* player = Player::GetPlayer(sysAddr); + auto* player = PlayerManager::GetPlayer(sysAddr); if (!player) { return; @@ -4967,7 +4923,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity mapId = Game::zoneManager->GetZoneID().GetMapID(); // Fallback to sending the player back to the same zone. } - Game::logger->Log("FireEventServerSide", "Player %llu has requested zone transfer to (%i, %i).", sender->GetObjectID(), (int)mapId, (int)cloneId); + LOG("Player %llu has requested zone transfer to (%i, %i).", sender->GetObjectID(), static_cast(mapId), static_cast(cloneId)); auto* character = player->GetCharacter(); @@ -4976,7 +4932,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity } ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, mapId, cloneId, false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) { - Game::logger->Log("UserManager", "Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); + LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort); if (character) { character->SetZoneID(zoneID); @@ -4996,17 +4952,17 @@ void GameMessages::HandleRequestPlatformResync(RakNet::BitStream* inStream, Enti GameMessages::SendPlatformResync(entity, sysAddr, eMovementPlatformState::Travelling); } -void GameMessages::HandleRebuildCancel(RakNet::BitStream* inStream, Entity* entity) { +void GameMessages::HandleQuickBuildCancel(RakNet::BitStream* inStream, Entity* entity) { bool bEarlyRelease; LWOOBJID userID; inStream->Read(bEarlyRelease); inStream->Read(userID); - RebuildComponent* rebComp = static_cast(entity->GetComponent(eReplicaComponentType::QUICK_BUILD)); - if (!rebComp) return; + auto* quickBuildComponent = static_cast(entity->GetComponent(eReplicaComponentType::QUICK_BUILD));; + if (!quickBuildComponent) return; - rebComp->CancelRebuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); + quickBuildComponent->CancelQuickBuild(Game::entityManager->GetEntity(userID), eQuickBuildFailReason::CANCELED_EARLY); } void GameMessages::HandleRequestUse(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { @@ -5025,7 +4981,7 @@ void GameMessages::HandleRequestUse(RakNet::BitStream* inStream, Entity* entity, Entity* interactedObject = Game::entityManager->GetEntity(objectID); if (interactedObject == nullptr) { - Game::logger->Log("GameMessages", "Object %llu tried to interact, but doesn't exist!", objectID); + LOG("Object %llu tried to interact, but doesn't exist!", objectID); return; } @@ -5064,27 +5020,35 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) inStream->Read(emoteID); inStream->Read(targetID); - Game::logger->LogDebug("GameMessages", "Emote (%i) (%llu)", emoteID, targetID); + LOG_DEBUG("Emote (%i) (%llu)", emoteID, targetID); //TODO: If targetID != 0, and we have one of the "perform emote" missions, complete them. if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote + CDEmoteTableTable* emotes = CDClientManager::GetTable(); + if (emotes) { + CDEmoteTable* emote = emotes->GetEmote(emoteID); + if (emote) sAnimationName = emote->animationName; + } + + RenderComponent::PlayAnimation(entity, sAnimationName); + MissionComponent* missionComponent = entity->GetComponent(); if (!missionComponent) return; if (targetID != LWOOBJID_EMPTY) { auto* targetEntity = Game::entityManager->GetEntity(targetID); - Game::logger->LogDebug("GameMessages", "Emote target found (%d)", targetEntity != nullptr); + LOG_DEBUG("Emote target found (%d)", targetEntity != nullptr); if (targetEntity != nullptr) { targetEntity->OnEmoteReceived(emoteID, entity); missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, targetID); } } else { - Game::logger->LogDebug("GameMessages", "Target ID is empty, using backup"); + LOG_DEBUG("Target ID is empty, using backup"); const auto scriptedEntities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT); const auto& referencePoint = entity->GetPosition(); @@ -5096,14 +5060,6 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) missionComponent->Progress(eMissionTaskType::EMOTE, emoteID, scripted->GetObjectID()); } } - - CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); - if (emotes) { - CDEmoteTable* emote = emotes->GetEmote(emoteID); - if (emote) sAnimationName = emote->animationName; - } - - RenderComponent::PlayAnimation(entity, sAnimationName); } void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { @@ -5126,6 +5082,10 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream* inStream, E item->Disassemble(TEMP_MODELS); + std::unique_ptr stmt(Database::Get()->CreatePreppedStmt("DELETE FROM ugc_modular_build where ugc_id = ?")); + stmt->setUInt64(1, item->GetSubKey()); + stmt->execute(); + item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD); } @@ -5136,13 +5096,8 @@ void GameMessages::HandleSetFlag(RakNet::BitStream* inStream, Entity* entity) { inStream->Read(bFlag); inStream->Read(iFlagID); - auto user = entity->GetParentUser(); - if (user) { - auto character = user->GetLastUsedChar(); - if (!character) return; - - character->SetPlayerFlag(iFlagID, bFlag); - } + auto character = entity->GetCharacter(); + if (character) character->SetPlayerFlag(iFlagID, bFlag); } void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* entity) { @@ -5160,7 +5115,7 @@ void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* e MissionComponent* missionComponent = static_cast(entity->GetComponent(eReplicaComponentType::MISSION)); if (!missionComponent) { - Game::logger->Log("GameMessages", "Unable to get mission component for entity %llu to handle RespondToMission", playerID); + LOG("Unable to get mission component for entity %llu to handle RespondToMission", playerID); return; } @@ -5168,13 +5123,13 @@ void GameMessages::HandleRespondToMission(RakNet::BitStream* inStream, Entity* e if (mission) { mission->SetReward(reward); } else { - Game::logger->Log("GameMessages", "Unable to get mission %i for entity %llu to update reward in RespondToMission", missionID, playerID); + LOG("Unable to get mission %i for entity %llu to update reward in RespondToMission", missionID, playerID); } Entity* offerer = Game::entityManager->GetEntity(receiverID); if (offerer == nullptr) { - Game::logger->Log("GameMessages", "Unable to get receiver entity %llu for RespondToMission", receiverID); + LOG("Unable to get receiver entity %llu for RespondToMission", receiverID); return; } @@ -5203,7 +5158,7 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream* inStream, Entity* en // Get the player's mission component MissionComponent* missionComponent = static_cast(player->GetComponent(eReplicaComponentType::MISSION)); if (!missionComponent) { - Game::logger->Log("GameMessages", "Unable to get mission component for entity %llu to handle MissionDialogueOK", player->GetObjectID()); + LOG("Unable to get mission component for entity %llu to handle MissionDialogueOK", player->GetObjectID()); return; } @@ -5212,6 +5167,14 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream* inStream, Entity* en } else if (iMissionState == eMissionState::READY_TO_COMPLETE || iMissionState == eMissionState::COMPLETE_READY_TO_COMPLETE) { missionComponent->CompleteMission(missionID); } + + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" + || !player->GetCharacter() + || !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + player->AddCallbackTimer(0.5f, [player]() { + if (!player) return; + GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress()); + }); } void GameMessages::HandleRequestLinkedMission(RakNet::BitStream* inStream, Entity* entity) { @@ -5320,7 +5283,7 @@ void GameMessages::HandleRequestDie(RakNet::BitStream* inStream, Entity* entity) /*uint32_t deathTypeLength = deathType.size(); inStream->Read(deathTypeLength); for (uint32_t k = 0; k < deathTypeLength; k++) { - inStream->Read(static_cast(deathType[k])); + inStream->Read(deathType[k]); }*/ inStream->Read(directionRelative_AngleXZ); @@ -5448,10 +5411,8 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En iStackCount = std::min(item->GetCount(), iStackCount); if (bConfirmed) { - for (auto i = 0; i < iStackCount; ++i) { - if (eInvType == eInventoryType::MODELS) { - item->DisassembleModel(); - } + if (eInvType == eInventoryType::MODELS) { + item->DisassembleModel(iStackCount); } item->SetCount(item->GetCount() - iStackCount, true); @@ -5465,6 +5426,18 @@ void GameMessages::HandleRemoveItemFromInventory(RakNet::BitStream* inStream, En } } +void GameMessages::SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr) { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(target); + bitStream.Write(eGameMessageType::SET_GRAVITY_SCALE); + + bitStream.Write(effectScale); + + SEND_PACKET; +} + void GameMessages::HandleMoveItemInInventory(RakNet::BitStream* inStream, Entity* entity) { bool destInvTypeIsDefault = false; int32_t destInvType = eInventoryType::INVALID; @@ -5544,7 +5517,7 @@ void GameMessages::HandleBuildModeSet(RakNet::BitStream* inStream, Entity* entit inStream->Read(bStart); // there's more here but we don't need it (for now?) - Game::logger->Log("GameMessages", "Set build mode to (%d) for (%llu)", bStart, entity->GetObjectID()); + LOG("Set build mode to (%d) for (%llu)", bStart, entity->GetObjectID()); if (entity->GetCharacter()) { entity->GetCharacter()->SetBuildMode(bStart); @@ -5559,13 +5532,6 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); if (!inv) return; - Game::logger->Log("GameMessages", "Build finished"); - - GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build - GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it - - //inv->UnequipItem(inv->GetItemStackByLOT(6086, eInventoryType::ITEMS)); // take off the thinking cap - //Game::entityManager->SerializeEntity(entity); uint8_t count; // 3 for rockets, 7 for cars @@ -5600,42 +5566,61 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* } } - const auto moduleAssembly = new LDFData(u"assemblyPartLOTs", modules); + ObjectIDManager::RequestPersistentID([=](uint32_t newId) { + LOG("Build finished"); + GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build + GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it - std::vector config; - config.push_back(moduleAssembly); + //inv->UnequipItem(inv->GetItemStackByLOT(6086, eInventoryType::ITEMS)); // take off the thinking cap + //Game::entityManager->SerializeEntity(entity); - if (count == 3) { - inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config); - } else if (count == 7) { - inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config); - } + const auto moduleAssembly = new LDFData(u"assemblyPartLOTs", modules); - auto* missionComponent = character->GetComponent(); + std::vector config; + config.push_back(moduleAssembly); - if (entity->GetLOT() != 9980 || Game::server->GetZoneID() != 1200) { - if (missionComponent != nullptr) { - missionComponent->Progress(eMissionTaskType::SCRIPT, entity->GetLOT(), entity->GetObjectID()); - if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, (LWOOBJID)eRacingTaskParam::MODULAR_BUILDING); + LWOOBJID newIdBig = newId; + GeneralUtils::SetBit(newIdBig, eObjectBits::CHARACTER); + + if (count == 3) { + inv->AddItem(6416, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig); + } else if (count == 7) { + inv->AddItem(8092, 1, eLootSourceType::QUICKBUILD, eInventoryType::MODELS, config, LWOOBJID_EMPTY, true, false, newIdBig); } - } - } - ScriptComponent* script = static_cast(entity->GetComponent(eReplicaComponentType::SCRIPT)); + std::unique_ptr stmt(Database::Get()->CreatePreppedStmt("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)")); + stmt->setUInt64(1, newIdBig); + stmt->setString(2, GeneralUtils::UTF16ToWTF8(modules).c_str()); + auto* pCharacter = character->GetCharacter(); + pCharacter ? stmt->setUInt(3, pCharacter->GetID()) : stmt->setNull(3, sql::DataType::BIGINT); + stmt->execute(); - for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) { - script->OnModularBuildExit(entity, character, count >= 3, modList); - } + auto* missionComponent = character->GetComponent(); - // Move remaining temp models back to models - std::vector items; + if (entity->GetLOT() != 9980 || Game::server->GetZoneID() != 1200) { + if (missionComponent != nullptr) { + missionComponent->Progress(eMissionTaskType::SCRIPT, entity->GetLOT(), entity->GetObjectID()); + if (count >= 7 && everyPieceSwapped) missionComponent->Progress(eMissionTaskType::RACING, LWOOBJID_EMPTY, static_cast(eRacingTaskParam::MODULAR_BUILDING)); + } + } - for (const auto& pair : temp->GetItems()) { - items.push_back(pair.second); - } + ScriptComponent* script = static_cast(entity->GetComponent(eReplicaComponentType::SCRIPT)); - for (auto* item : items) { - inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false); + for (CppScripts::Script* script : CppScripts::GetEntityScripts(entity)) { + script->OnModularBuildExit(entity, character, count >= 3, modList); + } + + // Move remaining temp models back to models + std::vector items; + + for (const auto& pair : temp->GetItems()) { + items.push_back(pair.second); + } + + for (auto* item : items) { + inv->MoveItemToInventory(item, eInventoryType::MODELS, item->GetCount(), false); + } + }); } } @@ -5688,7 +5673,7 @@ void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream* inStream, Enti inStream->Read(oldItemTYPE); /* - Game::logger->Log("GameMessages", + LOG("GameMessages", "\nnewSourceBAG: %d\nnewSourceID: %llu\nnewSourceLOT: %d\nnewSourceTYPE: %d\nnewTargetID: %llu\nnewTargetLOT: %d\nnewTargetTYPE: %d\nnewTargetPOS: %f, %f, %f\noldItemBAG: %d\noldItemID: %llu\noldItemLOT: %d\noldItemTYPE: %d", newSourceBAG, newSourceID, newSourceLOT, newSourceTYPE, newTargetID, newTargetLOT, newTargetTYPE, newTargetPOS.x, newTargetPOS.y, newTargetPOS.z, oldItemBAG, oldItemID, oldItemLOT, oldItemTYPE ); @@ -5706,14 +5691,14 @@ void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream* inStream, Enti } else if (!entities.empty()) { buildArea = entities[0]; - Game::logger->Log("BuildBorderComponent", "Using PropertyPlaque"); + LOG("Using PropertyPlaque"); } else { - Game::logger->Log("BuildBorderComponent", "No build area found"); + LOG("No build area found"); return; } - Game::logger->Log("GameMessages", "Build area found: %llu", buildArea->GetObjectID()); + LOG("Build area found: %llu", buildArea->GetObjectID()); GameMessages::SendStartArrangingWithItem( character, @@ -5732,7 +5717,7 @@ void GameMessages::HandleDoneArrangingWithItem(RakNet::BitStream* inStream, Enti ); } - Game::logger->Log("GameMessages", "Done Arranging"); + LOG("Done Arranging"); //GenericInventory* models = inv->GetGenericInventory(MODELS); //GenericInventory* tempModels = inv->GetGenericInventory(TEMP_MODELS); @@ -5756,7 +5741,7 @@ void GameMessages::HandleModularBuildMoveAndEquip(RakNet::BitStream* inStream, E Entity* character = Game::entityManager->GetEntity(user->GetLoggedInChar()); if (!character) return; - Game::logger->Log("GameMessages", "Build and move"); + LOG("Build and move"); LOT templateID; @@ -5960,31 +5945,27 @@ void GameMessages::SendGetHotPropertyData(RakNet::BitStream* inStream, Entity* e void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity) { //Definitely not stolen from autogenerated code, no sir: - std::u16string body; - std::string clientVersion; - std::string nOtherPlayerID; - std::string selection; - uint32_t messageLength; - int32_t reporterID = 0; + IBugReports::Info reportInfo; //Reading: + uint32_t messageLength; inStream->Read(messageLength); for (uint32_t i = 0; i < (messageLength); ++i) { uint16_t character; inStream->Read(character); - body.push_back(character); + reportInfo.body.push_back(static_cast(character)); } auto character = entity->GetCharacter(); - if (character) reporterID = character->GetID(); + if (character) reportInfo.characterId = character->GetID(); uint32_t clientVersionLength; inStream->Read(clientVersionLength); for (unsigned int k = 0; k < clientVersionLength; k++) { unsigned char character; inStream->Read(character); - clientVersion.push_back(character); + reportInfo.clientVersion.push_back(character); } uint32_t nOtherPlayerIDLength; @@ -5992,32 +5973,18 @@ void GameMessages::HandleReportBug(RakNet::BitStream* inStream, Entity* entity) for (unsigned int k = 0; k < nOtherPlayerIDLength; k++) { unsigned char character; inStream->Read(character); - nOtherPlayerID.push_back(character); + reportInfo.otherPlayer.push_back(character); } - // Convert other player id from LWOOBJID to the database id. - uint32_t otherPlayer = LWOOBJID_EMPTY; - if (nOtherPlayerID != "") otherPlayer = std::atoi(nOtherPlayerID.c_str()); uint32_t selectionLength; inStream->Read(selectionLength); for (unsigned int k = 0; k < selectionLength; k++) { unsigned char character; inStream->Read(character); - selection.push_back(character); + reportInfo.selection.push_back(character); } - try { - sql::PreparedStatement* insertBug = Database::CreatePreppedStmt("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)"); - insertBug->setString(1, GeneralUtils::UTF16ToWTF8(body)); - insertBug->setString(2, clientVersion); - insertBug->setString(3, std::to_string(otherPlayer)); - insertBug->setString(4, selection); - insertBug->setInt(5, reporterID); - insertBug->execute(); - delete insertBug; - } catch (sql::SQLException& e) { - Game::logger->Log("HandleReportBug", "Couldn't save bug report! (%s)", e.what()); - } + Database::Get()->InsertNewBugReport(reportInfo); } void @@ -6104,7 +6071,7 @@ void GameMessages::HandleUpdatePlayerStatistic(RakNet::BitStream* inStream, Enti auto* characterComponent = entity->GetComponent(); if (characterComponent != nullptr) { - characterComponent->UpdatePlayerStatistic((StatisticID)updateID, (uint64_t)std::max(updateValue, int64_t(0))); + characterComponent->UpdatePlayerStatistic(static_cast(updateID), static_cast(std::max(updateValue, static_cast(0)))); } } @@ -6208,7 +6175,7 @@ void GameMessages::HandleAddDonationItem(RakNet::BitStream* inStream, Entity* en auto* donationVendorComponent = entity->GetComponent(); if (!donationVendorComponent) return; if (donationVendorComponent->GetActivityID() == 0) { - Game::logger->Log("GameMessages", "WARNING: Trying to dontate to a vendor with no activity"); + LOG("WARNING: Trying to dontate to a vendor with no activity"); return; } User* user = UserManager::Instance()->GetUser(sysAddr); @@ -6261,9 +6228,9 @@ void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, En auto* donationEntity = Game::entityManager->GetEntity(characterComponent->GetCurrentInteracting()); if (!donationEntity) return; auto* donationVendorComponent = donationEntity->GetComponent(); - if(!donationVendorComponent) return; + if (!donationVendorComponent) return; if (donationVendorComponent->GetActivityID() == 0) { - Game::logger->Log("GameMessages", "WARNING: Trying to dontate to a vendor with no activity"); + LOG("WARNING: Trying to dontate to a vendor with no activity"); return; } auto* inventory = inventoryComponent->GetInventory(eInventoryType::DONATION); @@ -6271,7 +6238,7 @@ void GameMessages::HandleConfirmDonationOnPlayer(RakNet::BitStream* inStream, En auto items = inventory->GetItems(); if (!items.empty()) { uint32_t count = 0; - for (auto& [itemID, item] : items){ + for (auto& [itemID, item] : items) { count += item->GetCount(); item->RemoveFromInventory(); } @@ -6289,7 +6256,7 @@ void GameMessages::HandleCancelDonationOnPlayer(RakNet::BitStream* inStream, Ent auto* inventory = inventoryComponent->GetInventory(eInventoryType::DONATION); if (!inventory) return; auto items = inventory->GetItems(); - for (auto& [itemID, item] : items){ + for (auto& [itemID, item] : items) { inventoryComponent->MoveItemToInventory(item, eInventoryType::BRICKS, item->GetCount(), false, false, true); } auto* characterComponent = entity->GetComponent(); diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 3ef59772..cd496158 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -32,10 +32,12 @@ enum class eObjectWorldState : uint32_t; enum class eTerminateType : uint32_t; enum class eControlScheme : uint32_t; enum class eStateChangeType : uint32_t; +enum class ePetAbilityType : uint32_t; enum class ePetTamingNotifyType : uint32_t; enum class eUseItemResponse : uint32_t; enum class eQuickBuildFailReason : uint32_t; -enum class eRebuildState : uint32_t; +enum class eQuickBuildState : uint32_t; +enum class BehaviorSlot : int32_t; namespace GameMessages { class PropertyDataMessage; @@ -53,14 +55,14 @@ namespace GameMessages { const SystemAddress& sysAddr, bool bFirstTime = true, const LWOOBJID& buildAreaID = LWOOBJID_EMPTY, - NiPoint3 buildStartPOS = NiPoint3::ZERO, + NiPoint3 buildStartPOS = NiPoint3Constant::ZERO, int sourceBAG = 0, const LWOOBJID& sourceID = LWOOBJID_EMPTY, LOT sourceLOT = 0, int sourceTYPE = 8, const LWOOBJID& targetID = 0, LOT targetLOT = 0, - NiPoint3 targetPOS = NiPoint3::ZERO, + NiPoint3 targetPOS = NiPoint3Constant::ZERO, int targetTYPE = 0 ); @@ -73,6 +75,7 @@ namespace GameMessages { eMovementPlatformState movementState, bool bStopAtDesiredWaypoint = false, int iIndex = 0, int iDesiredWaypointIndex = 1, int nextIndex = 1); + void SendResetMissions(Entity* entity, const SystemAddress& sysAddr, const int32_t missionid = -1); void SendRestoreToPostLoadStats(Entity* entity, const SystemAddress& sysAddr); void SendServerDoneLoadingAllObjects(Entity* entity, const SystemAddress& sysAddr); void SendGMLevelBroadcast(const LWOOBJID& objectID, eGameMasterLevel level); @@ -89,6 +92,9 @@ namespace GameMessages { void SendModifyLEGOScore(Entity* entity, const SystemAddress& sysAddr, int64_t score, eLootSourceType sourceType); void SendUIMessageServerToSingleClient(Entity* entity, const SystemAddress& sysAddr, const std::string& message, AMFBaseValue& args); + + // Specify sysAddr if you need to send a flash message to a client who you dont know the objectID of. + void SendUIMessageServerToSingleClient(const std::string& message, AMFBaseValue& args, const SystemAddress& sysAddr); void SendUIMessageServerToAllClients(const std::string& message, AMFBaseValue& args); void SendPlayEmbeddedEffectOnAllClientsNearObject(Entity* entity, std::u16string effectName, const LWOOBJID& fromObjectID, float radius); @@ -98,8 +104,8 @@ namespace GameMessages { void SendBroadcastTextToChatbox(Entity* entity, const SystemAddress& sysAddr, const std::u16string& attrs, const std::u16string& wsText); void SendSetCurrency(Entity* entity, int64_t currency, int lootType, const LWOOBJID& sourceID, const LOT& sourceLOT, int sourceTradeID, bool overrideCurrent, eLootSourceType sourceType); - void SendRebuildNotifyState(Entity* entity, eRebuildState prevState, eRebuildState state, const LWOOBJID& playerID); - void SendEnableRebuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID); + void SendQuickBuildNotifyState(Entity* entity, eQuickBuildState prevState, eQuickBuildState state, const LWOOBJID& playerID); + void SendEnableQuickBuild(Entity* entity, bool enable, bool fail, bool success, eQuickBuildFailReason failReason, float duration, const LWOOBJID& playerID); void AddActivityOwner(Entity* entity, LWOOBJID& ownerID); void SendTerminateInteraction(const LWOOBJID& objectID, eTerminateType type, const LWOOBJID& terminator); @@ -108,18 +114,20 @@ namespace GameMessages { void SendSetInventorySize(Entity* entity, int invType, int size); + void SendSetGravityScale(const LWOOBJID& target, const float effectScale, const SystemAddress& sysAddr); + void SendSetEmoteLockState(Entity* entity, bool bLock, int emoteID); void SendSetJetPackMode(Entity* entity, bool use, bool bypassChecks = false, bool doHover = false, int effectID = -1, float airspeed = 10, float maxAirspeed = 15, float verticalVelocity = 1, int warningEffectID = -1); void SendResurrect(Entity* entity); void SendStop2DAmbientSound(Entity* entity, bool force, std::string audioGUID, bool result = false); void SendPlay2DAmbientSound(Entity* entity, std::string audioGUID, bool result = false); void SendSetNetworkScriptVar(Entity* entity, const SystemAddress& sysAddr, std::string data); - void SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos = NiPoint3::ZERO, int count = 1); + void SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID, LOT item, int currency, NiPoint3 spawnPos = NiPoint3Constant::ZERO, int count = 1); void SendSetPlayerControlScheme(Entity* entity, eControlScheme controlScheme); void SendPlayerReachedRespawnCheckpoint(Entity* entity, const NiPoint3& position, const NiQuaternion& rotation); - void SendAddSkill(Entity* entity, TSkillID skillID, int slotID); + void SendAddSkill(Entity* entity, TSkillID skillID, BehaviorSlot slotID); void SendRemoveSkill(Entity* entity, TSkillID skillID); void SendFinishArrangingWithItem(Entity* entity, const LWOOBJID& buildAreaID); @@ -203,7 +211,7 @@ namespace GameMessages { void SendAddBuff(LWOOBJID& objectID, const LWOOBJID& casterID, uint32_t buffID, uint32_t msDuration, bool addImmunity = false, bool cancelOnDamaged = false, bool cancelOnDeath = true, bool cancelOnLogout = false, bool cancelOnRemoveBuff = true, bool cancelOnUi = false, - bool cancelOnUnequip = false, bool cancelOnZone = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); + bool cancelOnUnequip = false, bool cancelOnZone = false, bool addedByTeammate = false, bool applyOnTeammates = false, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); void SendToggleGMInvis(LWOOBJID objectId, bool enabled, const SystemAddress& sysAddr); @@ -231,7 +239,7 @@ namespace GameMessages { void SendLockNodeRotation(Entity* entity, std::string nodeName); - void SendSetBuildModeConfirmed(LWOOBJID objectId, const SystemAddress& sysAddr, bool start, bool warnVisitors, bool modePaused, int32_t modeValue, LWOOBJID playerId, NiPoint3 startPos = NiPoint3::ZERO); + void SendSetBuildModeConfirmed(LWOOBJID objectId, const SystemAddress& sysAddr, bool start, bool warnVisitors, bool modePaused, int32_t modeValue, LWOOBJID playerId, NiPoint3 startPos = NiPoint3Constant::ZERO); void SendGetModelsOnProperty(LWOOBJID objectId, std::map models, const SystemAddress& sysAddr); @@ -382,7 +390,7 @@ namespace GameMessages { void SendClientExitTamingMinigame(LWOOBJID objectId, bool bVoluntaryExit, const SystemAddress& sysAddr); - void SendShowPetActionButton(LWOOBJID objectId, int32_t buttonLabel, bool bShow, const SystemAddress& sysAddr); + void SendShowPetActionButton(const LWOOBJID objectId, const ePetAbilityType petAbility, const bool bShow, const SystemAddress& sysAddr); void SendPlayEmote(LWOOBJID objectId, int32_t emoteID, LWOOBJID target, const SystemAddress& sysAddr); @@ -598,7 +606,7 @@ namespace GameMessages { void HandleSetGhostReffrenceOverride(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleFireEventServerSide(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandleRequestPlatformResync(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); - void HandleRebuildCancel(RakNet::BitStream* inStream, Entity* entity); + void HandleQuickBuildCancel(RakNet::BitStream* inStream, Entity* entity); void HandleRequestUse(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); void HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity); void HandleModularBuildConvertModel(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr); diff --git a/dGame/dGameMessages/PropertyDataMessage.cpp b/dGame/dGameMessages/PropertyDataMessage.cpp index c3443f31..ab7fe9c3 100644 --- a/dGame/dGameMessages/PropertyDataMessage.cpp +++ b/dGame/dGameMessages/PropertyDataMessage.cpp @@ -3,7 +3,7 @@ #include "GeneralUtils.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "CDClientManager.h" #include "CDPropertyTemplateTable.h" @@ -17,21 +17,21 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con stream.Write(cloneId); // clone id const auto& name = GeneralUtils::UTF8ToUTF16(Name); - stream.Write(uint32_t(name.size())); + stream.Write(name.size()); for (uint32_t i = 0; i < name.size(); ++i) { - stream.Write(uint16_t(name[i])); + stream.Write(name[i]); } const auto& description = GeneralUtils::UTF8ToUTF16(Description); - stream.Write(uint32_t(description.size())); + stream.Write(description.size()); for (uint32_t i = 0; i < description.size(); ++i) { - stream.Write(uint16_t(description[i])); + stream.Write(description[i]); } const auto& owner = GeneralUtils::UTF8ToUTF16(OwnerName); - stream.Write(uint32_t(owner.size())); + stream.Write(owner.size()); for (uint32_t i = 0; i < owner.size(); ++i) { - stream.Write(uint16_t(owner[i])); + stream.Write(owner[i]); } stream.Write(OwnerId); @@ -49,9 +49,9 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con stream.Write(0); const auto& spawn = GeneralUtils::ASCIIToUTF16(SpawnName); - stream.Write(uint32_t(spawn.size())); + stream.Write(spawn.size()); for (uint32_t i = 0; i < spawn.size(); ++i) { - stream.Write(uint16_t(spawn[i])); + stream.Write(spawn[i]); } stream.Write(0); // String length @@ -71,9 +71,9 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con // Does this go here??? // const auto& rejectionReasonConverted = GeneralUtils::UTF8ToUTF16(rejectionReason); - // stream.Write(uint32_t(rejectionReasonConverted.size())); + // stream.Write(rejectionReasonConverted.size()); // for (uint32_t i = 0; i < rejectionReasonConverted.size(); ++i) { - // stream.Write(uint16_t(rejectionReasonConverted[i])); + // stream.Write(rejectionReasonConverted[i]); // } stream.Write(0); @@ -93,7 +93,7 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con stream.Write(PrivacyOption); - stream.Write(uint32_t(Paths.size())); + stream.Write(Paths.size()); for (const auto& path : Paths) { stream.Write(path.x); @@ -103,7 +103,7 @@ void GameMessages::PropertyDataMessage::Serialize(RakNet::BitStream& stream) con } GameMessages::PropertyDataMessage::PropertyDataMessage(uint32_t mapID) { - const auto propertyTemplate = CDClientManager::Instance().GetTable()->GetByMapID(mapID); + const auto propertyTemplate = CDClientManager::GetTable()->GetByMapID(mapID); TemplateID = propertyTemplate.id; ZoneId = propertyTemplate.mapID; diff --git a/dGame/dGameMessages/PropertySelectQueryProperty.cpp b/dGame/dGameMessages/PropertySelectQueryProperty.cpp index 6e975879..2a256764 100644 --- a/dGame/dGameMessages/PropertySelectQueryProperty.cpp +++ b/dGame/dGameMessages/PropertySelectQueryProperty.cpp @@ -4,21 +4,21 @@ void PropertySelectQueryProperty::Serialize(RakNet::BitStream& stream) const { stream.Write(CloneId); const auto& owner = GeneralUtils::UTF8ToUTF16(OwnerName); - stream.Write(uint32_t(owner.size())); + stream.Write(owner.size()); for (uint32_t i = 0; i < owner.size(); ++i) { - stream.Write(static_cast(owner[i])); + stream.Write(owner[i]); } const auto& name = GeneralUtils::UTF8ToUTF16(Name); - stream.Write(uint32_t(name.size())); + stream.Write(name.size()); for (uint32_t i = 0; i < name.size(); ++i) { - stream.Write(static_cast(name[i])); + stream.Write(name[i]); } const auto& description = GeneralUtils::UTF8ToUTF16(Description); - stream.Write(uint32_t(description.size())); + stream.Write(description.size()); for (uint32_t i = 0; i < description.size(); ++i) { - stream.Write(static_cast(description[i])); + stream.Write(description[i]); } stream.Write(Reputation); diff --git a/dGame/dGameMessages/StartSkill.h b/dGame/dGameMessages/StartSkill.h index 40bc210f..6b0d267a 100644 --- a/dGame/dGameMessages/StartSkill.h +++ b/dGame/dGameMessages/StartSkill.h @@ -16,13 +16,13 @@ public: consumableItemID = LWOOBJID_EMPTY; fCasterLatency = 0.0f; iCastType = 0; - lastClickedPosit = NiPoint3::ZERO; + lastClickedPosit = NiPoint3Constant::ZERO; optionalTargetID = LWOOBJID_EMPTY; - originatorRot = NiQuaternion::IDENTITY; + originatorRot = NiQuaternionConstant::IDENTITY; uiSkillHandle = 0; } - StartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, LWOOBJID _consumableItemID = LWOOBJID_EMPTY, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternion::IDENTITY, uint32_t _uiSkillHandle = 0) { + StartSkill(LWOOBJID _optionalOriginatorID, std::string _sBitStream, TSkillID _skillID, bool _bUsedMouse = false, LWOOBJID _consumableItemID = LWOOBJID_EMPTY, float _fCasterLatency = 0.0f, int32_t _iCastType = 0, NiPoint3 _lastClickedPosit = NiPoint3Constant::ZERO, LWOOBJID _optionalTargetID = LWOOBJID_EMPTY, NiQuaternion _originatorRot = NiQuaternionConstant::IDENTITY, uint32_t _uiSkillHandle = 0) { bUsedMouse = _bUsedMouse; consumableItemID = _consumableItemID; fCasterLatency = _fCasterLatency; @@ -57,16 +57,16 @@ public: stream->Write(iCastType != 0); if (iCastType != 0) stream->Write(iCastType); - stream->Write(lastClickedPosit != NiPoint3::ZERO); - if (lastClickedPosit != NiPoint3::ZERO) stream->Write(lastClickedPosit); + stream->Write(lastClickedPosit != NiPoint3Constant::ZERO); + if (lastClickedPosit != NiPoint3Constant::ZERO) stream->Write(lastClickedPosit); stream->Write(optionalOriginatorID); stream->Write(optionalTargetID != LWOOBJID_EMPTY); if (optionalTargetID != LWOOBJID_EMPTY) stream->Write(optionalTargetID); - stream->Write(originatorRot != NiQuaternion::IDENTITY); - if (originatorRot != NiQuaternion::IDENTITY) stream->Write(originatorRot); + stream->Write(originatorRot != NiQuaternionConstant::IDENTITY); + if (originatorRot != NiQuaternionConstant::IDENTITY) stream->Write(originatorRot); uint32_t sBitStreamLength = sBitStream.length(); stream->Write(sBitStreamLength); diff --git a/dGame/dInventory/CMakeLists.txt b/dGame/dInventory/CMakeLists.txt index 60cfca75..bc741efe 100644 --- a/dGame/dInventory/CMakeLists.txt +++ b/dGame/dInventory/CMakeLists.txt @@ -1,5 +1,15 @@ -set(DGAME_DINVENTORY_SOURCES "EquippedItem.cpp" +set(DGAME_DINVENTORY_SOURCES + "EquippedItem.cpp" "Inventory.cpp" "Item.cpp" "ItemSet.cpp" - "ItemSetPassiveAbility.cpp" PARENT_SCOPE) + "ItemSetPassiveAbility.cpp") + +# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible. +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185 +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + set_source_files_properties("Item.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow") +endif() + +add_library(dInventory STATIC ${DGAME_DINVENTORY_SOURCES}) +target_precompile_headers(dInventory REUSE_FROM dGameBase) diff --git a/dGame/dInventory/Inventory.cpp b/dGame/dInventory/Inventory.cpp index 3d2c82ae..35222bea 100644 --- a/dGame/dInventory/Inventory.cpp +++ b/dGame/dInventory/Inventory.cpp @@ -194,7 +194,7 @@ void Inventory::AddManagedItem(Item* item) { const auto id = item->GetId(); if (items.find(id) != items.end()) { - Game::logger->Log("Inventory", "Attempting to add an item with an already present id (%llu)!", id); + LOG("Attempting to add an item with an already present id (%llu)!", id); return; } @@ -204,7 +204,7 @@ void Inventory::AddManagedItem(Item* item) { const auto slot = item->GetSlot(); if (slots.find(slot) != slots.end()) { - Game::logger->Log("Inventory", "Attempting to add an item with an already present slot (%i)!", slot); + LOG("Attempting to add an item with an already present slot (%i)!", slot); return; } @@ -218,7 +218,7 @@ void Inventory::RemoveManagedItem(Item* item) { const auto id = item->GetId(); if (items.find(id) == items.end()) { - Game::logger->Log("Inventory", "Attempting to remove an item with an invalid id (%llu), lot (%i)!", id, item->GetLot()); + LOG("Attempting to remove an item with an invalid id (%llu), lot (%i)!", id, item->GetLot()); return; } @@ -275,14 +275,14 @@ eInventoryType Inventory::FindInventoryTypeForLot(const LOT lot) { } const CDItemComponent& Inventory::FindItemComponent(const LOT lot) { - auto* registry = CDClientManager::Instance().GetTable(); + auto* registry = CDClientManager::GetTable(); - auto* itemComponents = CDClientManager::Instance().GetTable(); + auto* itemComponents = CDClientManager::GetTable(); const auto componentId = registry->GetByIDAndType(lot, eReplicaComponentType::ITEM); if (componentId == 0) { - Game::logger->Log("Inventory", "Failed to find item component for (%i)!", lot); + LOG("Failed to find item component for (%i)!", lot); return CDItemComponentTable::Default; } @@ -293,7 +293,7 @@ const CDItemComponent& Inventory::FindItemComponent(const LOT lot) { } bool Inventory::IsValidItem(const LOT lot) { - auto* registry = CDClientManager::Instance().GetTable(); + auto* registry = CDClientManager::GetTable(); const auto componentId = registry->GetByIDAndType(lot, eReplicaComponentType::ITEM); diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index 64f1dfbd..d3f15315 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -2,12 +2,12 @@ #include -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "GeneralUtils.h" #include "GameMessages.h" #include "Entity.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "EntityManager.h" #include "RenderComponent.h" #include "PossessableComponent.h" @@ -19,6 +19,8 @@ #include "eObjectBits.h" #include "eReplicaComponentType.h" #include "eUseItemResponse.h" +#include "dZoneManager.h" +#include "ChatPackets.h" #include "CDBrickIDTableTable.h" #include "CDObjectSkillsTable.h" @@ -98,7 +100,7 @@ Item::Item( if (isModMoveAndEquip) { Equip(); - Game::logger->Log("Item", "Move and equipped (%i) from (%i)", this->lot, this->inventory->GetType()); + LOG("Move and equipped (%i) from (%i)", this->lot, this->inventory->GetType()); Game::entityManager->SerializeEntity(inventory->GetComponent()->GetParent()); } @@ -245,11 +247,11 @@ bool Item::IsEquipped() const { } bool Item::Consume() { - auto* skillsTable = CDClientManager::Instance().GetTable(); + auto* skillsTable = CDClientManager::GetTable(); - auto skills = skillsTable->Query([=](const CDObjectSkills entry) { + auto skills = skillsTable->Query([this](const CDObjectSkills entry) { return entry.objectTemplate == static_cast(lot); - }); + }); auto success = false; @@ -260,7 +262,7 @@ bool Item::Consume() { } } - Game::logger->LogDebug("Item", "Consumed LOT (%i) itemID (%llu). Success=(%d)", lot, id, success); + LOG_DEBUG("Consumed LOT (%i) itemID (%llu). Success=(%d)", lot, id, success); GameMessages::SendUseItemResult(inventory->GetComponent()->GetParent(), lot, success); @@ -274,42 +276,49 @@ bool Item::Consume() { void Item::UseNonEquip(Item* item) { LOT thisLot = this->GetLot(); if (!GetInventory()) { - Game::logger->LogDebug("Item", "item %i has no inventory??", this->GetLot()); + LOG_DEBUG("item %i has no inventory??", this->GetLot()); return; } auto* playerInventoryComponent = GetInventory()->GetComponent(); if (!playerInventoryComponent) { - Game::logger->LogDebug("Item", "no inventory component attached to item id %llu lot %i", this->GetId(), this->GetLot()); + LOG_DEBUG("no inventory component attached to item id %llu lot %i", this->GetId(), this->GetLot()); return; } auto* playerEntity = playerInventoryComponent->GetParent(); if (!playerEntity) { - Game::logger->LogDebug("Item", "no player entity attached to inventory? item id is %llu", this->GetId()); + LOG_DEBUG("no player entity attached to inventory? item id is %llu", this->GetId()); return; } const auto type = static_cast(info->itemType); if (type == eItemType::MOUNT) { - playerInventoryComponent->HandlePossession(this); - // TODO Check if mounts are allowed to be spawned + if (Game::zoneManager->GetMountsAllowed()) { + playerInventoryComponent->HandlePossession(this); + } else { + ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Mounts are not allowed in this zone"); + } } else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) { - const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey); - if (databasePet.lot != LOT_NULL) { - playerInventoryComponent->SpawnPet(this); + if (Game::zoneManager->GetPetsAllowed()) { + const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey); + if (databasePet.lot != LOT_NULL) { + playerInventoryComponent->SpawnPet(this); + } + } else { + ChatPackets::SendSystemMessage(playerEntity->GetSystemAddress(), u"Pets are not allowed in this zone"); } // This precondition response is taken care of in SpawnPet(). } else { bool success = false; auto inventory = item->GetInventory(); if (inventory && inventory->GetType() == eInventoryType::ITEMS) { - auto* compRegistryTable = CDClientManager::Instance().GetTable(); + auto* compRegistryTable = CDClientManager::GetTable(); const auto packageComponentId = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::PACKAGE); if (packageComponentId == 0) return; - auto* packCompTable = CDClientManager::Instance().GetTable(); + auto* packCompTable = CDClientManager::GetTable(); auto packages = packCompTable->Query([=](const CDPackageComponent entry) {return entry.id == static_cast(packageComponentId); }); auto success = !packages.empty(); @@ -319,7 +328,7 @@ void Item::UseNonEquip(Item* item) { // Roll the loot for all the packages then see if it all fits. If it fits, give it to the player, otherwise don't. std::unordered_map rolledLoot{}; for (auto& pack : packages) { - auto thisPackage = LootGenerator::Instance().RollLootMatrix(entityParent, pack.LootMatrixIndex); + auto thisPackage = Loot::RollLootMatrix(entityParent, pack.LootMatrixIndex); for (auto& loot : thisPackage) { // If we already rolled this lot, add it to the existing one, otherwise create a new entry. auto existingLoot = rolledLoot.find(loot.first); @@ -331,7 +340,7 @@ void Item::UseNonEquip(Item* item) { } } if (playerInventoryComponent->HasSpaceForLoot(rolledLoot)) { - LootGenerator::Instance().GiveLoot(playerInventoryComponent->GetParent(), rolledLoot, eLootSourceType::CONSUMPTION); + Loot::GiveLoot(playerInventoryComponent->GetParent(), rolledLoot, eLootSourceType::CONSUMPTION); item->SetCount(item->GetCount() - 1); } else { success = false; @@ -346,7 +355,7 @@ void Item::UseNonEquip(Item* item) { } } } - Game::logger->LogDebug("Item", "Player %llu %s used item %i", playerEntity->GetObjectID(), success ? "successfully" : "unsuccessfully", thisLot); + LOG_DEBUG("Player %llu %s used item %i", playerEntity->GetObjectID(), success ? "successfully" : "unsuccessfully", thisLot); GameMessages::SendUseItemResult(playerInventoryComponent->GetParent(), thisLot, success); } } @@ -386,14 +395,13 @@ void Item::Disassemble(const eInventoryType inventoryType) { } } -void Item::DisassembleModel() { - auto* table = CDClientManager::Instance().GetTable(); +void Item::DisassembleModel(uint32_t numToDismantle) { + auto* table = CDClientManager::GetTable(); const auto componentId = table->GetByIDAndType(GetLot(), eReplicaComponentType::RENDER); - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT render_asset, LXFMLFolder FROM RenderComponent WHERE id = ?;"); - query.bind(1, (int)componentId); + auto query = CDClientDatabase::CreatePreppedStmt("SELECT render_asset, LXFMLFolder FROM RenderComponent WHERE id = ?;"); + query.bind(1, static_cast(componentId)); auto result = query.execQuery(); @@ -402,83 +410,89 @@ void Item::DisassembleModel() { } std::string renderAsset = std::string(result.getStringField(0)); - std::string lxfmlFolderName = std::string(result.getStringField(1)); - std::vector renderAssetSplit = GeneralUtils::SplitString(renderAsset, '\\'); - if (renderAssetSplit.size() == 0) return; - - std::string lxfmlPath = "BrickModels/" + lxfmlFolderName + "/" + GeneralUtils::SplitString(renderAssetSplit.back(), '.').at(0) + ".lxfml"; - auto buffer = Game::assetManager->GetFileAsBuffer(lxfmlPath.c_str()); - - if (!buffer.m_Success) { - Game::logger->Log("Item", "Failed to load %s to disassemble model into bricks, check that this file exists", lxfmlPath.c_str()); - return; + // normalize path slashes + for (auto& c : renderAsset) { + if (c == '\\') c = '/'; } - std::istream file(&buffer); + std::string lxfmlFolderName = std::string(result.getStringField(1)); + if (!lxfmlFolderName.empty()) lxfmlFolderName.insert(0, "/"); - result.finalize(); + std::vector renderAssetSplit = GeneralUtils::SplitString(renderAsset, '/'); + if (renderAssetSplit.empty()) return; - if (!file.good()) { - buffer.close(); + std::string lxfmlPath = "BrickModels" + lxfmlFolderName + "/" + GeneralUtils::SplitString(renderAssetSplit.back(), '.').at(0) + ".lxfml"; + auto file = Game::assetManager->GetFile(lxfmlPath.c_str()); + + if (!file) { + LOG("Failed to load %s to disassemble model into bricks, check that this file exists", lxfmlPath.c_str()); return; } std::stringstream data; data << file.rdbuf(); - buffer.close(); + uint32_t fileSize; + file.seekg(0, std::ios::end); + fileSize = static_cast(file.tellg()); + file.seekg(0, std::ios::beg); - if (data.str().empty()) { + if (fileSize == 0) return; + + tinyxml2::XMLDocument doc; + + if (doc.Parse(data.str().c_str(), data.str().size()) != tinyxml2::XML_SUCCESS) { return; } - auto* doc = new tinyxml2::XMLDocument(); - - if (!doc) { - return; - } - - if (doc->Parse(data.str().c_str(), data.str().size()) != 0) { - return; - } - - std::vector parts; - - auto* lxfml = doc->FirstChildElement("LXFML"); + auto* lxfml = doc.FirstChildElement("LXFML"); + if (!lxfml) return; auto* bricks = lxfml->FirstChildElement("Bricks"); std::string searchTerm = "Brick"; if (!bricks) { searchTerm = "Part"; - bricks = lxfml->FirstChildElement("Scene")->FirstChildElement("Model")->FirstChildElement("Group"); + auto* scene = lxfml->FirstChildElement("Scene"); + if (!scene) return; - if (!bricks) { - return; - } + auto* model = scene->FirstChildElement("Model"); + if (!model) return; + + bricks = model->FirstChildElement("Group"); + if (!bricks) return; } auto* currentBrick = bricks->FirstChildElement(searchTerm.c_str()); + + // First iteration gets the count + std::map parts; while (currentBrick) { - if (currentBrick->Attribute("designID") != nullptr) { - parts.push_back(std::stoi(currentBrick->Attribute("designID"))); + const char* const designID = currentBrick->Attribute("designID"); + if (designID) { + const auto designId = GeneralUtils::TryParse(designID); + if (!designId) { + LOG("Failed to parse designID %s", designID); + continue; + } + parts[designId.value()]++; } currentBrick = currentBrick->NextSiblingElement(searchTerm.c_str()); } - auto* brickIDTable = CDClientManager::Instance().GetTable(); + auto* brickIDTable = CDClientManager::GetTable(); - for (unsigned int part : parts) { - const auto brickID = brickIDTable->Query([=](const CDBrickIDTable& entry) { - return entry.LEGOBrickID == part; + // Second iteration actually distributes the bricks + for (const auto& [part, count] : parts) { + const auto partLocal = part; + const auto brickID = brickIDTable->Query([&](const CDBrickIDTable& entry) { + return entry.LEGOBrickID == partLocal; }); - if (brickID.empty()) { - continue; - } + if (brickID.empty()) continue; - GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, 1, eLootSourceType::DELETION); + GetInventory()->GetComponent()->AddItem(brickID[0].NDObjectID, count * numToDismantle, eLootSourceType::DELETION); } } diff --git a/dGame/dInventory/Item.h b/dGame/dInventory/Item.h index be2359ef..04d05d7c 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.h @@ -4,7 +4,7 @@ #include "Inventory.h" #include "LDFFormat.h" #include "CDClientManager.h" -#include "dLogger.h" +#include "Logger.h" #include "Preconditions.h" #include "eInventoryType.h" #include "eLootSourceType.h" @@ -207,7 +207,7 @@ public: /** * Disassembles this item into bricks */ - void DisassembleModel(); + void DisassembleModel(uint32_t numToDismantle); /** * Removes the item from the linked inventory diff --git a/dGame/dInventory/ItemSet.cpp b/dGame/dInventory/ItemSet.cpp index a8e58739..1d086786 100644 --- a/dGame/dInventory/ItemSet.cpp +++ b/dGame/dInventory/ItemSet.cpp @@ -19,7 +19,7 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) { auto query = CDClientDatabase::CreatePreppedStmt( "SELECT skillSetWith2, skillSetWith3, skillSetWith4, skillSetWith5, skillSetWith6, itemIDs FROM ItemSets WHERE setID = ?;"); - query.bind(1, (int)id); + query.bind(1, static_cast(id)); auto result = query.execQuery(); @@ -87,10 +87,8 @@ ItemSet::ItemSet(const uint32_t id, InventoryComponent* inventoryComponent) { m_Items = {}; while (std::getline(stream, token, ',')) { - int32_t value; - if (GeneralUtils::TryParse(token, value)) { - m_Items.push_back(value); - } + const auto validToken = GeneralUtils::TryParse(token); + if (validToken) m_Items.push_back(validToken.value()); } m_Equipped = {}; @@ -129,7 +127,7 @@ void ItemSet::OnEquip(const LOT lot) { auto* missionComponent = m_InventoryComponent->GetParent()->GetComponent(); for (const auto skill : skillSet) { - auto* skillTable = CDClientManager::Instance().GetTable(); + auto* skillTable = CDClientManager::GetTable(); const auto behaviorId = skillTable->GetSkillByID(skill).behaviorID; @@ -161,7 +159,7 @@ void ItemSet::OnUnEquip(const LOT lot) { const auto& skillComponent = m_InventoryComponent->GetParent()->GetComponent(); for (const auto skill : skillSet) { - auto* skillTable = CDClientManager::Instance().GetTable(); + auto* skillTable = CDClientManager::GetTable(); const auto behaviorId = skillTable->GetSkillByID(skill).behaviorID; diff --git a/dGame/dMission/CMakeLists.txt b/dGame/dMission/CMakeLists.txt index 652c3cb2..4e4bdec2 100644 --- a/dGame/dMission/CMakeLists.txt +++ b/dGame/dMission/CMakeLists.txt @@ -1,3 +1,8 @@ -set(DGAME_DMISSION_SOURCES "Mission.cpp" +set(DGAME_DMISSION_SOURCES + "Mission.cpp" "MissionPrerequisites.cpp" - "MissionTask.cpp" PARENT_SCOPE) + "MissionTask.cpp") + +add_library(dMission STATIC ${DGAME_DMISSION_SOURCES}) +target_link_libraries(dMission PUBLIC dDatabase) +target_precompile_headers(dMission REUSE_FROM dGameBase) diff --git a/dGame/dMission/Mission.cpp b/dGame/dMission/Mission.cpp index 34641335..4ed80bf3 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -13,7 +13,7 @@ #include "Mail.h" #include "MissionComponent.h" #include "eRacingTaskParam.h" -#include "dLogger.h" +#include "Logger.h" #include "dServer.h" #include "dZoneManager.h" #include "InventoryComponent.h" @@ -24,6 +24,7 @@ #include "eMissionTaskType.h" #include "eMissionLockState.h" #include "eReplicaComponentType.h" +#include "Character.h" #include "CDMissionEmailTable.h" @@ -40,17 +41,18 @@ Mission::Mission(MissionComponent* missionComponent, const uint32_t missionId) { m_State = eMissionState::UNKNOWN; - auto* missionsTable = CDClientManager::Instance().GetTable(); + auto* missionsTable = CDClientManager::GetTable(); - info = missionsTable->GetPtrByMissionID(missionId); + auto* mis = missionsTable->GetPtrByMissionID(missionId); + info = *mis; - if (info == &CDMissionsTable::Default) { - Game::logger->Log("Missions", "Failed to find mission (%i)!", missionId); + if (mis == &CDMissionsTable::Default) { + LOG("Failed to find mission (%i)!", missionId); return; } - auto* tasksTable = CDClientManager::Instance().GetTable(); + auto* tasksTable = CDClientManager::GetTable(); auto tasks = tasksTable->GetByMissionID(missionId); @@ -137,7 +139,7 @@ void Mission::UpdateXml(tinyxml2::XMLElement* element) { element->DeleteChildren(); - element->SetAttribute("id", static_cast(info->id)); + element->SetAttribute("id", static_cast(info.id)); if (m_Completions > 0) { element->SetAttribute("cct", static_cast(m_Completions)); @@ -178,7 +180,7 @@ void Mission::UpdateXml(tinyxml2::XMLElement* element) { } bool Mission::IsValidMission(const uint32_t missionId) { - auto* table = CDClientManager::Instance().GetTable(); + auto* table = CDClientManager::GetTable(); const auto missions = table->Query([=](const CDMissions& entry) { return entry.id == static_cast(missionId); @@ -188,7 +190,7 @@ bool Mission::IsValidMission(const uint32_t missionId) { } bool Mission::IsValidMission(const uint32_t missionId, CDMissions& info) { - auto* table = CDClientManager::Instance().GetTable(); + auto* table = CDClientManager::GetTable(); const auto missions = table->Query([=](const CDMissions& entry) { return entry.id == static_cast(missionId); @@ -207,16 +209,16 @@ Entity* Mission::GetAssociate() const { return m_MissionComponent->GetParent(); } -User* Mission::GetUser() const { - return GetAssociate()->GetParentUser(); +Character* Mission::GetCharacter() const { + return GetAssociate()->GetCharacter(); } uint32_t Mission::GetMissionId() const { - return info->id; + return info.id; } const CDMissions& Mission::GetClientInfo() const { - return *info; + return info; } uint32_t Mission::GetCompletions() const { @@ -240,15 +242,15 @@ eMissionState Mission::GetMissionState() const { } bool Mission::IsAchievement() const { - return !info->isMission; + return !info.isMission; } bool Mission::IsMission() const { - return info->isMission; + return info.isMission; } bool Mission::IsRepeatable() const { - return info->repeatable; + return info.repeatable; } bool Mission::IsComplete() const { @@ -256,7 +258,7 @@ bool Mission::IsComplete() const { } bool Mission::IsActive() const { - return m_State == eMissionState::ACTIVE || m_State == eMissionState::COMPLETE_AVAILABLE; + return m_State == eMissionState::ACTIVE || m_State == eMissionState::COMPLETE_ACTIVE; } void Mission::MakeActive() { @@ -284,7 +286,7 @@ void Mission::MakeAvalible() { } void Mission::Accept() { - SetMissionTypeState(eMissionLockState::NEW, info->defined_type, info->defined_subtype); + SetMissionTypeState(eMissionLockState::NEW, info.defined_type, info.defined_subtype); SetMissionState(m_Completions > 0 ? eMissionState::COMPLETE_ACTIVE : eMissionState::ACTIVE); @@ -321,18 +323,18 @@ void Mission::Complete(const bool yieldRewards) { auto* characterComponent = entity->GetComponent(); if (characterComponent != nullptr) { - characterComponent->TrackMissionCompletion(!info->isMission); + characterComponent->TrackMissionCompletion(!info.isMission); } auto* missionComponent = entity->GetComponent(); - missionComponent->Progress(eMissionTaskType::META, info->id); + missionComponent->Progress(eMissionTaskType::META, info.id); - missionComponent->Progress(eMissionTaskType::RACING, info->id, (LWOOBJID)eRacingTaskParam::COMPLETE_ANY_RACING_TASK); + missionComponent->Progress(eMissionTaskType::RACING, info.id, static_cast(eRacingTaskParam::COMPLETE_ANY_RACING_TASK)); - missionComponent->Progress(eMissionTaskType::RACING, info->id, (LWOOBJID)eRacingTaskParam::COMPLETE_TRACK_TASKS); + missionComponent->Progress(eMissionTaskType::RACING, info.id, static_cast(eRacingTaskParam::COMPLETE_TRACK_TASKS)); - auto* missionEmailTable = CDClientManager::Instance().GetTable(); + auto* missionEmailTable = CDClientManager::GetTable(); const auto missionId = GetMissionId(); @@ -389,7 +391,7 @@ void Mission::Catchup() { if (type == eMissionTaskType::PLAYER_FLAG) { for (int32_t target : task->GetAllTargets()) { - const auto flag = GetUser()->GetLastUsedChar()->GetPlayerFlag(target); + const auto flag = GetCharacter()->GetPlayerFlag(target); if (!flag) { continue; @@ -412,7 +414,7 @@ void Mission::YieldRewards() { return; } - auto* character = GetUser()->GetLastUsedChar(); + auto* character = GetCharacter(); auto* inventoryComponent = entity->GetComponent(); auto* levelComponent = entity->GetComponent(); @@ -441,24 +443,24 @@ void Mission::YieldRewards() { } int32_t coinsToSend = 0; - if (info->LegoScore > 0) { - eLootSourceType lootSource = info->isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; + if (info.LegoScore > 0) { + eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; if (levelComponent->GetLevel() >= Game::zoneManager->GetWorldConfig()->levelCap) { // Since the character is at the level cap we reward them with coins instead of UScore. - coinsToSend += info->LegoScore * Game::zoneManager->GetWorldConfig()->levelCapCurrencyConversion; + coinsToSend += info.LegoScore * Game::zoneManager->GetWorldConfig()->levelCapCurrencyConversion; } else { - characterComponent->SetUScore(characterComponent->GetUScore() + info->LegoScore); - GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info->LegoScore, lootSource); + characterComponent->SetUScore(characterComponent->GetUScore() + info.LegoScore); + GameMessages::SendModifyLEGOScore(entity, entity->GetSystemAddress(), info.LegoScore, lootSource); } } if (m_Completions > 0) { std::vector> items; - items.emplace_back(info->reward_item1_repeatable, info->reward_item1_repeat_count); - items.emplace_back(info->reward_item2_repeatable, info->reward_item2_repeat_count); - items.emplace_back(info->reward_item3_repeatable, info->reward_item3_repeat_count); - items.emplace_back(info->reward_item4_repeatable, info->reward_item4_repeat_count); + items.emplace_back(info.reward_item1_repeatable, info.reward_item1_repeat_count); + items.emplace_back(info.reward_item2_repeatable, info.reward_item2_repeat_count); + items.emplace_back(info.reward_item3_repeatable, info.reward_item3_repeat_count); + items.emplace_back(info.reward_item4_repeatable, info.reward_item4_repeat_count); for (const auto& pair : items) { // Some missions reward zero of an item and so they must be allowed through this clause, @@ -478,9 +480,9 @@ void Mission::YieldRewards() { inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT); } - if (info->reward_currency_repeatable > 0 || coinsToSend > 0) { - eLootSourceType lootSource = info->isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; - character->SetCoins(character->GetCoins() + info->reward_currency_repeatable + coinsToSend, lootSource); + if (info.reward_currency_repeatable > 0 || coinsToSend > 0) { + eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; + character->SetCoins(character->GetCoins() + info.reward_currency_repeatable + coinsToSend, lootSource); } return; @@ -488,10 +490,10 @@ void Mission::YieldRewards() { std::vector> items; - items.emplace_back(info->reward_item1, info->reward_item1_count); - items.emplace_back(info->reward_item2, info->reward_item2_count); - items.emplace_back(info->reward_item3, info->reward_item3_count); - items.emplace_back(info->reward_item4, info->reward_item4_count); + items.emplace_back(info.reward_item1, info.reward_item1_count); + items.emplace_back(info.reward_item2, info.reward_item2_count); + items.emplace_back(info.reward_item3, info.reward_item3_count); + items.emplace_back(info.reward_item4, info.reward_item4_count); for (const auto& pair : items) { // Some missions reward zero of an item and so they must be allowed through this clause, @@ -511,58 +513,58 @@ void Mission::YieldRewards() { inventoryComponent->AddItem(pair.first, count, IsMission() ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT); } - if (info->reward_currency > 0 || coinsToSend > 0) { - eLootSourceType lootSource = info->isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; - character->SetCoins(character->GetCoins() + info->reward_currency + coinsToSend, lootSource); + if (info.reward_currency > 0 || coinsToSend > 0) { + eLootSourceType lootSource = info.isMission ? eLootSourceType::MISSION : eLootSourceType::ACHIEVEMENT; + character->SetCoins(character->GetCoins() + info.reward_currency + coinsToSend, lootSource); } - if (info->reward_maxinventory > 0) { + if (info.reward_maxinventory > 0) { auto* inventory = inventoryComponent->GetInventory(ITEMS); - inventory->SetSize(inventory->GetSize() + info->reward_maxinventory); + inventory->SetSize(inventory->GetSize() + info.reward_maxinventory); } - if (info->reward_bankinventory > 0) { + if (info.reward_bankinventory > 0) { auto* inventory = inventoryComponent->GetInventory(eInventoryType::VAULT_ITEMS); auto modelInventory = inventoryComponent->GetInventory(eInventoryType::VAULT_MODELS); - inventory->SetSize(inventory->GetSize() + info->reward_bankinventory); - modelInventory->SetSize(modelInventory->GetSize() + info->reward_bankinventory); + inventory->SetSize(inventory->GetSize() + info.reward_bankinventory); + modelInventory->SetSize(modelInventory->GetSize() + info.reward_bankinventory); } - if (info->reward_reputation > 0) { - missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info->reward_reputation); + if (info.reward_reputation > 0) { + missionComponent->Progress(eMissionTaskType::EARN_REPUTATION, 0, 0L, "", info.reward_reputation); auto character = entity->GetComponent(); if (character) { - character->SetReputation(character->GetReputation() + info->reward_reputation); + character->SetReputation(character->GetReputation() + info.reward_reputation); GameMessages::SendUpdateReputation(entity->GetObjectID(), character->GetReputation(), entity->GetSystemAddress()); } } - if (info->reward_maxhealth > 0) { - destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast(info->reward_maxhealth), true); + if (info.reward_maxhealth > 0) { + destroyableComponent->SetMaxHealth(destroyableComponent->GetMaxHealth() + static_cast(info.reward_maxhealth), true); } - if (info->reward_maximagination > 0) { - destroyableComponent->SetMaxImagination(destroyableComponent->GetMaxImagination() + static_cast(info->reward_maximagination), true); + if (info.reward_maximagination > 0) { + destroyableComponent->SetMaxImagination(destroyableComponent->GetMaxImagination() + static_cast(info.reward_maximagination), true); } Game::entityManager->SerializeEntity(entity); - if (info->reward_emote > 0) { - character->UnlockEmote(info->reward_emote); + if (info.reward_emote > 0) { + character->UnlockEmote(info.reward_emote); } - if (info->reward_emote2 > 0) { - character->UnlockEmote(info->reward_emote2); + if (info.reward_emote2 > 0) { + character->UnlockEmote(info.reward_emote2); } - if (info->reward_emote3 > 0) { - character->UnlockEmote(info->reward_emote3); + if (info.reward_emote3 > 0) { + character->UnlockEmote(info.reward_emote3); } - if (info->reward_emote4 > 0) { - character->UnlockEmote(info->reward_emote4); + if (info.reward_emote4 > 0) { + character->UnlockEmote(info.reward_emote4); } } @@ -598,8 +600,10 @@ void Mission::SetMissionState(const eMissionState state, const bool sendingRewar if (entity == nullptr) { return; } + auto* characterComponent = entity->GetComponent(); + if (!characterComponent) return; - GameMessages::SendNotifyMission(entity, entity->GetParentUser()->GetSystemAddress(), info->id, static_cast(state), sendingRewards); + GameMessages::SendNotifyMission(entity, characterComponent->GetSystemAddress(), info.id, static_cast(state), sendingRewards); } void Mission::SetMissionTypeState(eMissionLockState state, const std::string& type, const std::string& subType) { diff --git a/dGame/dMission/Mission.h b/dGame/dMission/Mission.h index b04c3548..d8c104e8 100644 --- a/dGame/dMission/Mission.h +++ b/dGame/dMission/Mission.h @@ -17,6 +17,7 @@ namespace tinyxml2 { enum class eMissionState : int; enum class eMissionLockState : int; class MissionComponent; +class Character; /** * A mission (or achievement) that a player may unlock, progress and complete. @@ -46,7 +47,7 @@ public: * Returns the account owns the entity that is currently progressing this mission * @return the account owns the entity that is currently progressing this mission */ - User* GetUser() const; + Character* GetCharacter() const; /** * Returns the current state of this mission @@ -245,7 +246,7 @@ private: /** * The database information that corresponds to this mission */ - const CDMissions* info; + CDMissions info; /** * The current state this mission is in diff --git a/dGame/dMission/MissionPrerequisites.cpp b/dGame/dMission/MissionPrerequisites.cpp index ec4522b9..b5b81160 100644 --- a/dGame/dMission/MissionPrerequisites.cpp +++ b/dGame/dMission/MissionPrerequisites.cpp @@ -4,7 +4,7 @@ #include #include "CDClientManager.h" -#include "dLogger.h" +#include "Logger.h" PrerequisiteExpression::PrerequisiteExpression(const std::string& str) { @@ -105,9 +105,7 @@ bool PrerequisiteExpression::Execute(const std::unordered_mapsub != 0) { // Special case for one Wisp Lee repeatable mission. - a = mission->GetClientInfo().id == 1883 ? - mission->GetMissionState() == static_cast(this->sub) : - mission->GetMissionState() >= static_cast(this->sub); + a = mission->GetMissionState() == static_cast(this->sub); } else if (mission->IsComplete()) { a = true; } @@ -163,7 +161,7 @@ bool MissionPrerequisites::CheckPrerequisites(uint32_t missionId, const std::uno return index->second->Execute(missions); } - auto* missionsTable = CDClientManager::Instance().GetTable(); + auto* missionsTable = CDClientManager::GetTable(); const auto missionEntries = missionsTable->Query([=](const CDMissions& entry) { return entry.id == static_cast(missionId); }); diff --git a/dGame/dMission/MissionTask.cpp b/dGame/dMission/MissionTask.cpp index 997fd34e..2fe9bc9f 100644 --- a/dGame/dMission/MissionTask.cpp +++ b/dGame/dMission/MissionTask.cpp @@ -3,7 +3,7 @@ #include "MissionTask.h" #include "Game.h" -#include "dLogger.h" +#include "Logger.h" #include "Mission.h" #include "Character.h" #include "dServer.h" @@ -27,19 +27,15 @@ MissionTask::MissionTask(Mission* mission, CDMissionTasks* info, uint32_t mask) std::string token; while (std::getline(stream, token, ',')) { - uint32_t parameter; - if (GeneralUtils::TryParse(token, parameter)) { - parameters.push_back(parameter); - } + const auto parameter = GeneralUtils::TryParse(token); + if (parameter) parameters.push_back(parameter.value()); } stream = std::istringstream(info->targetGroup); while (std::getline(stream, token, ',')) { - uint32_t parameter; - if (GeneralUtils::TryParse(token, parameter)) { - targets.push_back(parameter); - } + const auto parameter = GeneralUtils::TryParse(token); + if (parameter) targets.push_back(parameter.value()); } } @@ -233,7 +229,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& entity = Game::entityManager->GetEntity(associate); if (entity == nullptr) { if (associate != LWOOBJID_EMPTY) { - Game::logger->Log("MissionTask", "Failed to find associated entity (%llu)!", associate); + LOG("Failed to find associated entity (%llu)!", associate); } break; } @@ -275,7 +271,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& entity = Game::entityManager->GetEntity(associate); if (entity == nullptr) { - Game::logger->Log("MissionTask", "Failed to find associated entity (%llu)!", associate); + LOG("Failed to find associated entity (%llu)!", associate); break; } @@ -349,7 +345,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& entity = Game::entityManager->GetEntity(associate); if (entity == nullptr) { - Game::logger->Log("MissionTask", "Failed to find associated entity (%llu)!", associate); + LOG("Failed to find associated entity (%llu)!", associate); break; } @@ -453,7 +449,7 @@ void MissionTask::Progress(int32_t value, LWOOBJID associate, const std::string& AddProgress(count); break; default: - Game::logger->Log("MissionTask", "Invalid mission task type (%i)!", static_cast(type)); + LOG("Invalid mission task type (%i)!", static_cast(type)); return; } diff --git a/dGame/dPropertyBehaviors/CMakeLists.txt b/dGame/dPropertyBehaviors/CMakeLists.txt index 5e33a5f5..47c8ff23 100644 --- a/dGame/dPropertyBehaviors/CMakeLists.txt +++ b/dGame/dPropertyBehaviors/CMakeLists.txt @@ -1,4 +1,7 @@ set(DGAME_DPROPERTYBEHAVIORS_SOURCES + "PropertyBehavior.cpp" + "State.cpp" + "Strip.cpp" "BlockDefinition.cpp" "ControlBehaviors.cpp" ) @@ -9,4 +12,5 @@ foreach(file ${DGAME_DPROPERTYBEHAVIORS_CONTROLBEHAVIORMESSAGES}) set(DGAME_DPROPERTYBEHAVIORS_SOURCES ${DGAME_DPROPERTYBEHAVIORS_SOURCES} "ControlBehaviorMessages/${file}") endforeach() -set(DGAME_DPROPERTYBEHAVIORS_SOURCES ${DGAME_DPROPERTYBEHAVIORS_SOURCES} PARENT_SCOPE) +add_library(dPropertyBehaviors STATIC ${DGAME_DPROPERTYBEHAVIORS_SOURCES}) +target_precompile_headers(dPropertyBehaviors REUSE_FROM dGameBase) diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp index eabe76b7..73f1391d 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp @@ -1,4 +1,5 @@ #include "Action.h" +#include "Amf3.h" Action::Action() { type = ""; @@ -12,20 +13,34 @@ Action::Action(AMFArrayValue* arguments) { valueParameterName = ""; valueParameterString = ""; valueParameterDouble = 0.0; - for (auto& typeValueMap : arguments->GetAssociative()) { - if (typeValueMap.first == "Type") { - if (typeValueMap.second->GetValueType() != eAmf::String) continue; - type = static_cast(typeValueMap.second)->GetValue(); + for (auto& [paramName, paramValue] : arguments->GetAssociative()) { + if (paramName == "Type") { + if (paramValue->GetValueType() != eAmf::String) continue; + type = static_cast(paramValue)->GetValue(); } else { - valueParameterName = typeValueMap.first; + valueParameterName = paramName; // Message is the only known string parameter if (valueParameterName == "Message") { - if (typeValueMap.second->GetValueType() != eAmf::String) continue; - valueParameterString = static_cast(typeValueMap.second)->GetValue(); + if (paramValue->GetValueType() != eAmf::String) continue; + valueParameterString = static_cast(paramValue)->GetValue(); } else { - if (typeValueMap.second->GetValueType() != eAmf::Double) continue; - valueParameterDouble = static_cast(typeValueMap.second)->GetValue(); + if (paramValue->GetValueType() != eAmf::Double) continue; + valueParameterDouble = static_cast(paramValue)->GetValue(); } } } } + +void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const { + auto* actionArgs = args.PushArray(); + actionArgs->Insert("Type", type); + + auto valueParameterName = GetValueParameterName(); + if (valueParameterName.empty()) return; + + if (valueParameterName == "Message") { + actionArgs->Insert(valueParameterName, valueParameterString); + } else { + actionArgs->Insert(valueParameterName, valueParameterDouble); + } +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h index c97b4050..df665889 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h @@ -1,7 +1,9 @@ #ifndef __ACTION__H__ #define __ACTION__H__ -#include "BehaviorMessageBase.h" +#include + +class AMFArrayValue; /** * @brief Sent if a ControlBehavior message has an Action associated with it @@ -11,10 +13,12 @@ class Action { public: Action(); Action(AMFArrayValue* arguments); - const std::string& GetType() { return type; }; - const std::string& GetValueParameterName() { return valueParameterName; }; - const std::string& GetValueParameterString() { return valueParameterString; }; - const double GetValueParameterDouble() { return valueParameterDouble; }; + const std::string& GetType() const { return type; }; + const std::string& GetValueParameterName() const { return valueParameterName; }; + const std::string& GetValueParameterString() const { return valueParameterString; }; + const double GetValueParameterDouble() const { return valueParameterDouble; }; + + void SendBehaviorBlocksToClient(AMFArrayValue& args) const; private: std::string type; std::string valueParameterName; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/ActionContext.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/ActionContext.h index 5f46fd8c..91e91e72 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/ActionContext.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/ActionContext.h @@ -14,8 +14,8 @@ class ActionContext { public: ActionContext(); ActionContext(AMFArrayValue* arguments, std::string customStateKey = "stateID", std::string customStripKey = "stripID"); - const StripId GetStripId() { return stripId; }; - const BehaviorState GetStateId() { return stateId; }; + const StripId GetStripId() const { return stripId; }; + const BehaviorState GetStateId() const { return stateId; }; private: BehaviorState GetBehaviorStateFromArgument(AMFArrayValue* arguments, const std::string& key); StripId GetStripIdFromArgument(AMFArrayValue* arguments, const std::string& key); diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.cpp index 98672909..36d9a3dc 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.cpp @@ -9,5 +9,5 @@ AddActionMessage::AddActionMessage(AMFArrayValue* arguments) : BehaviorMessageBa action = Action(actionValue); - Game::logger->LogDebug("AddActionMessage", "actionIndex %i stripId %i stateId %i type %s valueParameterName %s valueParameterString %s valueParameterDouble %f behaviorId %i", actionIndex, actionContext.GetStripId(), actionContext.GetStateId(), action.GetType().c_str(), action.GetValueParameterName().c_str(), action.GetValueParameterString().c_str(), action.GetValueParameterDouble(), behaviorId); + LOG_DEBUG("actionIndex %i stripId %i stateId %i type %s valueParameterName %s valueParameterString %s valueParameterDouble %f behaviorId %i", actionIndex, actionContext.GetStripId(), actionContext.GetStateId(), action.GetType().c_str(), action.GetValueParameterName().c_str(), action.GetValueParameterString().c_str(), action.GetValueParameterDouble(), behaviorId); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.h index 4faf6a53..ac3a9612 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddActionMessage.h @@ -14,11 +14,11 @@ class AMFArrayValue; class AddActionMessage : public BehaviorMessageBase { public: AddActionMessage(AMFArrayValue* arguments); - const uint32_t GetActionIndex() { return actionIndex; }; - Action GetAction() { return action; }; - ActionContext GetActionContext() { return actionContext; }; + int32_t GetActionIndex() const { return actionIndex; }; + Action GetAction() const { return action; }; + ActionContext GetActionContext() const { return actionContext; }; private: - uint32_t actionIndex; + int32_t actionIndex = -1; ActionContext actionContext; Action action; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.cpp index badee2c2..cf96ab13 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.cpp @@ -7,5 +7,5 @@ AddMessage::AddMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments if (!behaviorIndexValue) return; behaviorIndex = static_cast(behaviorIndexValue->GetValue()); - Game::logger->LogDebug("AddMessage", "behaviorId %i index %i", behaviorId, behaviorIndex); + LOG_DEBUG("behaviorId %i index %i", behaviorId, behaviorIndex); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.h index a46d5f98..76627665 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddMessage.h @@ -10,7 +10,7 @@ class AddMessage : public BehaviorMessageBase { public: AddMessage(AMFArrayValue* arguments); - const uint32_t GetBehaviorIndex() { return behaviorIndex; }; + const uint32_t GetBehaviorIndex() const { return behaviorIndex; }; private: uint32_t behaviorIndex; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.cpp index 261b3462..ac6e8db7 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.cpp @@ -2,7 +2,7 @@ #include "Action.h" -AddStripMessage::AddStripMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) { +AddStripMessage::AddStripMessage(AMFArrayValue* const arguments) : BehaviorMessageBase{ arguments } { actionContext = ActionContext(arguments); position = StripUiPosition(arguments); @@ -18,7 +18,11 @@ AddStripMessage::AddStripMessage(AMFArrayValue* arguments) : BehaviorMessageBase actionsToAdd.push_back(Action(actionValue)); - Game::logger->LogDebug("AddStripMessage", "xPosition %f yPosition %f stripId %i stateId %i behaviorId %i t %s valueParameterName %s valueParameterString %s valueParameterDouble %f", position.GetX(), position.GetY(), actionContext.GetStripId(), actionContext.GetStateId(), behaviorId, actionsToAdd.back().GetType().c_str(), actionsToAdd.back().GetValueParameterName().c_str(), actionsToAdd.back().GetValueParameterString().c_str(), actionsToAdd.back().GetValueParameterDouble()); + LOG_DEBUG("xPosition %f yPosition %f stripId %i stateId %i behaviorId %i t %s valueParameterName %s valueParameterString %s valueParameterDouble %f", position.GetX(), position.GetY(), actionContext.GetStripId(), actionContext.GetStateId(), behaviorId, actionsToAdd.back().GetType().c_str(), actionsToAdd.back().GetValueParameterName().c_str(), actionsToAdd.back().GetValueParameterString().c_str(), actionsToAdd.back().GetValueParameterDouble()); } - Game::logger->Log("AddStripMessage", "number of actions %i", actionsToAdd.size()); + LOG_DEBUG("number of actions %i", actionsToAdd.size()); +} + +std::vector AddStripMessage::GetActionsToAdd() const { + return actionsToAdd; } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h index db75aef7..2e2bf9a0 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h @@ -18,10 +18,10 @@ class AMFArrayValue; */ class AddStripMessage : public BehaviorMessageBase { public: - AddStripMessage(AMFArrayValue* arguments); - StripUiPosition GetPosition() { return position; }; - ActionContext GetActionContext() { return actionContext; }; - std::vector GetActionsToAdd() { return actionsToAdd; }; + AddStripMessage(AMFArrayValue* const arguments); + StripUiPosition GetPosition() const { return position; }; + ActionContext GetActionContext() const { return actionContext; }; + std::vector GetActionsToAdd() const; private: StripUiPosition position; ActionContext actionContext; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp index 3286504a..a49a8aeb 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp @@ -5,29 +5,26 @@ #include "dCommonVars.h" BehaviorMessageBase::BehaviorMessageBase(AMFArrayValue* arguments) { - behaviorId = 0; - behaviorId = GetBehaviorIdFromArgument(arguments); + this->behaviorId = GetBehaviorIdFromArgument(arguments); } -int32_t BehaviorMessageBase::GetBehaviorIdFromArgument(AMFArrayValue* arguments) { - const auto* key = "BehaviorID"; - auto* behaviorIDValue = arguments->Get(key); - int32_t behaviorID = -1; +int32_t BehaviorMessageBase::GetBehaviorIdFromArgument(AMFArrayValue* const arguments) { + const char* const key = "BehaviorID"; + const auto* const behaviorIDValue = arguments->Get(key); if (behaviorIDValue && behaviorIDValue->GetValueType() == eAmf::String) { - behaviorID = std::stoul(behaviorIDValue->GetValue()); - } else if (arguments->Get(key)->GetValueType() != eAmf::Undefined) { + this->behaviorId = + GeneralUtils::TryParse(behaviorIDValue->GetValue()).value_or(this->behaviorId); + } else if (arguments->Get(key) && arguments->Get(key)->GetValueType() != eAmf::Undefined) { throw std::invalid_argument("Unable to find behavior ID"); } - return behaviorID; + return this->behaviorId; } -uint32_t BehaviorMessageBase::GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName) { - auto* actionIndexAmf = arguments->Get(keyName); - if (!actionIndexAmf) { - throw std::invalid_argument("Unable to find actionIndex"); - } +int32_t BehaviorMessageBase::GetActionIndexFromArgument(AMFArrayValue* const arguments, const std::string& keyName) const { + const auto* const actionIndexAmf = arguments->Get(keyName); + if (!actionIndexAmf) throw std::invalid_argument("Unable to find actionIndex"); - return static_cast(actionIndexAmf->GetValue()); + return static_cast(actionIndexAmf->GetValue()); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.h index 8771286c..f55fde8e 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.h @@ -7,9 +7,6 @@ #include "Amf3.h" #include "dCommonVars.h" -#include "Game.h" -#include "dLogger.h" - enum class BehaviorState : uint32_t; /** @@ -18,12 +15,14 @@ enum class BehaviorState : uint32_t; */ class BehaviorMessageBase { public: - const uint32_t GetBehaviorId() { return behaviorId; }; + static constexpr int32_t DefaultBehaviorId = -1; + [[nodiscard]] int32_t GetBehaviorId() const { return behaviorId; }; + [[nodiscard]] bool IsDefaultBehaviorId() { return behaviorId == DefaultBehaviorId; }; + BehaviorMessageBase(AMFArrayValue* const arguments); protected: - BehaviorMessageBase(AMFArrayValue* arguments); - int32_t GetBehaviorIdFromArgument(AMFArrayValue* arguments); - uint32_t GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName = "actionIndex"); - int32_t behaviorId = -1; + [[nodiscard]] int32_t GetBehaviorIdFromArgument(AMFArrayValue* const arguments); + [[nodiscard]] int32_t GetActionIndexFromArgument(AMFArrayValue* const arguments, const std::string& keyName = "actionIndex") const; + int32_t behaviorId = DefaultBehaviorId; }; #endif //!__BEHAVIORMESSAGEBASE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/ControlBehaviorMsgs.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/ControlBehaviorMsgs.h new file mode 100644 index 00000000..0e17060b --- /dev/null +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/ControlBehaviorMsgs.h @@ -0,0 +1,23 @@ +#ifndef __CONTROLBEHAVIORMSGS__H__ +#define __CONTROLBEHAVIORMSGS__H__ + +#include "Action.h" +#include "ActionContext.h" +#include "AddActionMessage.h" +#include "AddMessage.h" +#include "AddStripMessage.h" +#include "BehaviorMessageBase.h" +#include "ControlBehaviorMsgs.h" +#include "MergeStripsMessage.h" +#include "MigrateActionsMessage.h" +#include "MoveToInventoryMessage.h" +#include "RearrangeStripMessage.h" +#include "RemoveActionsMessage.h" +#include "RemoveStripMessage.h" +#include "RenameMessage.h" +#include "SplitStripMessage.h" +#include "StripUiPosition.h" +#include "UpdateActionMessage.h" +#include "UpdateStripUiMessage.h" + +#endif //!__CONTROLBEHAVIORMSGS__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.cpp index df50641a..18327ecf 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.cpp @@ -6,6 +6,6 @@ MergeStripsMessage::MergeStripsMessage(AMFArrayValue* arguments) : BehaviorMessa destinationActionContext = ActionContext(arguments, "dstStateID", "dstStripID"); dstActionIndex = GetActionIndexFromArgument(arguments, "dstActionIndex"); - Game::logger->LogDebug("MergeStripsMessage", "srcstripId %i dststripId %i srcstateId %i dststateId %i dstactionIndex %i behaviorId %i", sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), dstActionIndex, behaviorId); + LOG_DEBUG("srcstripId %i dststripId %i srcstateId %i dststateId %i dstactionIndex %i behaviorId %i", sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), dstActionIndex, behaviorId); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.h index 0aff7f3a..7fa4d3a8 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MergeStripsMessage.h @@ -1,6 +1,7 @@ #ifndef __MERGESTRIPSMESSAGE__H__ #define __MERGESTRIPSMESSAGE__H__ +#include "Action.h" #include "ActionContext.h" #include "BehaviorMessageBase.h" @@ -13,13 +14,16 @@ class AMFArrayValue; class MergeStripsMessage : public BehaviorMessageBase { public: MergeStripsMessage(AMFArrayValue* arguments); - const uint32_t GetDstActionIndex() { return dstActionIndex; }; - ActionContext GetSourceActionContext() { return sourceActionContext; }; - ActionContext GetDestinationActionContext() { return destinationActionContext; }; + int32_t GetDstActionIndex() const { return dstActionIndex; }; + ActionContext GetSourceActionContext() const { return sourceActionContext; }; + ActionContext GetDestinationActionContext() const { return destinationActionContext; }; + const std::vector& GetMigratedActions() const { return migratedActions; }; + void SetMigratedActions(std::vector::const_iterator start, std::vector::const_iterator end) { migratedActions.assign(start, end); }; private: + std::vector migratedActions; ActionContext sourceActionContext; ActionContext destinationActionContext; - uint32_t dstActionIndex; + int32_t dstActionIndex; }; #endif //!__MERGESTRIPSMESSAGE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.cpp index 08f830a4..4d45429b 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.cpp @@ -7,5 +7,5 @@ MigrateActionsMessage::MigrateActionsMessage(AMFArrayValue* arguments) : Behavio destinationActionContext = ActionContext(arguments, "dstStateID", "dstStripID"); dstActionIndex = GetActionIndexFromArgument(arguments, "dstActionIndex"); - Game::logger->LogDebug("MigrateActionsMessage", "srcactionIndex %i dstactionIndex %i srcstripId %i dststripId %i srcstateId %i dststateId %i behaviorId %i", srcActionIndex, dstActionIndex, sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), behaviorId); + LOG_DEBUG("srcactionIndex %i dstactionIndex %i srcstripId %i dststripId %i srcstateId %i dststateId %i behaviorId %i", srcActionIndex, dstActionIndex, sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), behaviorId); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.h index f60e8748..2f1ac243 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MigrateActionsMessage.h @@ -1,6 +1,7 @@ #ifndef __MIGRATEACTIONSMESSAGE__H__ #define __MIGRATEACTIONSMESSAGE__H__ +#include "Action.h" #include "ActionContext.h" #include "BehaviorMessageBase.h" @@ -13,15 +14,18 @@ class AMFArrayValue; class MigrateActionsMessage : public BehaviorMessageBase { public: MigrateActionsMessage(AMFArrayValue* arguments); - const uint32_t GetSrcActionIndex() { return srcActionIndex; }; - const uint32_t GetDstActionIndex() { return dstActionIndex; }; - ActionContext GetSourceActionContext() { return sourceActionContext; }; - ActionContext GetDestinationActionContext() { return destinationActionContext; }; + int32_t GetSrcActionIndex() const { return srcActionIndex; }; + int32_t GetDstActionIndex() const { return dstActionIndex; }; + ActionContext GetSourceActionContext() const { return sourceActionContext; }; + ActionContext GetDestinationActionContext() const { return destinationActionContext; }; + const std::vector& GetMigratedActions() const { return migratedActions; }; + void SetMigratedActions(std::vector::const_iterator start, std::vector::const_iterator end) { migratedActions.assign(start, end); }; private: + std::vector migratedActions; ActionContext sourceActionContext; ActionContext destinationActionContext; - uint32_t srcActionIndex; - uint32_t dstActionIndex; + int32_t srcActionIndex; + int32_t dstActionIndex; }; #endif //!__MIGRATEACTIONSMESSAGE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.cpp index 5b61ee32..5195e676 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.cpp @@ -5,5 +5,5 @@ MoveToInventoryMessage::MoveToInventoryMessage(AMFArrayValue* arguments) : Behav if (!behaviorIndexValue) return; behaviorIndex = static_cast(behaviorIndexValue->GetValue()); - Game::logger->LogDebug("MoveToInventoryMessage", "behaviorId %i behaviorIndex %i", behaviorId, behaviorIndex); + LOG_DEBUG("behaviorId %i behaviorIndex %i", behaviorId, behaviorIndex); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.h index c48f7d17..dc105766 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/MoveToInventoryMessage.h @@ -13,7 +13,7 @@ class AMFArrayValue; class MoveToInventoryMessage : public BehaviorMessageBase { public: MoveToInventoryMessage(AMFArrayValue* arguments); - const uint32_t GetBehaviorIndex() { return behaviorIndex; }; + const uint32_t GetBehaviorIndex() const { return behaviorIndex; }; private: uint32_t behaviorIndex; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.cpp index 4018a423..d612ae2a 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.cpp @@ -6,5 +6,5 @@ RearrangeStripMessage::RearrangeStripMessage(AMFArrayValue* arguments) : Behavio dstActionIndex = GetActionIndexFromArgument(arguments, "dstActionIndex"); - Game::logger->LogDebug("RearrangeStripMessage", "srcactionIndex %i dstactionIndex %i stripId %i behaviorId %i stateId %i", srcActionIndex, dstActionIndex, actionContext.GetStripId(), behaviorId, actionContext.GetStateId()); + LOG_DEBUG("srcactionIndex %i dstactionIndex %i stripId %i behaviorId %i stateId %i", srcActionIndex, dstActionIndex, actionContext.GetStripId(), behaviorId, actionContext.GetStateId()); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.h index 46819404..db12c046 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RearrangeStripMessage.h @@ -11,13 +11,13 @@ class RearrangeStripMessage : public BehaviorMessageBase { public: RearrangeStripMessage(AMFArrayValue* arguments); - const uint32_t GetSrcActionIndex() { return srcActionIndex; }; - const uint32_t GetDstActionIndex() { return dstActionIndex; }; - ActionContext GetActionContext() { return actionContext; }; + int32_t GetSrcActionIndex() const { return srcActionIndex; }; + int32_t GetDstActionIndex() const { return dstActionIndex; }; + ActionContext GetActionContext() const { return actionContext; }; private: ActionContext actionContext; - uint32_t srcActionIndex; - uint32_t dstActionIndex; + int32_t srcActionIndex; + int32_t dstActionIndex; }; #endif //!__REARRANGESTRIPMESSAGE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.cpp index 8f00d2b0..c6164c6f 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.cpp @@ -4,5 +4,5 @@ RemoveActionsMessage::RemoveActionsMessage(AMFArrayValue* arguments) : BehaviorM actionContext = ActionContext(arguments); actionIndex = GetActionIndexFromArgument(arguments); - Game::logger->LogDebug("RemoveActionsMessage", "behaviorId %i actionIndex %i stripId %i stateId %i", behaviorId, actionIndex, actionContext.GetStripId(), actionContext.GetStateId()); + LOG_DEBUG("behaviorId %i actionIndex %i stripId %i stateId %i", behaviorId, actionIndex, actionContext.GetStripId(), actionContext.GetStateId()); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.h index 457ddba8..860df0af 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveActionsMessage.h @@ -13,11 +13,11 @@ class AMFArrayValue; class RemoveActionsMessage : public BehaviorMessageBase { public: RemoveActionsMessage(AMFArrayValue* arguments); - const uint32_t GetActionIndex() { return actionIndex; }; - ActionContext GetActionContext() { return actionContext; }; + int32_t GetActionIndex() const { return actionIndex; }; + ActionContext GetActionContext() const { return actionContext; }; private: ActionContext actionContext; - uint32_t actionIndex; + int32_t actionIndex; }; #endif //!__REMOVEACTIONSMESSAGE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.cpp index 40288b07..b70beece 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.cpp @@ -3,5 +3,5 @@ RemoveStripMessage::RemoveStripMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arguments) { actionContext = ActionContext(arguments); - Game::logger->LogDebug("RemoveStripMessage", "stripId %i stateId %i behaviorId %i", actionContext.GetStripId(), actionContext.GetStateId(), behaviorId); + LOG_DEBUG("stripId %i stateId %i behaviorId %i", actionContext.GetStripId(), actionContext.GetStateId(), behaviorId); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.h index 36e2e401..6a32ab0c 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RemoveStripMessage.h @@ -11,7 +11,7 @@ class RemoveStripMessage : public BehaviorMessageBase { public: RemoveStripMessage(AMFArrayValue* arguments); - ActionContext GetActionContext() { return actionContext; }; + ActionContext GetActionContext() const { return actionContext; }; private: ActionContext actionContext; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.cpp index a1c2abbc..17365be2 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.cpp @@ -5,5 +5,5 @@ RenameMessage::RenameMessage(AMFArrayValue* arguments) : BehaviorMessageBase(arg if (!nameAmf) return; name = nameAmf->GetValue(); - Game::logger->LogDebug("RenameMessage", "behaviorId %i n %s", behaviorId, name.c_str()); + LOG_DEBUG("behaviorId %i n %s", behaviorId, name.c_str()); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.h index ba181f63..3f4119d2 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/RenameMessage.h @@ -12,7 +12,7 @@ class AMFArrayValue; class RenameMessage : public BehaviorMessageBase { public: RenameMessage(AMFArrayValue* arguments); - const std::string& GetName() { return name; }; + const std::string& GetName() const { return name; }; private: std::string name; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.cpp index b4601a17..45dad737 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.cpp @@ -7,5 +7,5 @@ SplitStripMessage::SplitStripMessage(AMFArrayValue* arguments) : BehaviorMessage destinationActionContext = ActionContext(arguments, "dstStateID", "dstStripID"); destinationPosition = StripUiPosition(arguments, "dstStripUI"); - Game::logger->LogDebug("SplitStripMessage", "behaviorId %i xPosition %f yPosition %f sourceStrip %i destinationStrip %i sourceState %i destinationState %i srcActindex %i", behaviorId, destinationPosition.GetX(), destinationPosition.GetY(), sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), srcActionIndex); + LOG_DEBUG("behaviorId %i xPosition %f yPosition %f sourceStrip %i destinationStrip %i sourceState %i destinationState %i srcActindex %i", behaviorId, destinationPosition.GetX(), destinationPosition.GetY(), sourceActionContext.GetStripId(), destinationActionContext.GetStripId(), sourceActionContext.GetStateId(), destinationActionContext.GetStateId(), srcActionIndex); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.h index 9210efb0..e41d50eb 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/SplitStripMessage.h @@ -1,6 +1,7 @@ #ifndef __SPLITSTRIPMESSAGE__H__ #define __SPLITSTRIPMESSAGE__H__ +#include "Action.h" #include "ActionContext.h" #include "BehaviorMessageBase.h" #include "StripUiPosition.h" @@ -14,15 +15,19 @@ class AMFArrayValue; class SplitStripMessage : public BehaviorMessageBase { public: SplitStripMessage(AMFArrayValue* arguments); - ActionContext GetSourceActionContext() { return sourceActionContext; }; - ActionContext GetDestinationActionContext() { return destinationActionContext; }; - const uint32_t GetSrcActionIndex() { return srcActionIndex; }; - StripUiPosition GetPosition() { return destinationPosition; }; + ActionContext GetSourceActionContext() const { return sourceActionContext; }; + ActionContext GetDestinationActionContext() const { return destinationActionContext; }; + int32_t GetSrcActionIndex() const { return srcActionIndex; }; + StripUiPosition GetPosition() const { return destinationPosition; }; + const std::vector& GetTransferredActions() const { return transferredActions; }; + void SetTransferredActions(std::vector::const_iterator begin, std::vector::const_iterator end) { transferredActions.assign(begin, end); }; private: ActionContext sourceActionContext; ActionContext destinationActionContext; - uint32_t srcActionIndex; + int32_t srcActionIndex; StripUiPosition destinationPosition; + + std::vector transferredActions; }; #endif //!__SPLITSTRIPMESSAGE__H__ diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp index de612b45..8b2d1d36 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp @@ -20,3 +20,9 @@ StripUiPosition::StripUiPosition(AMFArrayValue* arguments, std::string uiKeyName yPosition = yPositionValue->GetValue(); xPosition = xPositionValue->GetValue(); } + +void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const { + auto* uiArgs = args.InsertArray("ui"); + uiArgs->Insert("x", xPosition); + uiArgs->Insert("y", yPosition); +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h index 809f8890..92578cdc 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h @@ -11,8 +11,9 @@ class StripUiPosition { public: StripUiPosition(); StripUiPosition(AMFArrayValue* arguments, std::string uiKeyName = "ui"); - double GetX() { return xPosition; }; - double GetY() { return yPosition; }; + void SendBehaviorBlocksToClient(AMFArrayValue& args) const; + double GetX() const { return xPosition; }; + double GetY() const { return yPosition; }; private: double xPosition; double yPosition; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.cpp index 23a0050d..924a9e62 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.cpp @@ -11,5 +11,5 @@ UpdateActionMessage::UpdateActionMessage(AMFArrayValue* arguments) : BehaviorMes action = Action(actionValue); actionIndex = GetActionIndexFromArgument(arguments); - Game::logger->LogDebug("UpdateActionMessage", "type %s valueParameterName %s valueParameterString %s valueParameterDouble %f behaviorId %i actionIndex %i stripId %i stateId %i", action.GetType().c_str(), action.GetValueParameterName().c_str(), action.GetValueParameterString().c_str(), action.GetValueParameterDouble(), behaviorId, actionIndex, actionContext.GetStripId(), actionContext.GetStateId()); + LOG_DEBUG("type %s valueParameterName %s valueParameterString %s valueParameterDouble %f behaviorId %i actionIndex %i stripId %i stateId %i", action.GetType().c_str(), action.GetValueParameterName().c_str(), action.GetValueParameterString().c_str(), action.GetValueParameterDouble(), behaviorId, actionIndex, actionContext.GetStripId(), actionContext.GetStateId()); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.h index 0a03ce9e..aa34940b 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateActionMessage.h @@ -14,11 +14,11 @@ class AMFArrayValue; class UpdateActionMessage : public BehaviorMessageBase { public: UpdateActionMessage(AMFArrayValue* arguments); - const uint32_t GetActionIndex() { return actionIndex; }; - ActionContext GetActionContext() { return actionContext; }; - Action GetAction() { return action; }; + int32_t GetActionIndex() const { return actionIndex; }; + ActionContext GetActionContext() const { return actionContext; }; + Action GetAction() const { return action; }; private: - uint32_t actionIndex; + int32_t actionIndex; ActionContext actionContext; Action action; }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.cpp index 073db9b4..05dc7cf7 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.cpp @@ -4,5 +4,5 @@ UpdateStripUiMessage::UpdateStripUiMessage(AMFArrayValue* arguments) : BehaviorM position = StripUiPosition(arguments); actionContext = ActionContext(arguments); - Game::logger->LogDebug("UpdateStripUIMessage", "xPosition %f yPosition %f stripId %i stateId %i behaviorId %i", position.GetX(), position.GetY(), actionContext.GetStripId(), actionContext.GetStateId(), behaviorId); + LOG_DEBUG("xPosition %f yPosition %f stripId %i stateId %i behaviorId %i", position.GetX(), position.GetY(), actionContext.GetStripId(), actionContext.GetStateId(), behaviorId); } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.h index 6d96f90c..0e9afe81 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/UpdateStripUiMessage.h @@ -14,8 +14,8 @@ class AMFArrayValue; class UpdateStripUiMessage : public BehaviorMessageBase { public: UpdateStripUiMessage(AMFArrayValue* arguments); - StripUiPosition GetPosition() { return position; }; - ActionContext GetActionContext() { return actionContext; }; + StripUiPosition GetPosition() const { return position; }; + ActionContext GetActionContext() const { return actionContext; }; private: StripUiPosition position; ActionContext actionContext; diff --git a/dGame/dPropertyBehaviors/ControlBehaviors.cpp b/dGame/dPropertyBehaviors/ControlBehaviors.cpp index d8a062ca..5fee358d 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviors.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviors.cpp @@ -5,14 +5,15 @@ #include "Game.h" #include "GameMessages.h" #include "ModelComponent.h" -#include "../../dWorldServer/ObjectIDManager.h" -#include "dLogger.h" +#include "ObjectIDManager.h" +#include "Logger.h" #include "BehaviorStates.h" #include "AssetManager.h" #include "BlockDefinition.h" #include "User.h" #include "tinyxml2.h" #include "CDClientDatabase.h" +#include "CharacterComponent.h" // Message includes #include "Action.h" @@ -30,319 +31,152 @@ #include "UpdateActionMessage.h" #include "UpdateStripUiMessage.h" -void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr) { - // auto behavior = modelComponent->FindBehavior(behaviorID); - // if (behavior->GetBehaviorID() == -1 || behavior->GetShouldSetNewID()) { - // ObjectIDManager::Instance()->RequestPersistentID( - // [behaviorID, behavior, modelComponent, modelOwner, sysAddr](uint32_t persistentId) { - // behavior->SetShouldGetNewID(false); - // behavior->SetIsTemplated(false); - // behavior->SetBehaviorID(persistentId); +void ControlBehaviors::RequestUpdatedID(ControlBehaviorContext& context) { + ObjectIDManager::RequestPersistentID( + [context](uint32_t persistentId) { + if (!context) { + LOG("Model to update behavior ID for is null. Cannot update ID."); + return; + } + // This updates the behavior ID of the behavior should this be a new behavior + AMFArrayValue args; - // // This updates the behavior ID of the behavior should this be a new behavior - // AMFArrayValue args; + args.Insert("behaviorID", std::to_string(persistentId)); + args.Insert("objectID", std::to_string(context.modelComponent->GetParent()->GetObjectID())); - // AMFStringValue* behaviorIDString = new AMFStringValue(); - // behaviorIDString->SetValue(std::to_string(persistentId)); - // args.InsertValue("behaviorID", behaviorIDString); + GameMessages::SendUIMessageServerToSingleClient(context.modelOwner, context.modelOwner->GetSystemAddress(), "UpdateBehaviorID", args); + context.modelComponent->UpdatePendingBehaviorId(persistentId); - // AMFStringValue* objectIDAsString = new AMFStringValue(); - // objectIDAsString->SetValue(std::to_string(modelComponent->GetParent()->GetObjectID())); - // args.InsertValue("objectID", objectIDAsString); - - // GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args); - // ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner); - // }); - // } + ControlBehaviors::Instance().SendBehaviorListToClient(context); + }); } -void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner) { - auto* modelComponent = modelEntity->GetComponent(); - - if (!modelComponent) return; +void ControlBehaviors::SendBehaviorListToClient(const ControlBehaviorContext& context) { + if (!context) return; AMFArrayValue behaviorsToSerialize; + context.modelComponent->SendBehaviorListToClient(behaviorsToSerialize); - /** - * The behaviors AMFArray will have up to 5 elements in the dense portion. - * Each element in the dense portion will be made up of another AMFArray - * with the following information mapped in the associative portion - * "id": Behavior ID cast to an AMFString - * "isLocked": AMFTrue or AMFFalse of whether or not the behavior is locked - * "isLoot": AMFTrue or AMFFalse of whether or not the behavior is a custom behavior (true if custom) - * "name": The name of the behavior formatted as an AMFString - */ - - behaviorsToSerialize.Insert("behaviors"); - behaviorsToSerialize.Insert("objectID", std::to_string(modelComponent->GetParent()->GetObjectID())); - - GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorList", behaviorsToSerialize); -} - -void ControlBehaviors::ModelTypeChanged(AMFArrayValue* arguments, ModelComponent* ModelComponent) { - auto* modelTypeAmf = arguments->Get("ModelType"); - if (!modelTypeAmf) return; - - uint32_t modelType = static_cast(modelTypeAmf->GetValue()); - - //TODO Update the model type here -} - -void ControlBehaviors::ToggleExecutionUpdates() { - //TODO do something with this info -} - -void ControlBehaviors::AddStrip(AMFArrayValue* arguments) { - AddStripMessage addStripMessage(arguments); -} - -void ControlBehaviors::RemoveStrip(AMFArrayValue* arguments) { - RemoveStripMessage removeStrip(arguments); -} - -void ControlBehaviors::MergeStrips(AMFArrayValue* arguments) { - MergeStripsMessage mergeStripsMessage(arguments); -} - -void ControlBehaviors::SplitStrip(AMFArrayValue* arguments) { - SplitStripMessage splitStripMessage(arguments); -} - -void ControlBehaviors::UpdateStripUI(AMFArrayValue* arguments) { - UpdateStripUiMessage updateStripUiMessage(arguments); -} - -void ControlBehaviors::AddAction(AMFArrayValue* arguments) { - AddActionMessage addActionMessage(arguments); -} - -void ControlBehaviors::MigrateActions(AMFArrayValue* arguments) { - MigrateActionsMessage migrateActionsMessage(arguments); -} - -void ControlBehaviors::RearrangeStrip(AMFArrayValue* arguments) { - RearrangeStripMessage rearrangeStripMessage(arguments); -} - -void ControlBehaviors::Add(AMFArrayValue* arguments) { - AddMessage addMessage(arguments); -} - -void ControlBehaviors::RemoveActions(AMFArrayValue* arguments) { - RemoveActionsMessage removeActionsMessage(arguments); -} - -void ControlBehaviors::Rename(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { - RenameMessage renameMessage(arguments); + GameMessages::SendUIMessageServerToSingleClient(context.modelOwner, context.modelOwner->GetSystemAddress(), "UpdateBehaviorList", behaviorsToSerialize); } // TODO This is also supposed to serialize the state of the behaviors in progress but those aren't implemented yet -void ControlBehaviors::SendBehaviorBlocksToClient(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { - // uint32_t behaviorID = ControlBehaviors::GetBehaviorIDFromArgument(arguments); +void ControlBehaviors::SendBehaviorBlocksToClient(ControlBehaviorContext& context) { + if (!context) return; + BehaviorMessageBase behaviorMsg(context.arguments); - // auto modelBehavior = modelComponent->FindBehavior(behaviorID); - - // if (!modelBehavior) return; - - // modelBehavior->VerifyStates(); - - // auto states = modelBehavior->GetBehaviorStates(); - - // // Begin serialization. - - // /** - // * for each state - // * strip id - // * ui info - // * x - // * y - // * actions - // * action1 - // * action2 - // * ... - // * behaviorID of strip - // * objectID of strip - // */ - // LWOOBJID targetObjectID = LWOOBJID_EMPTY; - // behaviorID = 0; - // AMFArrayValue behaviorInfo; - - // AMFArrayValue* stateSerialize = new AMFArrayValue(); - - // for (auto it = states.begin(); it != states.end(); it++) { - // Game::logger->Log("PropertyBehaviors", "Begin serialization of state %i!\n", it->first); - // AMFArrayValue* state = new AMFArrayValue(); - - // AMFDoubleValue* stateAsDouble = new AMFDoubleValue(); - // stateAsDouble->SetValue(it->first); - // state->InsertValue("id", stateAsDouble); - - // AMFArrayValue* strips = new AMFArrayValue(); - // auto stripsInState = it->second->GetStrips(); - // for (auto strip = stripsInState.begin(); strip != stripsInState.end(); strip++) { - // Game::logger->Log("PropertyBehaviors", "Begin serialization of strip %i!\n", strip->first); - // AMFArrayValue* thisStrip = new AMFArrayValue(); - - // AMFDoubleValue* stripID = new AMFDoubleValue(); - // stripID->SetValue(strip->first); - // thisStrip->InsertValue("id", stripID); - - // AMFArrayValue* uiArray = new AMFArrayValue(); - // AMFDoubleValue* yPosition = new AMFDoubleValue(); - // yPosition->SetValue(strip->second->GetYPosition()); - // uiArray->InsertValue("y", yPosition); - - // AMFDoubleValue* xPosition = new AMFDoubleValue(); - // xPosition->SetValue(strip->second->GetXPosition()); - // uiArray->InsertValue("x", xPosition); - - // thisStrip->InsertValue("ui", uiArray); - // targetObjectID = modelComponent->GetParent()->GetObjectID(); - // behaviorID = modelBehavior->GetBehaviorID(); - - // AMFArrayValue* stripSerialize = new AMFArrayValue(); - // for (auto behaviorAction : strip->second->GetActions()) { - // Game::logger->Log("PropertyBehaviors", "Begin serialization of action %s!\n", behaviorAction->actionName.c_str()); - // AMFArrayValue* thisAction = new AMFArrayValue(); - - // AMFStringValue* actionName = new AMFStringValue(); - // actionName->SetValue(behaviorAction->actionName); - // thisAction->InsertValue("Type", actionName); - - // if (behaviorAction->parameterValueString != "") - // { - // AMFStringValue* valueAsString = new AMFStringValue(); - // valueAsString->SetValue(behaviorAction->parameterValueString); - // thisAction->InsertValue(behaviorAction->parameterName, valueAsString); - // } - // else if (behaviorAction->parameterValueDouble != 0.0) - // { - // AMFDoubleValue* valueAsDouble = new AMFDoubleValue(); - // valueAsDouble->SetValue(behaviorAction->parameterValueDouble); - // thisAction->InsertValue(behaviorAction->parameterName, valueAsDouble); - // } - // stripSerialize->PushBackValue(thisAction); - // } - // thisStrip->InsertValue("actions", stripSerialize); - // strips->PushBackValue(thisStrip); - // } - // state->InsertValue("strips", strips); - // stateSerialize->PushBackValue(state); - // } - // behaviorInfo.InsertValue("states", stateSerialize); - - // AMFStringValue* objectidAsString = new AMFStringValue(); - // objectidAsString->SetValue(std::to_string(targetObjectID)); - // behaviorInfo.InsertValue("objectID", objectidAsString); - - // AMFStringValue* behaviorIDAsString = new AMFStringValue(); - // behaviorIDAsString->SetValue(std::to_string(behaviorID)); - // behaviorInfo.InsertValue("BehaviorID", behaviorIDAsString); - - // GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorBlocks", &behaviorInfo); + context.modelComponent->VerifyBehaviors(); + AMFArrayValue behavior; + context.modelComponent->SendBehaviorBlocksToClient(behaviorMsg.GetBehaviorId(), behavior); + GameMessages::SendUIMessageServerToSingleClient(context.modelOwner, context.modelOwner->GetSystemAddress(), "UpdateBehaviorBlocks", behavior); } void ControlBehaviors::UpdateAction(AMFArrayValue* arguments) { UpdateActionMessage updateActionMessage(arguments); - auto* blockDefinition = GetBlockInfo(updateActionMessage.GetAction().GetType()); + auto blockDefinition = GetBlockInfo(updateActionMessage.GetAction().GetType()); if (!blockDefinition) { - Game::logger->Log("ControlBehaviors", "Received undefined block type %s. Ignoring.", updateActionMessage.GetAction().GetType().c_str()); + LOG("Received undefined block type %s. Ignoring.", updateActionMessage.GetAction().GetType().c_str()); return; } if (updateActionMessage.GetAction().GetValueParameterString().size() > 0) { if (updateActionMessage.GetAction().GetValueParameterString().size() < blockDefinition->GetMinimumValue() || updateActionMessage.GetAction().GetValueParameterString().size() > blockDefinition->GetMaximumValue()) { - Game::logger->Log("ControlBehaviors", "Updated block %s is out of range. Ignoring update", updateActionMessage.GetAction().GetType().c_str()); + LOG("Updated block %s is out of range. Ignoring update", updateActionMessage.GetAction().GetType().c_str()); return; } } else { if (updateActionMessage.GetAction().GetValueParameterDouble() < blockDefinition->GetMinimumValue() || updateActionMessage.GetAction().GetValueParameterDouble() > blockDefinition->GetMaximumValue()) { - Game::logger->Log("ControlBehaviors", "Updated block %s is out of range. Ignoring update", updateActionMessage.GetAction().GetType().c_str()); + LOG("Updated block %s is out of range. Ignoring update", updateActionMessage.GetAction().GetType().c_str()); return; } } } -void ControlBehaviors::MoveToInventory(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments) { - // This closes the UI menu should it be open while the player is removing behaviors - AMFArrayValue args; - - args.Insert("visible", false); - - GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->GetSystemAddress(), "ToggleBehaviorEditor", args); - - MoveToInventoryMessage moveToInventoryMessage(arguments); - - SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner); -} - void ControlBehaviors::ProcessCommand(Entity* modelEntity, const SystemAddress& sysAddr, AMFArrayValue* arguments, std::string command, Entity* modelOwner) { if (!isInitialized || !modelEntity || !modelOwner || !arguments) return; auto* modelComponent = modelEntity->GetComponent(); if (!modelComponent) return; - if (command == "sendBehaviorListToClient") - SendBehaviorListToClient(modelEntity, sysAddr, modelOwner); - else if (command == "modelTypeChanged") - ModelTypeChanged(arguments, modelComponent); - else if (command == "toggleExecutionUpdates") - ToggleExecutionUpdates(); - else if (command == "addStrip") - AddStrip(arguments); - else if (command == "removeStrip") - RemoveStrip(arguments); - else if (command == "mergeStrips") - MergeStrips(arguments); - else if (command == "splitStrip") - SplitStrip(arguments); - else if (command == "updateStripUI") - UpdateStripUI(arguments); - else if (command == "addAction") - AddAction(arguments); - else if (command == "migrateActions") - MigrateActions(arguments); - else if (command == "rearrangeStrip") - RearrangeStrip(arguments); - else if (command == "add") - Add(arguments); - else if (command == "removeActions") - RemoveActions(arguments); - else if (command == "rename") - Rename(modelEntity, sysAddr, modelOwner, arguments); - else if (command == "sendBehaviorBlocksToClient") - SendBehaviorBlocksToClient(modelComponent, sysAddr, modelOwner, arguments); - else if (command == "moveToInventory") - MoveToInventory(modelComponent, sysAddr, modelOwner, arguments); - else if (command == "updateAction") - UpdateAction(arguments); - else - Game::logger->Log("ControlBehaviors", "Unknown behavior command (%s)\n", command.c_str()); + ControlBehaviorContext context(arguments, modelComponent, modelOwner); + + if (command == "sendBehaviorListToClient") { + SendBehaviorListToClient(context); + } else if (command == "modelTypeChanged") { + auto* modelType = arguments->Get("ModelType"); + if (!modelType) return; + + modelEntity->SetVar(u"modelType", modelType->GetValue()); + } else if (command == "toggleExecutionUpdates") { + // TODO + } else if (command == "addStrip") { + if (BehaviorMessageBase(context.arguments).IsDefaultBehaviorId()) RequestUpdatedID(context); + + context.modelComponent->HandleControlBehaviorsMsg(context.arguments); + } else if (command == "removeStrip") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "mergeStrips") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "splitStrip") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "updateStripUI") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "addAction") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "migrateActions") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "rearrangeStrip") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "add") { + AddMessage msg(context.arguments); + context.modelComponent->AddBehavior(msg); + SendBehaviorListToClient(context); + } else if (command == "removeActions") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else if (command == "rename") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + + // Send the list back to the client so the name is updated. + SendBehaviorListToClient(context); + } else if (command == "sendBehaviorBlocksToClient") { + SendBehaviorBlocksToClient(context); + } else if (command == "moveToInventory") { + MoveToInventoryMessage msg(arguments); + context.modelComponent->MoveToInventory(msg); + auto* characterComponent = modelOwner->GetComponent(); + if (!characterComponent) return; + + AMFArrayValue args; + args.Insert("BehaviorID", std::to_string(msg.GetBehaviorId())); + GameMessages::SendUIMessageServerToSingleClient(modelOwner, characterComponent->GetSystemAddress(), "BehaviorRemoved", args); + + SendBehaviorListToClient(context); + } else if (command == "updateAction") { + context.modelComponent->HandleControlBehaviorsMsg(arguments); + } else { + LOG("Unknown behavior command (%s)", command.c_str()); + } } ControlBehaviors::ControlBehaviors() { - auto blocksDefStreamBuffer = Game::assetManager->GetFileAsBuffer("ui\\ingame\\blocksdef.xml"); - if (!blocksDefStreamBuffer.m_Success) { - Game::logger->Log("ControlBehaviors", "failed to open blocksdef"); - return; - } - std::istream blocksBuffer(&blocksDefStreamBuffer); - if (!blocksBuffer.good()) { - Game::logger->Log("ControlBehaviors", "Blocks buffer is not good!"); + auto blocksBuffer = Game::assetManager->GetFile("ui\\ingame\\blocksdef.xml"); + if (!blocksBuffer) { + LOG("Failed to open blocksdef.xml, property behaviors will be disabled for this zone! " + "(This is a necessary file for cheat detection and ensuring we do not send unexpected values to the client)"); return; } tinyxml2::XMLDocument m_Doc; - std::string read{}; + std::string read; - std::string buffer{}; + std::string buffer; bool commentBlockStart = false; while (std::getline(blocksBuffer, read)) { - // tinyxml2 should handle comment blocks but the client has one that fails the processing. + // tinyxml2 should handle comment blocks but the client has one that fails the processing. // This preprocessing just removes all comments from the read file out of an abundance of caution. if (read.find("