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/.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 45c7645a..52524045 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.18) project(Darkflame) include(CTest) -set (CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") # Read variables from file FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) @@ -14,30 +15,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 +50,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 +98,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,24 +163,23 @@ foreach (resource_file ${RESOURCE_FILES}) endforeach() endif() endforeach() + message(STATUS "Resource file integrity check complete") # if navmeshes directory does not exist, create it -if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes) +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 -) +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() @@ -171,26 +187,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 @@ -198,7 +206,9 @@ set(INCLUDED_DIRECTORIES "dCommon" "dCommon/dClient" "dCommon/dEnums" + "dChatFilter" + "dGame" "dGame/dBehaviors" "dGame/dComponents" @@ -210,116 +220,54 @@ 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" "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}) @@ -330,8 +278,9 @@ add_subdirectory(thirdparty) 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 ) @@ -366,15 +315,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") # 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() @@ -385,12 +335,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} @@ -412,6 +356,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/CMakeVariables.txt b/CMakeVariables.txt index abfe9e15..d9430d9d 100644 --- a/CMakeVariables.txt +++ b/CMakeVariables.txt @@ -1,22 +1,32 @@ PROJECT_VERSION_MAJOR=1 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/ -# Uncomment the below line to cache the entire CDClient into memory -# CDCLIENT_CACHE_ALL=1 + +# 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 310e75eb..1d998511 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) @@ -344,6 +348,62 @@ 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 + +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 a18657ca..5593f0e1 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -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 { Logger* logger = nullptr; dServer* server = nullptr; dConfig* config = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; } -Logger* SetupLogger(); void HandlePacket(Packet* packet); int main(int argc, char** argv) { @@ -42,27 +45,21 @@ 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"); - LOG("Starting Auth server..."); - LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); + LOG("Version: %s", PROJECT_VERSION); LOG("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"); - try { - Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); + Database::Connect(); } catch (sql::SQLException& ex) { LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("AuthServer"); @@ -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 maxClients = 999; 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"; + GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients); + GeneralUtils::TryParse(Game::config->GetValue("auth_server_port"), ourPort); + 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::Auth, Game::config, &Game::shouldShutdown); + 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; } -Logger* 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 Logger(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 8d2015b0..6e81db3b 100644 --- a/dChatFilter/dChatFilter.cpp +++ b/dChatFilter/dChatFilter.cpp @@ -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() { 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 72383c38..5e2e58d7 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -2,7 +2,6 @@ #include "PlayerContainer.h" #include "Database.h" #include -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "Game.h" #include "dServer.h" @@ -18,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: @@ -27,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); @@ -71,7 +55,7 @@ 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: @@ -83,107 +67,94 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { 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); + 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; } - if (requestor->playerName == playerName) { - SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN); + // 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 @@ -204,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{}; @@ -283,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; @@ -296,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); @@ -328,204 +296,210 @@ 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); - - LOG("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; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(otherMember->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(otherMember->playerID); - bitStream.Write(8); - bitStream.Write(69); - bitStream.Write(LUWString(senderName)); - bitStream.Write(sender->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(otherName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //teams? - bitStream.Write(LUWString(message, 512)); - - SystemAddress sysAddr = otherMember->sysAddr; - SEND_PACKET; - } -} - -void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { - LWOOBJID senderID = PacketUtils::ReadS64(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; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonA->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(0); //success - bitStream.Write(LUWString(message, 512)); - - SystemAddress sysAddr = goonA->sysAddr; - SEND_PACKET; - } - - //To the receiver: - { - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(goonB->playerID); - - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); - bitStream.Write(goonA->playerID); - bitStream.Write(7); - bitStream.Write(69); - bitStream.Write(LUWString(goonAName)); - bitStream.Write(goonA->playerID); - bitStream.Write(0); - bitStream.Write(0); //not mythran nametag - bitStream.Write(LUWString(goonBName)); - bitStream.Write(0); //not mythran for receiver - bitStream.Write(3); //new whisper - bitStream.Write(LUWString(message, 512)); - - 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; } @@ -538,7 +512,7 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) { SendTeamInvite(other, player); - LOG("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) { @@ -558,12 +532,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { return; } - auto* team = playerContainer.GetTeam(leaderID); + auto* team = Game::playerContainer.GetTeam(leaderID); if (team == nullptr) { LOG("Failed to find team for leader (%llu)", leaderID); - team = playerContainer.GetTeam(playerID); + team = Game::playerContainer.GetTeam(playerID); } if (team == nullptr) { @@ -571,7 +545,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) { return; } - playerContainer.AddMember(team, playerID); + Game::playerContainer.AddMember(team, playerID); } void ChatPacketHandler::HandleTeamLeave(Packet* packet) { @@ -581,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); 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); - LOG("(%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()); - LOG("(%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); } } @@ -652,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); } } @@ -670,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; } @@ -692,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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE); - bitStream.Write(LUWString(sender->playerName.c_str())); - 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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); @@ -746,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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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); @@ -771,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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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); @@ -843,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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + 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 @@ -889,38 +870,36 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(friendData->playerID); + bitStream.Write(friendData.playerID); //portion that will get routed: 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(); 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. } @@ -928,54 +907,50 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST); - bitStream.Write(LUWString(sender->playerName.c_str())); + 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); // Portion that will get routed: 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 - bitStream.Write(LUWString(sender->playerName.c_str())); + 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; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); - bitStream.Write(receiver->playerID); + bitStream.Write(receiver.playerID); //portion that will get routed: BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE); bitStream.Write(isSuccessful); //isOnline 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 6fb54594..d04cbd01 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -19,12 +19,15 @@ #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 { Logger* logger = nullptr; @@ -32,16 +35,13 @@ namespace Game { dConfig* config = nullptr; dChatFilter* chatFilter = nullptr; AssetManager* assetManager = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; + PlayerContainer playerContainer; } - -Logger* SetupLogger(); void HandlePacket(Packet* packet); -PlayerContainer playerContainer; - int main(int argc, char** argv) { constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFrameDelta = mediumFrameDelta; @@ -49,17 +49,19 @@ 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"); LOG("Starting Chat server..."); - LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); + LOG("Version: %s", PROJECT_VERSION); LOG("Compiled on: %s", __TIMESTAMP__); try { @@ -78,13 +80,8 @@ int main(int argc, char** argv) { } //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) { LOG("Got an error while connecting to the database: %s", ex.what()); Database::Destroy("ChatServer"); @@ -96,28 +93,30 @@ 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 maxClients = 999; 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"; + GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients); + GeneralUtils::TryParse(Game::config->GetValue("chat_server_port"), ourPort); + 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")))); + bool dontGenerateDCF = false; + GeneralUtils::TryParse(Game::config->GetValue("dont_generate_dcf"), dontGenerateDCF); + 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 +126,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 +158,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,18 +182,6 @@ int main(int argc, char** argv) { return EXIT_SUCCESS; } -Logger* 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 Logger(logPath, logToConsole, logDebugStatements); -} - void HandlePacket(Packet* packet) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { LOG("A server has disconnected, erasing their connected players from the list."); @@ -211,19 +196,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: { @@ -239,13 +224,22 @@ void HandlePacket(Packet* packet) { } 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: - LOG("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,9 +295,61 @@ 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: - LOG("Unknown CHAT id: %i", int(packet->data[3])); + LOG("Unknown CHAT Message id: %i", chat_message_type); } } diff --git a/dChatServer/PlayerContainer.cpp b/dChatServer/PlayerContainer.cpp index f467cfbe..969f5c2e 100644 --- a/dChatServer/PlayerContainer.cpp +++ b/dChatServer/PlayerContainer.cpp @@ -10,14 +10,21 @@ #include "Database.h" #include "eConnectionType.h" #include "eChatInternalMessageType.h" +#include "eGameMasterLevel.h" #include "ChatPackets.h" #include "dConfig.h" -PlayerContainer::PlayerContainer() { +void PlayerContainer::Initialize() { + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends); + GeneralUtils::TryParse(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends); } PlayerContainer::~PlayerContainer() { - mPlayers.clear(); + m_Players.clear(); +} + +PlayerData::PlayerData() { + gmLevel == eGameMasterLevel::CIVILIAN; } TeamData::TeamData() { @@ -26,34 +33,33 @@ TeamData::TeamData() { 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)); - LOG("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) { @@ -62,42 +68,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 }); } } LOG("Removed user: %llu", playerID); - mPlayers.erase(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) { @@ -107,15 +107,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) { + if (!player) { LOG("Failed to find user: %llu", playerID); return; } - player->muteExpire = expire; + player.muteExpire = expire; BroadcastMuteUpdate(playerID, expire); } @@ -191,7 +191,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; @@ -213,11 +213,11 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) { } void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { - if (team->memberIDs.size() >= 4){ + if (team->memberIDs.size() >= 4) { LOG("Tried to add player to team that already had 4 players"); - auto* player = GetPlayerData(playerID); + 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!"); + ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!"); return; } @@ -227,18 +227,18 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { 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); } @@ -246,16 +246,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); } } } @@ -265,9 +265,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); } @@ -278,9 +278,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); } @@ -302,9 +302,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); } @@ -316,14 +316,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); @@ -338,19 +338,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); } } @@ -366,7 +366,7 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool 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); } @@ -376,23 +376,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 a0a40058..f34b1e54 100644 --- a/dChatServer/PlayerContainer.h +++ b/dChatServer/PlayerContainer.h @@ -7,14 +7,46 @@ #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 { + 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; + bool isFTP = false; }; struct TeamData { @@ -29,31 +61,19 @@ 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); @@ -66,14 +86,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/AmfSerialize.cpp b/dCommon/AmfSerialize.cpp index 08f1eaad..3072b8e1 100644 --- a/dCommon/AmfSerialize.cpp +++ b/dCommon/AmfSerialize.cpp @@ -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 d9b589c1..b771c986 100644 --- a/dCommon/BrickByBrickFix.cpp +++ b/dCommon/BrickByBrickFix.cpp @@ -13,9 +13,8 @@ //! 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,10 +51,10 @@ 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 { - LOG("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++; @@ -75,26 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() { "", completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos ) { - LOG("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 { - LOG("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; } @@ -106,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; @@ -128,34 +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(); - LOG("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) { LOG("Failed to update model %i. This model should be inspected manually to see why." - "The database error is %s", modelId, exception.what()); + "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* * @@ -171,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 1354c051..5300a4f2 100644 --- a/dCommon/CMakeLists.txt +++ b/dCommon/CMakeLists.txt @@ -5,6 +5,7 @@ set(DCOMMON_SOURCES "dConfig.cpp" "Diagnostics.cpp" "Logger.cpp" + "Game.cpp" "GeneralUtils.cpp" "LDFFormat.cpp" "MD5.cpp" diff --git a/dCommon/Diagnostics.cpp b/dCommon/Diagnostics.cpp index 264744f9..46c17e43 100644 --- a/dCommon/Diagnostics.cpp +++ b/dCommon/Diagnostics.cpp @@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) { #include #include -#if defined(__include_backtrace__) +#if defined(INCLUDE_BACKTRACE) #include #include @@ -115,7 +115,14 @@ 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"; LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str()); @@ -167,7 +174,7 @@ void CatchUnhandled(int sig) { backtrace_symbols_fd(array, size, STDOUT_FILENO); # endif // defined(__GNUG__) -#else // __include_backtrace__ +#else // INCLUDE_BACKTRACE struct backtrace_state* state = backtrace_create_state( Diagnostics::GetProcessFileName().c_str(), @@ -178,7 +185,7 @@ void CatchUnhandled(int sig) { struct bt_ctx ctx = { state, 0 }; Bt(state); -#endif // __include_backtrace__ +#endif // INCLUDE_BACKTRACE exit(EXIT_FAILURE); } @@ -197,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 0b2c546c..d8409cd5 100644 --- a/dCommon/FdbToSqlite.cpp +++ b/dCommon/FdbToSqlite.cpp @@ -28,19 +28,17 @@ 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) { 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 79a271ec..d8113497 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include class dServer; class Logger; @@ -12,8 +14,10 @@ class AssetManager; struct SystemAddress; class EntityManager; class dZoneManager; +class PlayerContainer; namespace Game { + using signal_t = volatile std::sig_atomic_t; extern Logger* logger; extern dServer* server; extern InstanceManager* im; @@ -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..b45165fa 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 diff --git a/dCommon/GeneralUtils.h b/dCommon/GeneralUtils.h index 7acb15b8..37291ab8 100644 --- a/dCommon/GeneralUtils.h +++ b/dCommon/GeneralUtils.h @@ -9,7 +9,7 @@ #include #include #include -#include +#include "BitStream.h" #include "NiPoint3.h" #include "Game.h" @@ -151,6 +151,11 @@ namespace GeneralUtils { return std::stod(value); } + template <> + inline uint16_t Parse(const char* value) { + return std::stoul(value); + } + template <> inline uint32_t Parse(const char* value) { return std::stoul(value); @@ -229,18 +234,30 @@ 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 + inline constexpr typename std::underlying_type_t CastUnderlyingType(const eType entry) { + static_assert(std::is_enum_v, "Not an enum"); + + 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.h b/dCommon/LDFFormat.h index 0921d04c..7bcc91a2 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); } @@ -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 index 83a97aa6..1888f1eb 100644 --- a/dCommon/Logger.cpp +++ b/dCommon/Logger.cpp @@ -6,6 +6,8 @@ #include Writer::~Writer() { + // Flush before we close + Flush(); // Dont try to close stdcout... if (!m_Outfile || m_IsConsoleWriter) return; @@ -14,7 +16,7 @@ Writer::~Writer() { } void Writer::Log(const char* time, const char* message) { - if (!m_Outfile) return; + if (!m_Outfile || !m_Enabled) return; fputs(time, m_Outfile); fputs(message, m_Outfile); 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 5ad06f30..3394deb7 100644 --- a/dCommon/NiPoint3.cpp +++ b/dCommon/NiPoint3.cpp @@ -114,13 +114,13 @@ bool NiPoint3::operator!=(const NiPoint3& point) const { //! Operator for subscripting float& NiPoint3::operator[](int i) { float* base = &x; - return (float&)base[i]; + return base[i]; } //! Operator for subscripting const float& NiPoint3::operator[](int i) const { const float* base = &x; - return (float&)base[i]; + return base[i]; } //! Operator for addition of vectors @@ -181,7 +181,7 @@ bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& if (this->y < minPoint.y) return false; if (this->y > maxPoint.y) return false; - return (this->z < maxPoint.z&& this->z > minPoint.z); + return (this->z < maxPoint.z && this->z > minPoint.z); } //! Checks to see if the point (or vector) is within a sphere @@ -232,10 +232,21 @@ NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, 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); } diff --git a/dCommon/PositionUpdate.h b/dCommon/PositionUpdate.h new file mode 100644 index 00000000..1b84e371 --- /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 = NiPoint3::ZERO; + NiPoint3 linearVelocity = NiPoint3::ZERO; +}; + +struct PositionUpdate { + NiPoint3 position = NiPoint3::ZERO; + NiQuaternion rotation = NiQuaternion::IDENTITY; + bool onGround = false; + bool onRail = false; + NiPoint3 velocity = NiPoint3::ZERO; + NiPoint3 angularVelocity = NiPoint3::ZERO; + LocalSpaceInfo localSpaceInfo; + RemoteInputInfo remoteInputInfo; +}; + +#endif //!__POSITIONUPDATE__H__ diff --git a/dCommon/SHA512.cpp b/dCommon/SHA512.cpp index f5a71d99..e3c2d9f7 100644 --- a/dCommon/SHA512.cpp +++ b/dCommon/SHA512.cpp @@ -1,3 +1,5 @@ +// Source: http://www.zedwood.com/article/cpp-sha512-function + #include "SHA512.h" #include 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 c672b335..59427ee5 100644 --- a/dCommon/dClient/AssetManager.cpp +++ b/dCommon/dClient/AssetManager.cpp @@ -4,7 +4,7 @@ #include "Game.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 d2543489..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() { - free(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/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/PackIndex.cpp b/dCommon/dClient/PackIndex.cpp index 55c26b48..0d7a7fe9 100644 --- a/dCommon/dClient/PackIndex.cpp +++ b/dCommon/dClient/PackIndex.cpp @@ -8,21 +8,10 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) { 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); diff --git a/dCommon/dConfig.cpp b/dCommon/dConfig.cpp index 195d5a5f..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,23 +49,20 @@ void dConfig::ReloadConfig() { } const std::string& dConfig::GetValue(std::string key) { - static std::string emptyString{}; - - const auto& it = this->m_ConfigValues.find(key); - - if (it == this->m_ConfigValues.end()) return emptyString; - - return it->second; + 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..16eafdca --- /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 dc7a1d54..f4f8fdfb 100644 --- a/dCommon/dEnums/dCommonVars.h +++ b/dCommon/dEnums/dCommonVars.h @@ -130,7 +130,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); @@ -148,11 +148,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 df822c52..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 = 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, + 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/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 a0be9dd4..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, 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/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 92% rename from dDatabase/CDClientDatabase.h rename to dDatabase/CDClientDatabase/CDClientDatabase.h index 7f42918d..fa58906c 100644 --- a/dDatabase/CDClientDatabase.h +++ b/dDatabase/CDClientDatabase/CDClientDatabase.h @@ -15,6 +15,10 @@ //! 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/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp similarity index 85% rename from dDatabase/CDClientManager.cpp rename to dDatabase/CDClientDatabase/CDClientManager.cpp index 465fd4e6..f270d849 100644 --- a/dDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -3,6 +3,7 @@ #include "CDAnimationsTable.h" #include "CDBehaviorParameterTable.h" #include "CDBehaviorTemplateTable.h" +#include "CDClientDatabase.h" #include "CDComponentsRegistryTable.h" #include "CDCurrencyTableTable.h" #include "CDDestructibleComponentTable.h" @@ -37,10 +38,15 @@ #include "CDPropertyTemplateTable.h" #include "CDFeatureGatingTable.h" #include "CDRailActivatorComponent.h" +#include "CDRewardCodesTable.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 +// # define CDCLIENT_CACHE_ALL +#endif // CDCLIENT_CACHE_ALL #ifdef CDCLIENT_CACHE_ALL #define CDCLIENT_DONT_CACHE_TABLE(x) x @@ -48,7 +54,16 @@ #define CDCLIENT_DONT_CACHE_TABLE(x) #endif -CDClientManager::CDClientManager() { +class CDClientConnectionException : public std::exception { +public: + virtual const char* what() const throw() { + return "CDClientDatabase is not connected!"; + } +}; + +void CDClientManager::LoadValuesFromDatabase() { + if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); + CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); CDActivitiesTable::Instance().LoadValuesFromDatabase(); CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase()); @@ -76,15 +91,23 @@ CDClientManager::CDClientManager() { 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/CDClientManager.h b/dDatabase/CDClientDatabase/CDClientManager.h similarity index 81% rename from dDatabase/CDClientManager.h rename to dDatabase/CDClientDatabase/CDClientManager.h index 74069ff4..ae628a36 100644 --- a/dDatabase/CDClientManager.h +++ b/dDatabase/CDClientDatabase/CDClientManager.h @@ -11,7 +11,10 @@ */ class CDClientManager : public Singleton { public: - CDClientManager(); + CDClientManager() = default; + + void LoadValuesFromDatabase(); + void LoadValuesFromDefaults(); /** * Fetch a table from CDClient diff --git a/dDatabase/Tables/CDActivitiesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp similarity index 98% rename from dDatabase/Tables/CDActivitiesTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp index c967f724..998c4095 100644 --- a/dDatabase/Tables/CDActivitiesTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.cpp @@ -2,7 +2,7 @@ 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); diff --git a/dDatabase/Tables/CDActivitiesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h similarity index 62% rename from dDatabase/Tables/CDActivitiesTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h index 2e39d595..75fc602e 100644 --- a/dDatabase/Tables/CDActivitiesTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivitiesTable.h @@ -4,22 +4,22 @@ #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; + 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; - unsigned int leaderboardType; + uint32_t leaderboardType; bool localize; - int optionalCostLOT; - int optionalCostCount; + int32_t optionalCostLOT; + int32_t optionalCostCount; bool showUIRewards; - unsigned int CommunityActivityFlagID; + uint32_t CommunityActivityFlagID; std::string gate_version; bool noTeamLootOnDeath; float optionalPercentage; diff --git a/dDatabase/Tables/CDActivityRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp similarity index 98% rename from dDatabase/Tables/CDActivityRewardsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp index a2434d19..7795a177 100644 --- a/dDatabase/Tables/CDActivityRewardsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDActivityRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h similarity index 56% rename from dDatabase/Tables/CDActivityRewardsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h index a177a3c0..40ab17e7 100644 --- a/dDatabase/Tables/CDActivityRewardsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDActivityRewardsTable.h @@ -4,12 +4,12 @@ #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 + 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 }; diff --git a/dDatabase/Tables/CDAnimationsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp similarity index 100% rename from dDatabase/Tables/CDAnimationsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.cpp diff --git a/dDatabase/Tables/CDAnimationsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h similarity index 92% rename from dDatabase/Tables/CDAnimationsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h index 494d5cde..1b6280ca 100644 --- a/dDatabase/Tables/CDAnimationsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDAnimationsTable.h @@ -4,13 +4,13 @@ #include 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 diff --git a/dDatabase/Tables/CDBehaviorParameterTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp similarity index 100% rename from dDatabase/Tables/CDBehaviorParameterTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.cpp diff --git a/dDatabase/Tables/CDBehaviorParameterTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h similarity index 100% rename from dDatabase/Tables/CDBehaviorParameterTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorParameterTable.h diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp similarity index 98% rename from dDatabase/Tables/CDBehaviorTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp index c343ef85..a67398a9 100644 --- a/dDatabase/Tables/CDBehaviorTemplateTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h similarity index 74% rename from dDatabase/Tables/CDBehaviorTemplateTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h index 49ce11f2..cbc494a2 100644 --- a/dDatabase/Tables/CDBehaviorTemplateTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBehaviorTemplateTable.h @@ -6,9 +6,9 @@ #include struct CDBehaviorTemplate { - unsigned int behaviorID; //!< The Behavior ID - unsigned int templateID; //!< The Template ID (LOT) - unsigned int effectID; //!< The Effect ID attached + 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 }; diff --git a/dDatabase/Tables/CDBrickIDTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp similarity index 97% rename from dDatabase/Tables/CDBrickIDTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp index 9be75f0b..abc917b5 100644 --- a/dDatabase/Tables/CDBrickIDTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDBrickIDTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h similarity index 91% rename from dDatabase/Tables/CDBrickIDTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h index 68c0b1b6..1a9f9a94 100644 --- a/dDatabase/Tables/CDBrickIDTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDBrickIDTableTable.h @@ -10,8 +10,8 @@ //! BrickIDTable Entry Struct struct CDBrickIDTable { - unsigned int NDObjectID; - unsigned int LEGOBrickID; + uint32_t NDObjectID; + uint32_t LEGOBrickID; }; diff --git a/dDatabase/Tables/CDComponentsRegistryTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp similarity index 75% rename from dDatabase/Tables/CDComponentsRegistryTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp index ac547a24..3cf7ac62 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.cpp @@ -10,7 +10,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() { 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); + this->mappedEntries.insert_or_assign(static_cast(entry.component_type) << 32 | static_cast(entry.id), entry.component_id); this->mappedEntries.insert_or_assign(entry.id, 0); tableData.nextRow(); @@ -22,7 +22,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() { int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) { auto exists = mappedEntries.find(id); if (exists != mappedEntries.end()) { - auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id)); + auto iter = mappedEntries.find(static_cast(componentType) << 32 | static_cast(id)); return iter == mappedEntries.end() ? defaultValue : iter->second; } @@ -38,15 +38,14 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent 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); + this->mappedEntries.insert_or_assign(static_cast(entry.component_type) << 32 | static_cast(entry.id), entry.component_id); tableData.nextRow(); } mappedEntries.insert_or_assign(id, 0); - auto iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id)); + auto iter = this->mappedEntries.find(static_cast(componentType) << 32 | static_cast(id)); return iter == this->mappedEntries.end() ? defaultValue : iter->second; } - diff --git a/dDatabase/Tables/CDComponentsRegistryTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h similarity index 69% rename from dDatabase/Tables/CDComponentsRegistryTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h index fc461989..45da7614 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDComponentsRegistryTable.h @@ -3,11 +3,13 @@ // 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 }; diff --git a/dDatabase/Tables/CDCurrencyTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp similarity index 98% rename from dDatabase/Tables/CDCurrencyTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp index 58c517f2..deb2b168 100644 --- a/dDatabase/Tables/CDCurrencyTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.cpp @@ -4,7 +4,7 @@ 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); diff --git a/dDatabase/Tables/CDCurrencyTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h similarity index 63% rename from dDatabase/Tables/CDCurrencyTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h index fd70a968..9c68c277 100644 --- a/dDatabase/Tables/CDCurrencyTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDCurrencyTableTable.h @@ -10,11 +10,11 @@ //! 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 + 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 diff --git a/dDatabase/Tables/CDDestructibleComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDDestructibleComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp index f92aec94..4939a50e 100644 --- a/dDatabase/Tables/CDDestructibleComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.cpp @@ -2,7 +2,7 @@ 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); diff --git a/dDatabase/Tables/CDDestructibleComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h similarity index 54% rename from dDatabase/Tables/CDDestructibleComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h index fb6ee4cd..0871d9ea 100644 --- a/dDatabase/Tables/CDDestructibleComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDDestructibleComponentTable.h @@ -4,20 +4,20 @@ #include "CDTable.h" struct CDDestructibleComponent { - unsigned int id; //!< The component ID from the ComponentsRegistry Table - int faction; //!< The Faction ID of the object + 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 - 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; //!< ??? + 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 - unsigned int death_behavior; //!< The behavior ID of the death behavior + uint32_t death_behavior; //!< The behavior ID of the death behavior bool isnpc; //!< Whether or not the object is an NPC - unsigned int attack_priority; //!< ??? + uint32_t attack_priority; //!< ??? bool isSmashable; //!< Whether or not the object is smashable - int difficultyLevel; //!< ??? + int32_t difficultyLevel; //!< ??? }; class CDDestructibleComponentTable : public CDTable { diff --git a/dDatabase/Tables/CDEmoteTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp similarity index 94% rename from dDatabase/Tables/CDEmoteTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp index 77aa226c..a7f39705 100644 --- a/dDatabase/Tables/CDEmoteTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.cpp @@ -20,7 +20,7 @@ void CDEmoteTableTable::LoadValuesFromDatabase() { tableData.finalize(); } -CDEmoteTable* CDEmoteTableTable::GetEmote(int id) { +CDEmoteTable* CDEmoteTableTable::GetEmote(int32_t id) { 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 86% rename from dDatabase/Tables/CDEmoteTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h index a49d1c45..360cfc38 100644 --- a/dDatabase/Tables/CDEmoteTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDEmoteTable.h @@ -16,11 +16,11 @@ 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; @@ -33,5 +33,5 @@ private: public: 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 88% rename from dDatabase/Tables/CDFeatureGatingTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp index 5013dd13..1a146bf1 100644 --- a/dDatabase/Tables/CDFeatureGatingTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.cpp @@ -3,7 +3,7 @@ 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); @@ -42,9 +42,9 @@ std::vector CDFeatureGatingTable::Query(std::function= entry) { return true; } } diff --git a/dDatabase/Tables/CDFeatureGatingTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h similarity index 64% rename from dDatabase/Tables/CDFeatureGatingTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h index 9a978218..5df202e3 100644 --- a/dDatabase/Tables/CDFeatureGatingTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDFeatureGatingTable.h @@ -9,6 +9,12 @@ 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 { @@ -21,7 +27,7 @@ public: // Queries the table with a custom "where" clause std::vector Query(std::function predicate); - bool FeatureUnlocked(const std::string& feature) const; + bool FeatureUnlocked(const CDFeatureGating& feature) const; const std::vector& GetEntries(void) const; }; diff --git a/dDatabase/Tables/CDInventoryComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDInventoryComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp index ffc8fee6..1a21a899 100644 --- a/dDatabase/Tables/CDInventoryComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDInventoryComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h similarity index 72% rename from dDatabase/Tables/CDInventoryComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h index 26d47ffe..1f5a525d 100644 --- a/dDatabase/Tables/CDInventoryComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDInventoryComponentTable.h @@ -4,9 +4,9 @@ #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 }; diff --git a/dDatabase/Tables/CDItemComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp similarity index 99% rename from dDatabase/Tables/CDItemComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp index 5d6722f9..9f7609e9 100644 --- a/dDatabase/Tables/CDItemComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.cpp @@ -5,7 +5,7 @@ CDItemComponent CDItemComponentTable::Default = {}; 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); @@ -69,7 +69,7 @@ void CDItemComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDItemComponent& CDItemComponentTable::GetItemComponentByID(unsigned int skillID) { +const CDItemComponent& CDItemComponentTable::GetItemComponentByID(uint32_t skillID) { const auto& it = this->entries.find(skillID); if (it != this->entries.end()) { return it->second; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h new file mode 100644 index 00000000..014c9801 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemComponentTable.h @@ -0,0 +1,64 @@ +#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 { +private: + std::map entries; + +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 96% rename from dDatabase/Tables/CDItemSetSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp index 6fb1689e..0376bad4 100644 --- a/dDatabase/Tables/CDItemSetSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.cpp @@ -3,7 +3,7 @@ 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); @@ -44,7 +44,7 @@ const std::vector& CDItemSetSkillsTable::GetEntries() 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) { diff --git a/dDatabase/Tables/CDItemSetSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h similarity index 64% rename from dDatabase/Tables/CDItemSetSkillsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h index 07321a7f..ee5fda05 100644 --- a/dDatabase/Tables/CDItemSetSkillsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetSkillsTable.h @@ -4,9 +4,9 @@ #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 + uint32_t SkillSetID; //!< The skill set ID + uint32_t SkillID; //!< The skill ID + uint32_t SkillCastType; //!< The skill cast type }; class CDItemSetSkillsTable : public CDTable { @@ -20,5 +20,5 @@ public: const std::vector& GetEntries() const; - std::vector GetBySkillID(unsigned int SkillSetID); + std::vector GetBySkillID(uint32_t SkillSetID); }; diff --git a/dDatabase/Tables/CDItemSetsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp similarity index 98% rename from dDatabase/Tables/CDItemSetsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp index de70e180..f3859ae2 100644 --- a/dDatabase/Tables/CDItemSetsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h new file mode 100644 index 00000000..77cc0c35 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDItemSetsTable.h @@ -0,0 +1,35 @@ +#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 { +private: + std::vector entries; + +public: + void LoadValuesFromDatabase(); + // 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/CDLevelProgressionLookupTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp similarity index 98% rename from dDatabase/Tables/CDLevelProgressionLookupTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp index d0a6ca93..930f3a85 100644 --- a/dDatabase/Tables/CDLevelProgressionLookupTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDLevelProgressionLookupTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h similarity index 82% rename from dDatabase/Tables/CDLevelProgressionLookupTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h index 6b68bd0d..fa1bb4cc 100644 --- a/dDatabase/Tables/CDLevelProgressionLookupTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLevelProgressionLookupTable.h @@ -4,8 +4,8 @@ #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 }; diff --git a/dDatabase/Tables/CDLootMatrixTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp similarity index 98% rename from dDatabase/Tables/CDLootMatrixTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp index 4755c116..36bebb69 100644 --- a/dDatabase/Tables/CDLootMatrixTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.cpp @@ -16,7 +16,7 @@ CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const { void CDLootMatrixTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootMatrix"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/Tables/CDLootMatrixTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h similarity index 63% rename from dDatabase/Tables/CDLootMatrixTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h index 551583f6..c7157e07 100644 --- a/dDatabase/Tables/CDLootMatrixTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootMatrixTable.h @@ -4,12 +4,12 @@ #include "CDTable.h" struct CDLootMatrix { - unsigned int LootTableIndex; //!< The Loot Table Index - unsigned int RarityTableIndex; //!< The Rarity Table Index + uint32_t LootTableIndex; //!< The Loot Table Index + uint32_t 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 flagID; //!< ??? + 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 }; diff --git a/dDatabase/Tables/CDLootTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp similarity index 99% rename from dDatabase/Tables/CDLootTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp index 9ee875df..a90f8108 100644 --- a/dDatabase/Tables/CDLootTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.cpp @@ -40,7 +40,7 @@ CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const { void CDLootTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM LootTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/Tables/CDLootTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h similarity index 75% rename from dDatabase/Tables/CDLootTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h index b8ac2066..e432d621 100644 --- a/dDatabase/Tables/CDLootTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDLootTableTable.h @@ -4,10 +4,10 @@ #include "CDTable.h" struct CDLootTable { - unsigned int itemid; //!< The LOT of the item - unsigned int LootTableIndex; //!< The Loot Table Index + 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 - unsigned int sortPriority; //!< The sorting priority + uint32_t sortPriority; //!< The sorting priority }; typedef uint32_t LootTableIndex; diff --git a/dDatabase/Tables/CDMissionEmailTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp similarity index 93% rename from dDatabase/Tables/CDMissionEmailTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp index 207d3815..c7e884c2 100644 --- a/dDatabase/Tables/CDMissionEmailTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.cpp @@ -3,7 +3,7 @@ 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); @@ -25,7 +25,7 @@ void CDMissionEmailTable::LoadValuesFromDatabase() { 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", ""); @@ -50,4 +50,3 @@ std::vector CDMissionEmailTable::Query(std::function& CDMissionEmailTable::GetEntries() const { return this->entries; } - diff --git a/dDatabase/Tables/CDMissionEmailTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h similarity index 75% rename from dDatabase/Tables/CDMissionEmailTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h index 954da78e..06c15e71 100644 --- a/dDatabase/Tables/CDMissionEmailTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionEmailTable.h @@ -4,13 +4,13 @@ #include "CDTable.h" struct CDMissionEmail { - unsigned int ID; - unsigned int messageType; - unsigned int notificationGroup; - unsigned int missionID; - unsigned int attachmentLOT; + uint32_t ID; + uint32_t messageType; + uint32_t notificationGroup; + uint32_t missionID; + uint32_t attachmentLOT; bool localize; - unsigned int locStatus; + uint32_t locStatus; std::string gate_version; }; diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDMissionNPCComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp index 59ea40b7..87d2bd61 100644 --- a/dDatabase/Tables/CDMissionNPCComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDMissionNPCComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h similarity index 87% rename from dDatabase/Tables/CDMissionNPCComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h index 2b2b3303..8c4b790d 100644 --- a/dDatabase/Tables/CDMissionNPCComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionNPCComponentTable.h @@ -4,8 +4,8 @@ #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 diff --git a/dDatabase/Tables/CDMissionTasksTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp similarity index 98% rename from dDatabase/Tables/CDMissionTasksTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp index 9795ea8f..b2cb9e21 100644 --- a/dDatabase/Tables/CDMissionTasksTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDMissionTasksTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h similarity index 64% rename from dDatabase/Tables/CDMissionTasksTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h index 0b169db8..420dbfbe 100644 --- a/dDatabase/Tables/CDMissionTasksTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionTasksTable.h @@ -4,17 +4,17 @@ #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 + 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 - int targetValue; //!< The target value + int32_t 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(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); //!< ??? }; diff --git a/dDatabase/Tables/CDMissionsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp similarity index 99% rename from dDatabase/Tables/CDMissionsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp index 37f0c81c..bc9eb76c 100644 --- a/dDatabase/Tables/CDMissionsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.cpp @@ -5,7 +5,7 @@ CDMissions CDMissionsTable::Default = {}; 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); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h new file mode 100644 index 00000000..de4b21c3 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMissionsTable.h @@ -0,0 +1,81 @@ +#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 { +private: + std::vector entries; + +public: + void LoadValuesFromDatabase(); + // 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() 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/CDMovementAIComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDMovementAIComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp index 35b46416..be1c3d96 100644 --- a/dDatabase/Tables/CDMovementAIComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDMovementAIComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h similarity index 97% rename from dDatabase/Tables/CDMovementAIComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDMovementAIComponentTable.h index b40694bd..8b415f3a 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; diff --git a/dDatabase/Tables/CDObjectSkillsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp similarity index 98% rename from dDatabase/Tables/CDObjectSkillsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp index 2439622c..958c6cc8 100644 --- a/dDatabase/Tables/CDObjectSkillsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDObjectSkillsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h similarity index 67% rename from dDatabase/Tables/CDObjectSkillsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h index bd9929e2..0b88fb6f 100644 --- a/dDatabase/Tables/CDObjectSkillsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectSkillsTable.h @@ -4,10 +4,10 @@ #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; //!< ??? + 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 { diff --git a/dDatabase/Tables/CDObjectsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp similarity index 97% rename from dDatabase/Tables/CDObjectsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp index d3094b68..3282e14c 100644 --- a/dDatabase/Tables/CDObjectsTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.cpp @@ -2,7 +2,7 @@ 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); @@ -40,7 +40,7 @@ void CDObjectsTable::LoadValuesFromDatabase() { m_default.id = 0; } -const CDObjects& CDObjectsTable::GetByID(unsigned int LOT) { +const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) { const auto& it = this->entries.find(LOT); if (it != this->entries.end()) { return it->second; diff --git a/dDatabase/Tables/CDObjectsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h similarity index 53% rename from dDatabase/Tables/CDObjectsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h index 3a776684..2ef47727 100644 --- a/dDatabase/Tables/CDObjectsTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDObjectsTable.h @@ -4,30 +4,30 @@ #include "CDTable.h" struct CDObjects { - unsigned int id; //!< The LOT of the object + uint32_t 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 + 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(unsigned int localize); //!< Whether or not the object should localize - UNUSED(unsigned int npcTemplateID); //!< Something related to NPCs... + 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(unsigned int nametag); //!< ??? + UNUSED(uint32_t nametag); //!< ??? UNUSED(std::string _internalNotes); //!< Some internal notes (rarely used) - UNUSED(unsigned int locStatus); //!< ??? + UNUSED(uint32_t 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 + UNUSED(uint32_t HQ_valid); //!< Probably used for the Nexus HQ database on LEGOUniverse.com }; class CDObjectsTable : public CDTable { private: - std::map entries; + std::map entries; CDObjects m_default; public: void LoadValuesFromDatabase(); // Gets an entry by ID - const CDObjects& GetByID(unsigned int LOT); + const CDObjects& GetByID(uint32_t LOT); }; diff --git a/dDatabase/Tables/CDPackageComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDPackageComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp index 8b955162..8038c779 100644 --- a/dDatabase/Tables/CDPackageComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDPackageComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h similarity index 85% rename from dDatabase/Tables/CDPackageComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h index 7ee58761..cc8b0636 100644 --- a/dDatabase/Tables/CDPackageComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPackageComponentTable.h @@ -4,9 +4,9 @@ #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 { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp new file mode 100644 index 00000000..c3dd5d50 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -0,0 +1,61 @@ +#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"); + while (!tableData.eof()) { + const uint32_t componentID = tableData.getIntField("id", defaultEntry.id); + + auto& entry = m_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() { + m_Entries.insert(std::make_pair(defaultEntry.id, defaultEntry)); +} + +CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { + auto itr = m_Entries.find(componentID); + if (itr == m_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..fa54e457 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.h @@ -0,0 +1,45 @@ +#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); + +private: + std::map m_Entries; +}; diff --git a/dDatabase/Tables/CDPhysicsComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp similarity index 94% rename from dDatabase/Tables/CDPhysicsComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp index e17be4df..ebc5327b 100644 --- a/dDatabase/Tables/CDPhysicsComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.cpp @@ -28,7 +28,7 @@ void CDPhysicsComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -CDPhysicsComponent* CDPhysicsComponentTable::GetByID(unsigned int componentID) { +CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) { auto itr = m_entries.find(componentID); return itr != m_entries.end() ? &itr->second : nullptr; } diff --git a/dDatabase/Tables/CDPhysicsComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h similarity index 78% rename from dDatabase/Tables/CDPhysicsComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPhysicsComponentTable.h index 49f3b4c3..5ed33cc9 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); @@ -26,8 +26,8 @@ public: void LoadValuesFromDatabase(); static const std::string GetTableName() { return "PhysicsComponent"; }; - CDPhysicsComponent* GetByID(unsigned int componentID); + CDPhysicsComponent* GetByID(uint32_t componentID); private: - std::map m_entries; + std::map m_entries; }; diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp similarity index 100% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.cpp diff --git a/dDatabase/Tables/CDPropertyEntranceComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h similarity index 100% rename from dDatabase/Tables/CDPropertyEntranceComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyEntranceComponentTable.h diff --git a/dDatabase/Tables/CDPropertyTemplateTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp similarity index 100% rename from dDatabase/Tables/CDPropertyTemplateTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.cpp diff --git a/dDatabase/Tables/CDPropertyTemplateTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h similarity index 100% rename from dDatabase/Tables/CDPropertyTemplateTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDPropertyTemplateTable.h diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDProximityMonitorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp index ae0abac8..6edd00b2 100644 --- a/dDatabase/Tables/CDProximityMonitorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDProximityMonitorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h similarity index 97% rename from dDatabase/Tables/CDProximityMonitorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h index a50dd37e..861c900e 100644 --- a/dDatabase/Tables/CDProximityMonitorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDProximityMonitorComponentTable.h @@ -4,7 +4,7 @@ #include "CDTable.h" struct CDProximityMonitorComponent { - unsigned int id; + uint32_t id; std::string Proximities; bool LoadOnClient; bool LoadOnServer; diff --git a/dDatabase/Tables/CDRailActivatorComponent.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp similarity index 100% rename from dDatabase/Tables/CDRailActivatorComponent.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.cpp diff --git a/dDatabase/Tables/CDRailActivatorComponent.h b/dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h similarity index 100% rename from dDatabase/Tables/CDRailActivatorComponent.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRailActivatorComponent.h diff --git a/dDatabase/Tables/CDRarityTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp similarity index 97% rename from dDatabase/Tables/CDRarityTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp index aa451ae3..6f086e34 100644 --- a/dDatabase/Tables/CDRarityTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.cpp @@ -3,7 +3,7 @@ void CDRarityTableTable::LoadValuesFromDatabase() { // First, get the size of the table - unsigned int size = 0; + uint32_t size = 0; auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable"); while (!tableSize.eof()) { size = tableSize.getIntField(0, 0); diff --git a/dDatabase/Tables/CDRarityTableTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h similarity index 95% rename from dDatabase/Tables/CDRarityTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h index e2053da7..1248350b 100644 --- a/dDatabase/Tables/CDRarityTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRarityTableTable.h @@ -5,7 +5,7 @@ struct CDRarityTable { float randmax; - unsigned int rarity; + uint32_t rarity; }; typedef std::vector RarityTable; diff --git a/dDatabase/Tables/CDRebuildComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDRebuildComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp index f5706a28..30534936 100644 --- a/dDatabase/Tables/CDRebuildComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDRebuildComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h similarity index 75% rename from dDatabase/Tables/CDRebuildComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h index fc78e904..aed71905 100644 --- a/dDatabase/Tables/CDRebuildComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRebuildComponentTable.h @@ -4,15 +4,15 @@ #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 }; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp new file mode 100644 index 00000000..4dab9ee9 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp @@ -0,0 +1,47 @@ +#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 + this->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", "")); + + this->entries.push_back(entry); + tableData.nextRow(); + } +} + +LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const { + for (auto const &entry : this->entries){ + if (rewardCodeId == entry.id) return entry.attachmentLOT; + } + return LOT_NULL; +} + +uint32_t CDRewardCodesTable::GetCodeID(std::string code) const { + for (auto const &entry : this->entries){ + 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..1010a572 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h @@ -0,0 +1,25 @@ +#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 { +private: + std::vector entries; + +public: + void LoadValuesFromDatabase(); + const std::vector& GetEntries() const; + LOT GetAttachmentLOT(uint32_t rewardCodeId) const; + uint32_t GetCodeID(std::string code) const; +}; diff --git a/dDatabase/Tables/CDRewardsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp similarity index 100% rename from dDatabase/Tables/CDRewardsTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.cpp diff --git a/dDatabase/Tables/CDRewardsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h similarity index 100% rename from dDatabase/Tables/CDRewardsTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDRewardsTable.h diff --git a/dDatabase/Tables/CDScriptComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp similarity index 83% rename from dDatabase/Tables/CDScriptComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp index 1a922199..a2fe0514 100644 --- a/dDatabase/Tables/CDScriptComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.cpp @@ -3,7 +3,7 @@ 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); @@ -28,8 +28,8 @@ void CDScriptComponentTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDScriptComponent& CDScriptComponentTable::GetByID(unsigned int id) { - std::map::iterator it = this->entries.find(id); +const CDScriptComponent& CDScriptComponentTable::GetByID(uint32_t id) { + std::map::iterator it = this->entries.find(id); if (it != this->entries.end()) { return it->second; } diff --git a/dDatabase/Tables/CDScriptComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h similarity index 70% rename from dDatabase/Tables/CDScriptComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h index d2b7e7ae..56296776 100644 --- a/dDatabase/Tables/CDScriptComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDScriptComponentTable.h @@ -4,19 +4,19 @@ #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; + std::map entries; CDScriptComponent m_ToReturnWhenNoneFound; public: 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 91% rename from dDatabase/Tables/CDSkillBehaviorTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp index 8ffbb5ce..51ed7de3 100644 --- a/dDatabase/Tables/CDSkillBehaviorTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.cpp @@ -4,7 +4,7 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() { m_empty = CDSkillBehavior(); // 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); @@ -49,8 +49,8 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() { tableData.finalize(); } -const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) { - std::map::iterator it = this->entries.find(skillID); +const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(uint32_t skillID) { + std::map::iterator it = this->entries.find(skillID); if (it != this->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..0c970be6 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDSkillBehaviorTable.h @@ -0,0 +1,39 @@ +#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 { +private: + std::map entries; + CDSkillBehavior m_empty; + +public: + void LoadValuesFromDatabase(); + + // Gets an entry by skillID + const CDSkillBehavior& GetSkillByID(uint32_t skillID); +}; + diff --git a/dDatabase/Tables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h similarity index 89% rename from dDatabase/Tables/CDTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDTable.h index e4c11fb9..ab965127 100644 --- a/dDatabase/Tables/CDTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h @@ -8,6 +8,7 @@ #include #include #include +#include // CPPLinq #ifdef _WIN32 @@ -22,6 +23,9 @@ // 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" diff --git a/dDatabase/Tables/CDVendorComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp similarity index 98% rename from dDatabase/Tables/CDVendorComponentTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp index 0f963b04..990d0b32 100644 --- a/dDatabase/Tables/CDVendorComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.cpp @@ -3,7 +3,7 @@ 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); diff --git a/dDatabase/Tables/CDVendorComponentTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h similarity index 83% rename from dDatabase/Tables/CDVendorComponentTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h index 29854d49..133ce78f 100644 --- a/dDatabase/Tables/CDVendorComponentTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDVendorComponentTable.h @@ -4,11 +4,11 @@ #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 { diff --git a/dDatabase/Tables/CDZoneTableTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp similarity index 96% rename from dDatabase/Tables/CDZoneTableTable.cpp rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp index 2793ccb9..b599c37f 100644 --- a/dDatabase/Tables/CDZoneTableTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.cpp @@ -3,7 +3,7 @@ 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); @@ -53,7 +53,7 @@ void CDZoneTableTable::LoadValuesFromDatabase() { } //! Queries the table with a zoneID to find. -const CDZoneTable* CDZoneTableTable::Query(unsigned int zoneID) { +const CDZoneTable* CDZoneTableTable::Query(uint32_t zoneID) { 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 67% rename from dDatabase/Tables/CDZoneTableTable.h rename to dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h index c3f51aa6..5f5970ae 100644 --- a/dDatabase/Tables/CDZoneTableTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDZoneTableTable.h @@ -4,14 +4,14 @@ #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? @@ -19,9 +19,9 @@ struct CDZoneTable { UNUSED(std::string mixerProgram); //!< ??? UNUSED(std::string clientPhysicsFramerate); //!< The client physics framerate 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 + 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; //!< ??? @@ -35,11 +35,11 @@ struct CDZoneTable { class CDZoneTableTable : public CDTable { private: - std::map m_Entries; + std::map m_Entries; public: 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 b6a02b02..af401db2 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" @@ -23,6 +23,7 @@ set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" "CDMovementAIComponentTable.cpp" "CDObjectSkillsTable.cpp" "CDObjectsTable.cpp" + "CDPetComponentTable.cpp" "CDPackageComponentTable.cpp" "CDPhysicsComponentTable.cpp" "CDPropertyEntranceComponentTable.cpp" @@ -31,6 +32,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/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 5fc76786..00000000 --- a/dDatabase/Database.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "Database.h" -#include "Game.h" -#include "dConfig.h" -#include "Logger.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 != "") LOG("Destroying MySQL connection from %s!", source.c_str()); - else LOG("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(); - 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"); - } - - 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 71% rename from dDatabase/MigrationRunner.cpp rename to dDatabase/GameDatabase/MigrationRunner.cpp index 2dbe9101..8034a3e2 100644 --- a/dDatabase/MigrationRunner.cpp +++ b/dDatabase/GameDatabase/MigrationRunner.cpp @@ -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,13 +43,7 @@ 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; LOG("Running migration: %s", migration.name.c_str()); if (migration.name == "dlu/5_brick_model_sd0.sql") { @@ -60,10 +52,7 @@ void MigrationRunner::RunMigrations() { 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) { @@ -73,11 +62,10 @@ void MigrationRunner::RunMigrations() { 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) { LOG("Encountered error running migration: %s", e.what()); } @@ -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,27 +95,17 @@ 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; } @@ -148,9 +124,8 @@ void MigrationRunner::RunSQLiteMigrations() { // 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;"); } 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/CDItemComponentTable.h b/dDatabase/Tables/CDItemComponentTable.h deleted file mode 100644 index 685e5acd..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: - void LoadValuesFromDatabase(); - 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/CDItemSetsTable.h b/dDatabase/Tables/CDItemSetsTable.h deleted file mode 100644 index a3a738b1..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: - void LoadValuesFromDatabase(); - // 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/CDMissionsTable.h b/dDatabase/Tables/CDMissionsTable.h deleted file mode 100644 index c8ddc2a3..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: - void LoadValuesFromDatabase(); - // 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() 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/CDSkillBehaviorTable.h b/dDatabase/Tables/CDSkillBehaviorTable.h deleted file mode 100644 index 5b1081cd..00000000 --- a/dDatabase/Tables/CDSkillBehaviorTable.h +++ /dev/null @@ -1,39 +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: - void LoadValuesFromDatabase(); - - // Gets an entry by skillID - const CDSkillBehavior& GetSkillByID(unsigned int skillID); -}; - diff --git a/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 984ecee0..b3d3c25c 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -3,69 +3,34 @@ set(DGAME_SOURCES "Character.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() - add_subdirectory(dCinema) -foreach(file ${DGAME_DCINEMA_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dCinema/${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 dCinema +) diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 7259602f..ee04710a 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -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; @@ -406,17 +343,11 @@ void Character::SetIsNewLogin() { 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) { diff --git a/dGame/Character.h b/dGame/Character.h index 79ce0c0c..6c8ead30 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 @@ -458,6 +458,7 @@ public: void SetBillboardVisible(bool visible); 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 0f8e9351..8b72a80c 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -3,7 +3,6 @@ #include "CDClientManager.h" #include "Game.h" #include "Logger.h" -#include #include #include "CDDestructibleComponentTable.h" #include "CDClientDatabase.h" @@ -25,6 +24,8 @@ #include "eMissionTaskType.h" #include "eTriggerEventType.h" #include "eObjectBits.h" +#include "PositionUpdate.h" +#include "eChatMessageType.h" //Component includes: #include "Component.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" @@ -76,10 +77,11 @@ #include "eGameMasterLevel.h" #include "eReplicaComponentType.h" #include "eReplicaPacketType.h" -#include "ZoneControlComponent.h" +#include "MiniGameControlComponent.h" #include "RacingStatsComponent.h" #include "CollectibleComponent.h" #include "ItemComponent.h" +#include "GhostComponent.h" // Table includes #include "CDComponentsRegistryTable.h" @@ -166,7 +168,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); + } } /** @@ -217,8 +221,8 @@ void Entity::Initialize() { AddComponent(petComponentId); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ZONE_CONTROL) > 0) { - AddComponent(); + if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MINI_GAME_CONTROL) > 0) { + AddComponent(); } uint32_t possessableComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::POSSESSABLE); @@ -299,10 +303,10 @@ void Entity::Initialize() { AddComponent()->SetPhysicsEffectActive(false); } - if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VEHICLE_PHYSICS) > 0) { - auto* vehiclePhysicsComponent = AddComponent(); - 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) { @@ -325,16 +329,16 @@ void Entity::Initialize() { * 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 = -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(); std::vector destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); }); - + bool isSmashable = GetVarAs(u"is_smashable") != 0; if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) { DestroyableComponent* comp = AddComponent(); @@ -434,6 +438,8 @@ void Entity::Initialize() { AddComponent(); AddComponent(m_Character)->LoadFromXml(m_Character->GetXMLDoc()); + + AddComponent(); } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { @@ -519,45 +525,50 @@ void Entity::Initialize() { } if (int componentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD) > 0) { - auto* rebuildComponent = AddComponent(); + auto* quickBuildComponent = AddComponent(); CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable(); - std::vector rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == rebuildComponentID); }); + std::vector rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == quickBuildComponentID); }); if (rebCompData.size() > 0) { - rebuildComponent->SetResetTime(rebCompData[0].reset_time); - rebuildComponent->SetCompleteTime(rebCompData[0].complete_time); - rebuildComponent->SetTakeImagination(rebCompData[0].take_imagination); - rebuildComponent->SetInterruptible(rebCompData[0].interruptible); - rebuildComponent->SetSelfActivator(rebCompData[0].self_activator); - rebuildComponent->SetActivityId(rebCompData[0].activityID); - rebuildComponent->SetPostImaginationCost(rebCompData[0].post_imagination_cost); - rebuildComponent->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) { - rebuildComponent->SetResetTime(rebuildResetTime); + quickBuildComponent->SetResetTime(rebuildResetTime); // 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) - { - rebuildComponent->SetResetTime(rebuildComponent->GetResetTime() + 25); + if (m_TemplateID == 9483) { + quickBuildComponent->SetResetTime(quickBuildComponent->GetResetTime() + 25); } } const auto activityID = GetVar(u"activityID"); if (activityID > 0) { - rebuildComponent->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) { - rebuildComponent->SetCompleteTime(compTime); + quickBuildComponent->SetCompleteTime(compTime); } } } @@ -598,8 +609,8 @@ void Entity::Initialize() { } // Scripted activity component - int scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY); - if ((scriptedActivityID > 0)) { + int scriptedActivityID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SCRIPTED_ACTIVITY, -1); + if ((scriptedActivityID != -1)) { AddComponent(scriptedActivityID); } @@ -738,7 +749,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)) { @@ -848,9 +859,20 @@ void Entity::SetGMLevel(eGameMasterLevel value) { } CharacterComponent* character = GetComponent(); - if (character) character->SetGMLevel(value); + if (!character) return; + character->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) { @@ -975,9 +997,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()); } } } @@ -1017,9 +1039,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate); } - VehiclePhysicsComponent* vehiclePhysicsComponent; - if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) { - vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); + HavokVehiclePhysicsComponent* havokVehiclePhysicsComponent; + if (TryGetComponent(eReplicaComponentType::HAVOK_VEHICLE_PHYSICS, havokVehiclePhysicsComponent)) { + havokVehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate); } PhantomPhysicsComponent* phantomPhysicsComponent; @@ -1118,14 +1140,14 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType 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); } destroyableSerialized = true; - rebuildComponent->Serialize(outBitStream, bIsInitialUpdate); + quickBuildComponent->Serialize(outBitStream, bIsInitialUpdate); } MovingPlatformComponent* movingPlatformComponent; @@ -1191,9 +1213,9 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType } } - ZoneControlComponent* zoneControlComponent; - if (TryGetComponent(eReplicaComponentType::ZONE_CONTROL, zoneControlComponent)) { - zoneControlComponent->Serialize(outBitStream, bIsInitialUpdate); + MiniGameControlComponent* miniGameControlComponent; + if (TryGetComponent(eReplicaComponentType::MINI_GAME_CONTROL, miniGameControlComponent)) { + miniGameControlComponent->Serialize(outBitStream, bIsInitialUpdate); } // BBB Component, unused currently @@ -1215,39 +1237,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()) { @@ -1503,7 +1542,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) { @@ -1527,7 +1566,7 @@ void Entity::Kill(Entity* murderer) { bool waitForDeathAnimation = false; if (destroyableComponent) { - waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0; + waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT; } // Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction! @@ -1581,10 +1620,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); } } @@ -1685,31 +1724,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) { @@ -1721,8 +1749,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; } @@ -1730,21 +1758,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 { @@ -1840,7 +1857,7 @@ const NiPoint3& Entity::GetPosition() const { return simple->GetPosition(); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { return vehicel->GetPosition(); @@ -1868,7 +1885,7 @@ const NiQuaternion& Entity::GetRotation() const { return simple->GetRotation(); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { return vehicel->GetRotation(); @@ -1877,7 +1894,7 @@ const NiQuaternion& Entity::GetRotation() const { return NiQuaternion::IDENTITY; } -void Entity::SetPosition(NiPoint3 position) { +void Entity::SetPosition(const NiPoint3& position) { auto* controllable = GetComponent(); if (controllable != nullptr) { @@ -1896,7 +1913,7 @@ void Entity::SetPosition(NiPoint3 position) { simple->SetPosition(position); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { vehicel->SetPosition(position); @@ -1905,7 +1922,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) { @@ -1924,7 +1941,7 @@ void Entity::SetRotation(NiQuaternion rotation) { simple->SetRotation(rotation); } - auto* vehicel = GetComponent(); + auto* vehicel = GetComponent(); if (vehicel != nullptr) { vehicel->SetRotation(rotation); @@ -2054,3 +2071,75 @@ 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 != NiPoint3::ZERO); + havokVehiclePhysicsComponent->SetAngularVelocity(update.angularVelocity); + havokVehiclePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::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 != NiPoint3::ZERO); + possessedControllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + possessedControllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + } + Game::entityManager->SerializeEntity(possassableEntity); + } + } + + if (!updateChar) { + update.velocity = NiPoint3::ZERO; + update.angularVelocity = NiPoint3::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 != NiPoint3::ZERO); + controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity); + controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO); + + auto* ghostComponent = GetComponent(); + if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position); + Game::entityManager->QueueGhostUpdate(GetObjectID()); + + if (updateChar) Game::entityManager->SerializeEntity(this); +} diff --git a/dGame/Entity.h b/dGame/Entity.h index a5588c3e..36621d5c 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; @@ -105,7 +106,7 @@ public: virtual User* GetParentUser() const; - virtual SystemAddress GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; + virtual const SystemAddress& GetSystemAddress() const { return UNASSIGNED_SYSTEM_ADDRESS; }; /** * Setters @@ -123,13 +124,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) {} + virtual void SetRespawnPos(const NiPoint3& position) {} - virtual void SetRespawnRot(NiQuaternion rotation) {} + virtual void SetRespawnRot(const NiQuaternion& rotation) {} virtual void SetSystemAddress(const SystemAddress& value) {}; @@ -160,6 +161,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); @@ -210,8 +213,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(); @@ -226,8 +229,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; } + virtual const NiPoint3& GetRespawnPosition() const { return NiPoint3::ZERO; } + virtual const NiQuaternion& GetRespawnRotation() const { return NiQuaternion::IDENTITY; } void Sleep(); void Wake(); @@ -294,6 +297,8 @@ public: Entity* GetScheduledKiller() { return m_ScheduleKiller; } + void ProcessPositionUpdate(PositionUpdate& update); + protected: LWOOBJID m_ObjectID; @@ -324,9 +329,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; diff --git a/dGame/EntityManager.cpp b/dGame/EntityManager.cpp index a1bb4ca7..f7f55a61 100644 --- a/dGame/EntityManager.cpp +++ b/dGame/EntityManager.cpp @@ -2,7 +2,7 @@ #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" @@ -11,7 +11,6 @@ #include "SkillComponent.h" #include "SwitchComponent.h" #include "UserManager.h" -#include "PacketUtils.h" #include "Metrics.hpp" #include "dZoneManager.h" #include "MissionComponent.h" @@ -24,6 +23,8 @@ #include "eGameMasterLevel.h" #include "eReplicaComponentType.h" #include "eReplicaPacketType.h" +#include "PlayerManager.h" +#include "GhostComponent.h" #include "ServerPreconditions.h" // Configure which zones have ghosting disabled, mostly small worlds. @@ -48,10 +49,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 }; @@ -94,7 +91,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 @@ -186,15 +183,16 @@ void EntityManager::SerializeEntities() { 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(toSerialize)) { + for (auto* player : PlayerManager::GetAllPlayers()) { + auto* ghostComponent = player->GetComponent(); + if (ghostComponent && ghostComponent->IsObserved(toSerialize)) { Game::server->Send(&stream, player->GetSystemAddress(), false); } } @@ -371,9 +369,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); @@ -382,11 +380,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()); } } } @@ -394,8 +393,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); @@ -413,7 +410,7 @@ void EntityManager::ConstructAllEntities(const SystemAddress& sysAddr) { } } - UpdateGhosting(Player::GetPlayer(sysAddr)); + UpdateGhosting(PlayerManager::GetPlayer(sysAddr)); } void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr) { @@ -421,14 +418,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()); } } } @@ -439,8 +437,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) { @@ -475,7 +471,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; @@ -493,13 +489,14 @@ void EntityManager::UpdateGhosting(Player* player) { } 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; @@ -508,7 +505,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); @@ -522,7 +519,7 @@ void EntityManager::UpdateGhosting(Player* player) { auto condition = ServerPreconditions::CheckPreconditions(entity, player); if (observed && ((distance > ghostingDistanceMax && !isOverride) || !condition)) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); @@ -539,7 +536,7 @@ void EntityManager::UpdateGhosting(Player* player) { } } - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); @@ -560,25 +557,28 @@ 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); const auto precondition = ServerPreconditions::CheckPreconditions(entity, player); if (observed && (distance > ghostingDistanceMax || !precondition)) { - player->GhostEntity(id); + ghostComponent->GhostEntity(id); DestructEntity(entity, player->GetSystemAddress()); entity->SetObservers(entity->GetObservers() - 1); } else if (!observed && (ghostingDistanceMin > distance && precondition)) { - player->ObserveEntity(id); + ghostComponent->ObserveEntity(id); ConstructEntity(entity, player->GetSystemAddress()); diff --git a/dGame/LeaderboardManager.cpp b/dGame/LeaderboardManager.cpp index b7083ea8..1fd49d26 100644 --- a/dGame/LeaderboardManager.cpp +++ b/dGame/LeaderboardManager.cpp @@ -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; @@ -237,7 +237,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r } baseLookup += " LIMIT 1"; LOG_DEBUG("query is %s", baseLookup.c_str()); - std::unique_ptr baseQuery(Database::CreatePreppedStmt(baseLookup)); + std::unique_ptr baseQuery(Database::Get()->CreatePreppedStmt(baseLookup)); baseQuery->setInt(1, this->gameID); std::unique_ptr baseResult(baseQuery->executeQuery()); @@ -250,7 +250,7 @@ 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())); + 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) { @@ -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()); @@ -378,14 +378,14 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi saveQuery = FormatInsert(leaderboardType, newScore, false); } LOG("save query %s %i %i", saveQuery.c_str(), playerID, activityId); - std::unique_ptr saveStatement(Database::CreatePreppedStmt(saveQuery)); + 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(); diff --git a/dGame/Player.cpp b/dGame/Player.cpp index 884c1df1..8f414b43 100644 --- a/dGame/Player.cpp +++ b/dGame/Player.cpp @@ -3,22 +3,33 @@ #include #include "Character.h" -#include "Database.h" -#include "MissionComponent.h" #include "UserManager.h" #include "EntityManager.h" +#include "Game.h" #include "Logger.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" +#include "PlayerManager.h" -std::vector Player::m_Players = {}; +void Player::SetRespawnPos(const NiPoint3& position) { + if (!m_Character) return; + + m_respawnPos = position; + + m_Character->SetRespawnPoint(Game::zoneManager->GetZone()->GetWorldID(), position); + +} + +void Player::SetRespawnRot(const NiQuaternion& rotation) { + m_respawnRot = rotation; +} + +void Player::SetSystemAddress(const SystemAddress& value) { + m_SystemAddress = value; +} Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Entity* parentEntity) : Entity(objectID, info, parentEntity) { m_ParentUser = user; @@ -26,237 +37,19 @@ Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Enti 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_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) { - m_ObservedEntities.emplace(id); -} - -bool Player::IsObserved(int32_t id) { - return m_ObservedEntities.find(id) != m_ObservedEntities.end(); -} - -void Player::GhostEntity(int32_t id) { - const auto& iter = m_ObservedEntities.find(id); - - if (iter == m_ObservedEntities.end()) { - return; - } - - m_ObservedEntities.erase(iter); -} - -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; + PlayerManager::AddPlayer(this); } Player::~Player() { LOG("Deleted player"); - - for (const auto& id : m_ObservedEntities) - { - 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()) { + + // 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; } @@ -275,6 +68,4 @@ Player::~Player() { } } } - - m_Players.erase(iter); } diff --git a/dGame/Player.h b/dGame/Player.h index f41ff4a5..3a269f57 100644 --- a/dGame/Player.h +++ b/dGame/Player.h @@ -20,92 +20,34 @@ public: * Getters */ - User* GetParentUser() const override; + User* GetParentUser() const override { return m_ParentUser; }; - SystemAddress GetSystemAddress() const override; + const SystemAddress& GetSystemAddress() const override { return m_SystemAddress; }; - NiPoint3 GetRespawnPosition() const override; + const NiPoint3& GetRespawnPosition() const override { return m_respawnPos; }; - NiQuaternion GetRespawnRotation() const override; + const NiQuaternion& GetRespawnRotation() const override { return m_respawnRot; }; - const NiPoint3& GetGhostReferencePoint() const; + std::map& GetDroppedLoot() { return m_DroppedLoot; }; - const NiPoint3& GetOriginGhostReferencePoint() const; - - const NiPoint3& GetGhostOverridePoint() const; - - bool GetGhostOverride() const; - - std::map& GetDroppedLoot(); - - uint64_t GetDroppedCoins(); + uint64_t GetDroppedCoins() const { return m_DroppedCoins; }; /** * Setters */ + void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; }; + void SetSystemAddress(const SystemAddress& value) override; - void SetRespawnPos(NiPoint3 position) override; + void SetRespawnPos(const 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); + void SetRespawnRot(const NiQuaternion& rotation) override; /** * 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; @@ -116,19 +58,7 @@ private: User* m_ParentUser; - NiPoint3 m_GhostReferencePoint; - - NiPoint3 m_GhostOverridePoint; - - bool m_GhostOverride; - - std::unordered_set m_ObservedEntities; - - 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..e3017f05 --- /dev/null +++ b/dGame/PlayerManager.cpp @@ -0,0 +1,68 @@ +#include "PlayerManager.h" + +#include "Character.h" +#include "Player.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(Player* 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(Player* 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; +} + +Player* PlayerManager::GetPlayer(const SystemAddress& sysAddr) { + auto* entity = UserManager::Instance()->GetUser(sysAddr)->GetLastUsedChar()->GetEntity(); + + return static_cast(entity); +} + +Player* PlayerManager::GetPlayer(const std::string& name) { + const auto characters = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CHARACTER); + + Player* player = nullptr; + for (auto* character : characters) { + if (!character->IsPlayer()) continue; + + if (GeneralUtils::CaseInsensitiveStringCompare(name, character->GetCharacter()->GetName())) { + player = dynamic_cast(character); + break; + } + } + + return player; +} + +Player* PlayerManager::GetPlayer(LWOOBJID playerID) { + Player* 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..bb54f83b --- /dev/null +++ b/dGame/PlayerManager.h @@ -0,0 +1,25 @@ +#ifndef __PLAYERMANAGER__H__ +#define __PLAYERMANAGER__H__ + +#include "dCommonVars.h" + +#include + +class Player; +struct SystemAddress; + +namespace PlayerManager { + void AddPlayer(Player* player); + + bool RemovePlayer(Player* player); + + Player* GetPlayer(const SystemAddress& sysAddr); + + Player* GetPlayer(const std::string& name); + + Player* GetPlayer(LWOOBJID playerID); + + const std::vector& GetAllPlayers(); +}; + +#endif //!__PLAYERMANAGER__H__ diff --git a/dGame/TradingManager.cpp b/dGame/TradingManager.cpp index 7c3c9bb6..3d9a209c 100644 --- a/dGame/TradingManager.cpp +++ b/dGame/TradingManager.cpp @@ -2,7 +2,7 @@ #include "EntityManager.h" #include "GameMessages.h" #include "InventoryComponent.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "Game.h" #include "Logger.h" #include "Item.h" @@ -273,7 +273,7 @@ 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); diff --git a/dGame/User.cpp b/dGame/User.cpp index afde75f6..0b2c3c3f 100644 --- a/dGame/User.cpp +++ b/dGame/User.cpp @@ -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); - LOG("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() { @@ -131,3 +110,7 @@ void User::UserOutOfSync() { 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 aaa4893f..fc582108 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -8,11 +8,10 @@ #include "Game.h" #include "Logger.h" #include "User.h" -#include +#include "WorldPackets.h" #include "Character.h" -#include -#include "PacketUtils.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "BitStream.h" +#include "ObjectIDManager.h" #include "Logger.h" #include "GeneralUtils.h" #include "ZoneInstanceManager.h" @@ -44,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) { + 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) { + 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) { + 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) { + // 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() { @@ -125,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; } } @@ -158,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]); @@ -200,11 +181,6 @@ bool UserManager::IsNamePreapproved(const std::string& requestedName) { void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { User* u = GetUser(sysAddr); if (!u) return; - - 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(); std::vector& chars = u->GetCharacters(); for (size_t i = 0; i < chars.size(); ++i) { @@ -226,157 +202,186 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { chars[i]->SaveXMLToDatabase(); chars[i]->GetEntity()->SetCharacter(nullptr); - + delete chars[i]; } chars.clear(); - while (res->next()) { - LWOOBJID objID = res->getUInt64(1); - Character* character = new Character(uint32_t(objID), u); + for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) { + Character* character = new Character(characterId, u); + character->UpdateFromDatabase(); character->SetIsNewLogin(); chars.push_back(character); } - delete res; - delete stmt; + RakNet::BitStream bitStream; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE); - WorldPackets::SendCharacterList(sysAddr, u); + 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]); + } + } + + 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::ReadU32(74, packet); - uint32_t middleNameIndex = PacketUtils::ReadU32(78, packet); - uint32_t lastNameIndex = PacketUtils::ReadU32(82, packet); + const auto name = LUWStringName.GetAsString(); std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex); - - uint32_t shirtColor = PacketUtils::ReadU32(95, packet); - uint32_t shirtStyle = PacketUtils::ReadU32(99, packet); - uint32_t pantsColor = PacketUtils::ReadU32(103, packet); - uint32_t hairStyle = PacketUtils::ReadU32(107, packet); - uint32_t hairColor = PacketUtils::ReadU32(111, packet); - uint32_t lh = PacketUtils::ReadU32(115, packet); - uint32_t rh = PacketUtils::ReadU32(119, packet); - uint32_t eyebrows = PacketUtils::ReadU32(123, packet); - uint32_t eyes = PacketUtils::ReadU32(127, packet); - uint32_t mouth = PacketUtils::ReadU32(131, packet); - LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle); LOT pantsLOT = FindCharPantsID(pantsColor); - if (name != "" && !UserManager::IsNameAvailable(name)) { + 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)) { + 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 == "") { + if (name.empty()) { LOG("AccountID: %i is creating a character with predefined name: %s", u->GetAccountID(), predefinedName.c_str()); } else { 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()) { - LOG("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); }); } @@ -387,7 +392,9 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) return; } - LWOOBJID objectID = PacketUtils::ReadS64(8, packet); + CINSTREAM_SKIP_HEADER; + LWOOBJID objectID; + inStream.Read(objectID); uint32_t charID = static_cast(objectID); LOG("Received char delete req for ID: %llu (%u)", objectID, charID); @@ -403,73 +410,12 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet) WorldPackets::SendCharacterDeleteResponse(sysAddr, false); } else { LOG("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; - BitStreamUtils::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; - } + 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); } @@ -482,14 +428,18 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) return; } - LWOOBJID objectID = PacketUtils::ReadS64(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); 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; @@ -517,26 +467,14 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet) 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; - + 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; - + 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); @@ -566,11 +504,7 @@ 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 @@ -592,16 +526,18 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID 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&) { - LOG("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; } @@ -609,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&) { - LOG("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/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 463cad8e..2a7e9754 100644 --- a/dGame/dBehaviors/AreaOfEffectBehavior.cpp +++ b/dGame/dBehaviors/AreaOfEffectBehavior.cpp @@ -7,7 +7,7 @@ #include "Logger.h" #include "BehaviorBranchContext.h" #include "BehaviorContext.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include "Game.h" #include "Logger.h" diff --git a/dGame/dBehaviors/BasicAttackBehavior.cpp b/dGame/dBehaviors/BasicAttackBehavior.cpp index 36615a9f..914e67a5 100644 --- a/dGame/dBehaviors/BasicAttackBehavior.cpp +++ b/dGame/dBehaviors/BasicAttackBehavior.cpp @@ -22,7 +22,6 @@ void BasicAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bi if (entity->IsPlayer() && !this->m_DontApplyImmune) { const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime; destroyableComponent->SetDamageCooldownTimer(immunityTime); - LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime); } } @@ -188,11 +187,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet return; } - const float immunityTime = Game::zoneManager->GetWorldConfig()->globalImmunityTime; - LOG_DEBUG("Damage cooldown timer currently %f s", destroyableComponent->GetDamageCooldownTimer()); - - const bool isImmune = (destroyableComponent->IsImmune()) || (destroyableComponent->IsCooldownImmune()); - + const bool isImmune = destroyableComponent->IsImmune() || destroyableComponent->IsCooldownImmune(); bitStream->Write(isImmune); if (isImmune) { @@ -219,8 +214,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet //Handle player damage cooldown if (isSuccess && targetEntity->IsPlayer() && !this->m_DontApplyImmune) { - destroyableComponent->SetDamageCooldownTimer(immunityTime); - LOG_DEBUG("Target targetEntity %llu took damage, setting damage cooldown timer to %f s", branch.target, immunityTime); + destroyableComponent->SetDamageCooldownTimer(Game::zoneManager->GetWorldConfig()->globalImmunityTime); } eBasicAttackSuccessTypes successState = eBasicAttackSuccessTypes::FAILIMMUNE; diff --git a/dGame/dBehaviors/Behavior.cpp b/dGame/dBehaviors/Behavior.cpp index 26c284bf..c70b34c7 100644 --- a/dGame/dBehaviors/Behavior.cpp +++ b/dGame/dBehaviors/Behavior.cpp @@ -64,6 +64,7 @@ #include "FallSpeedBehavior.h" #include "ChangeIdleFlagsBehavior.h" #include "DarkInspirationBehavior.h" +#include "ConsumeItemBehavior.h" //CDClient includes #include "CDBehaviorParameterTable.h" @@ -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; @@ -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(); } diff --git a/dGame/dBehaviors/BehaviorContext.cpp b/dGame/dBehaviors/BehaviorContext.cpp index d23ee596..e4f3011c 100644 --- a/dGame/dBehaviors/BehaviorContext.cpp +++ b/dGame/dBehaviors/BehaviorContext.cpp @@ -13,7 +13,7 @@ #include "DestroyableComponent.h" #include "EchoSyncSkill.h" #include "PhantomPhysicsComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "eReplicaComponentType.h" #include "TeamManager.h" #include "eConnectionType.h" @@ -249,7 +249,7 @@ 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; @@ -412,8 +412,8 @@ bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const { if (!target) return false; // ignore quickbuilds that aren't completed - auto* targetQuickbuildComponent = target->GetComponent(); - if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eRebuildState::COMPLETED) return false; + auto* targetQuickbuildComponent = target->GetComponent(); + if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eQuickBuildState::COMPLETED) return false; return true; } 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/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/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/ProjectileAttackBehavior.cpp b/dGame/dBehaviors/ProjectileAttackBehavior.cpp index c63a3d6f..732ff186 100644 --- a/dGame/dBehaviors/ProjectileAttackBehavior.cpp +++ b/dGame/dBehaviors/ProjectileAttackBehavior.cpp @@ -5,7 +5,7 @@ #include "Game.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) { @@ -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/SpawnBehavior.cpp b/dGame/dBehaviors/SpawnBehavior.cpp index 96430766..dd45fb1b 100644 --- a/dGame/dBehaviors/SpawnBehavior.cpp +++ b/dGame/dBehaviors/SpawnBehavior.cpp @@ -6,7 +6,7 @@ #include "Game.h" #include "Logger.h" #include "DestroyableComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Entity.h" #include "EntityInfo.h" #include "eReplicaComponentType.h" @@ -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); diff --git a/dGame/dBehaviors/SwitchMultipleBehavior.cpp b/dGame/dBehaviors/SwitchMultipleBehavior.cpp index 947c331f..faaa0d23 100644 --- a/dGame/dBehaviors/SwitchMultipleBehavior.cpp +++ b/dGame/dBehaviors/SwitchMultipleBehavior.cpp @@ -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 29cee327..0cea8213 100644 --- a/dGame/dBehaviors/TacArcBehavior.cpp +++ b/dGame/dBehaviors/TacArcBehavior.cpp @@ -6,7 +6,7 @@ #include "BehaviorContext.h" #include "BaseCombatAIComponent.h" #include "EntityManager.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include @@ -104,7 +104,7 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS const auto casterPosition = self->GetPosition(); - auto reference = self->GetPosition(); //+ m_offset; + auto reference = self->GetPosition() + m_offset; targets.clear(); @@ -114,46 +114,34 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS 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 (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) { - continue; - } - + if (targets.size() >= this->m_maxTargets) break; + if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) continue; if (validTarget->GetIsDead()) continue; - const auto otherPosition = validTarget->GetPosition(); + const auto targetPos = validTarget->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) - { + // 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; - }*/ 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_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) { targets.push_back(validTarget); } @@ -167,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 { @@ -214,8 +195,8 @@ void TacArcBehavior::Load() { GetFloat("offset_z", 0.0f) ); this->m_method = GetInt("method", 1); - this->m_upperBound = GetFloat("upper_bound", 4.4f); - this->m_lowerBound = GetFloat("lower_bound", 0.4f); + 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); diff --git a/dGame/dComponents/ScriptedActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp similarity index 79% rename from dGame/dComponents/ScriptedActivityComponent.cpp rename to dGame/dComponents/ActivityComponent.cpp index 838fe5ed..aa6a4604 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" @@ -7,7 +7,7 @@ #include "ZoneInstanceManager.h" #include "Game.h" #include "Logger.h" -#include +#include "WorldPackets.h" #include "EntityManager.h" #include "ChatPackets.h" #include "Player.h" @@ -29,45 +29,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 + // First lookup the loot matrix id for this component id. CDActivityRewardsTable* activityRewardsTable = CDClientManager::Instance().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 +70,48 @@ ScriptedActivityComponent::ScriptedActivityComponent(Entity* parent, int activit } } } +void ActivityComponent::LoadActivityData(const int32_t activityId) { + CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable(); + std::vector activities = activitiesTable->Query([activityId](CDActivities entry) {return (entry.ActivityID == activityId); }); -ScriptedActivityComponent::~ScriptedActivityComponent() -= default; - -void ScriptedActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { - 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()) { + GeneralUtils::TryParse(transferOverride, m_ActivityInfo.instanceMapID); } } } } -void ScriptedActivityComponent::ReloadConfig() { +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::Instance().GetTable(); - std::vector activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); }); + 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) { @@ -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) { @@ -562,7 +547,7 @@ 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); }); + std::vector activityRewards = activityRewardsTable->Query([this](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); }); if (!activityRewards.empty()) { uint32_t minCoins = 0; 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 1961e6d6..9cc92c26 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,7 +20,7 @@ #include #include "SkillComponent.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "DestroyableComponent.h" #include "Metrics.hpp" #include "CDComponentsRegistryTable.h" @@ -34,13 +34,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 +76,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(); @@ -243,12 +242,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; } } @@ -523,7 +522,7 @@ bool BaseCombatAIComponent::IsMech() { 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; } @@ -559,12 +558,12 @@ bool BaseCombatAIComponent::IsEnemy(LWOOBJID target) const { 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; } } diff --git a/dGame/dComponents/BaseCombatAIComponent.h b/dGame/dComponents/BaseCombatAIComponent.h index a08b008e..8ae04611 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 diff --git a/dGame/dComponents/BouncerComponent.cpp b/dGame/dComponents/BouncerComponent.cpp index 56002ac4..78ee3637 100644 --- a/dGame/dComponents/BouncerComponent.cpp +++ b/dGame/dComponents/BouncerComponent.cpp @@ -6,7 +6,7 @@ #include "Game.h" #include "Logger.h" #include "GameMessages.h" -#include +#include "BitStream.h" #include "eTriggerEventType.h" BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) { diff --git a/dGame/dComponents/BuffComponent.cpp b/dGame/dComponents/BuffComponent.cpp index 24124f6e..10ac4ebb 100644 --- a/dGame/dComponents/BuffComponent.cpp +++ b/dGame/dComponents/BuffComponent.cpp @@ -1,5 +1,5 @@ #include "BuffComponent.h" -#include +#include "BitStream.h" #include "CDClientDatabase.h" #include #include "DestroyableComponent.h" @@ -11,9 +11,21 @@ #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) { } @@ -22,32 +34,38 @@ BuffComponent::~BuffComponent() { 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; @@ -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, ',')) { diff --git a/dGame/dComponents/BuffComponent.h b/dGame/dComponents/BuffComponent.h index 5b6f8fd6..7f7b44d8 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,6 +33,15 @@ 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; }; /** @@ -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/CMakeLists.txt b/dGame/dComponents/CMakeLists.txt index b1550f37..59c49ce9 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -1,4 +1,6 @@ -set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" +set(DGAME_DCOMPONENTS_SOURCES + "ActivityComponent.cpp" + "BaseCombatAIComponent.cpp" "BouncerComponent.cpp" "BuffComponent.cpp" "BuildBorderComponent.cpp" @@ -8,6 +10,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "ControllablePhysicsComponent.cpp" "DestroyableComponent.cpp" "DonationVendorComponent.cpp" + "GhostComponent.cpp" "InventoryComponent.cpp" "ItemComponent.cpp" "LevelProgressionComponent.cpp" @@ -31,20 +34,25 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp" "ProximityMonitorComponent.cpp" "RacingControlComponent.cpp" "RailActivatorComponent.cpp" - "RebuildComponent.cpp" + "QuickBuildComponent.cpp" "RenderComponent.cpp" "RigidbodyPhantomPhysicsComponent.cpp" "MultiZoneEntranceComponent.cpp" "RocketLaunchpadControlComponent.cpp" - "ScriptedActivityComponent.cpp" "ShootingGalleryComponent.cpp" "SimplePhysicsComponent.cpp" "SkillComponent.cpp" "SoundTriggerComponent.cpp" "SwitchComponent.cpp" "TriggerComponent.cpp" - "VehiclePhysicsComponent.cpp" + "HavokVehiclePhysicsComponent.cpp" "VendorComponent.cpp" - "ZoneControlComponent.cpp" - PARENT_SCOPE + "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 7a30d821..70eec0cf 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -1,5 +1,5 @@ #include "CharacterComponent.h" -#include +#include "BitStream.h" #include "tinyxml2.h" #include "Game.h" #include "Logger.h" @@ -10,12 +10,18 @@ #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) { @@ -74,10 +80,14 @@ CharacterComponent::~CharacterComponent() { 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()); @@ -127,7 +137,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); } @@ -149,7 +159,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); @@ -186,6 +196,13 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { 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 @@ -214,7 +231,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(); } @@ -308,6 +325,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { 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()); @@ -478,7 +500,7 @@ void CharacterComponent::TrackArmorDelta(int32_t armor) { } } -void CharacterComponent::TrackRebuildComplete() { +void CharacterComponent::TrackQuickBuildComplete() { UpdatePlayerStatistic(QuickBuildsCompleted); const auto mapID = Game::zoneManager->GetZoneID().GetMapID(); @@ -495,9 +517,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)); } } @@ -700,7 +722,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) { @@ -738,3 +760,60 @@ 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) return; + auto* user = m_Parent->GetParentUser(); + if (!user) return; + + auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID()); + if (rewardCodes.empty()) return; + + auto* cdrewardCodes = CDClientManager::Instance().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); + }); +} diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 5bafb3df..fae99dcb 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -10,6 +10,7 @@ #include "CDMissionsTable.h" #include "tinyxml2.h" #include "eReplicaComponentType.h" +#include enum class eGameActivity : uint32_t; @@ -232,7 +233,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 +281,14 @@ 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; + /** * Character info regarding this character, including clothing styles, etc. */ @@ -566,6 +575,10 @@ private: LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY; + + std::array m_ClaimCodes{}; + + void AwardClaimCodes(); }; #endif // CHARACTERCOMPONENT_H diff --git a/dGame/dComponents/Component.h b/dGame/dComponents/Component.h index c0debb0b..d1ad0860 100644 --- a/dGame/dComponents/Component.h +++ b/dGame/dComponents/Component.h @@ -1,6 +1,6 @@ #pragma once -#include "../thirdparty/tinyxml2/tinyxml2.h" +#include "tinyxml2.h" class Entity; diff --git a/dGame/dComponents/DestroyableComponent.cpp b/dGame/dComponents/DestroyableComponent.cpp index e98bc33b..64dca4f1 100644 --- a/dGame/dComponents/DestroyableComponent.cpp +++ b/dGame/dComponents/DestroyableComponent.cpp @@ -1,5 +1,5 @@ #include "DestroyableComponent.h" -#include +#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" @@ -85,11 +85,11 @@ void DestroyableComponent::Reinitialize(LOT templateID) { 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(); @@ -154,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]); } @@ -380,7 +380,7 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore 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(); @@ -557,7 +557,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32 } if (IsImmune() || IsCooldownImmune()) { - LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); //Immune is succesfully proc'd + LOG_DEBUG("Target targetEntity %llu is immune!", m_Parent->GetObjectID()); return; } @@ -796,7 +796,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) { diff --git a/dGame/dComponents/DonationVendorComponent.cpp b/dGame/dComponents/DonationVendorComponent.cpp index 7f85ea97..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); } diff --git a/dGame/dComponents/GhostComponent.cpp b/dGame/dComponents/GhostComponent.cpp new file mode 100644 index 00000000..2978c912 --- /dev/null +++ b/dGame/dComponents/GhostComponent.cpp @@ -0,0 +1,57 @@ +#include "GhostComponent.h" + +GhostComponent::GhostComponent(Entity* parent) : Component(parent) { + m_GhostReferencePoint = NiPoint3::ZERO; + m_GhostOverridePoint = NiPoint3::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..5ae308a4 --- /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 : 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 77% rename from dGame/dComponents/VehiclePhysicsComponent.cpp rename to dGame/dComponents/HavokVehiclePhysicsComponent.cpp index 0e93782d..62e49a56 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.cpp +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.cpp @@ -1,7 +1,7 @@ -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "EntityManager.h" -VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) { +HavokVehiclePhysicsComponent::HavokVehiclePhysicsComponent(Entity* parent) : PhysicsComponent(parent) { m_Velocity = NiPoint3::ZERO; m_AngularVelocity = NiPoint3::ZERO; m_IsOnGround = true; @@ -12,45 +12,45 @@ VehiclePhysicsComponent::VehiclePhysicsComponent(Entity* parent) : PhysicsCompon m_EndBehavior = GeneralUtils::GenerateRandomNumber(0, 7); } -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::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) { +void HavokVehiclePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_DirtyPosition); if (bIsInitialUpdate || m_DirtyPosition) { @@ -111,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 72% rename from dGame/dComponents/VehiclePhysicsComponent.h rename to dGame/dComponents/HavokVehiclePhysicsComponent.h index 69f8579c..ba46f093 100644 --- a/dGame/dComponents/VehiclePhysicsComponent.h +++ b/dGame/dComponents/HavokVehiclePhysicsComponent.h @@ -4,33 +4,16 @@ #include "Entity.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 PhysicsComponent { +class HavokVehiclePhysicsComponent : public PhysicsComponent { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::VEHICLE_PHYSICS; + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::HAVOK_VEHICLE_PHYSICS; - VehiclePhysicsComponent(Entity* parentEntity); + HavokVehiclePhysicsComponent(Entity* parentEntity); void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override; diff --git a/dGame/dComponents/InventoryComponent.cpp b/dGame/dComponents/InventoryComponent.cpp index 7a7ca20a..23d5c163 100644 --- a/dGame/dComponents/InventoryComponent.cpp +++ b/dGame/dComponents/InventoryComponent.cpp @@ -7,7 +7,7 @@ #include "Game.h" #include "Logger.h" #include "CDClientManager.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "MissionComponent.h" #include "GameMessages.h" #include "SkillComponent.h" @@ -19,7 +19,7 @@ #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 +31,7 @@ #include "eMissionTaskType.h" #include "eStateChangeType.h" #include "eUseItemResponse.h" +#include "Mail.h" #include "CDComponentsRegistryTable.h" #include "CDInventoryComponentTable.h" @@ -68,7 +69,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 +87,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++ }); @@ -264,17 +265,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 +295,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) { LOG("Attempted to remove 0 of item (%i) from the inventory!", lot); - - return; + 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) { @@ -993,7 +976,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 @@ -1353,7 +1336,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); diff --git a/dGame/dComponents/InventoryComponent.h b/dGame/dComponents/InventoryComponent.h index e818d2cb..f4d38d43 100644 --- a/dGame/dComponents/InventoryComponent.h +++ b/dGame/dComponents/InventoryComponent.h @@ -118,8 +118,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 diff --git a/dGame/dComponents/LUPExhibitComponent.cpp b/dGame/dComponents/LUPExhibitComponent.cpp index 151f2897..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) { - outBitStream->Write1(); // Dirty flag? - outBitStream->Write(m_Exhibit); + 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 646d361c..47b13a17 100644 --- a/dGame/dComponents/LUPExhibitComponent.h +++ b/dGame/dComponents/LUPExhibitComponent.h @@ -3,6 +3,9 @@ #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 @@ -11,35 +14,15 @@ class LUPExhibitComponent : public Component { public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::EXHIBIT; + inline static const 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) override; - - /** - * After the timer runs out, this changes the currently exhibited LOT to the next one - */ - void NextExhibit(); + 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/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..06a9c24e --- /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: + inline static const 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 d7da29d9..7219589a 100644 --- a/dGame/dComponents/MissionComponent.cpp +++ b/dGame/dComponents/MissionComponent.cpp @@ -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) { @@ -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,7 +318,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, } } - return any; + return acceptedAchievements; #else auto* missionTasksTable = CDClientManager::Instance().GetTable(); auto* missionsTable = CDClientManager::Instance().GetTable(); @@ -326,7 +327,7 @@ bool MissionComponent::LookForAchievements(eMissionTaskType type, int32_t value, return entry.taskType == static_cast(type); }); - auto any = false; + std::vector acceptedAchievements; for (const auto& task : tasks) { if (GetMission(task.id) != nullptr) { @@ -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 } @@ -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 e82b5b67..d53db64e 100644 --- a/dGame/dComponents/MissionComponent.h +++ b/dGame/dComponents/MissionComponent.h @@ -141,7 +141,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 +170,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 9299ebd7..25d4a739 100644 --- a/dGame/dComponents/MissionOfferComponent.cpp +++ b/dGame/dComponents/MissionOfferComponent.cpp @@ -29,11 +29,11 @@ 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; } @@ -55,54 +55,39 @@ MissionOfferComponent::MissionOfferComponent(Entity* parent, const LOT parentLot }); 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) { 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); @@ -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 ad5f83bc..0f0d23ba 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: @@ -64,7 +64,6 @@ public: inline static const 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 ccdad281..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(); @@ -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 dd9984e7..a36328be 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,7 +9,15 @@ #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. @@ -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 9089c14c..5b7042df 100644 --- a/dGame/dComponents/ModuleAssemblyComponent.cpp +++ b/dGame/dComponents/ModuleAssemblyComponent.cpp @@ -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/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index a95735fd..366bebd3 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -35,7 +35,7 @@ MoverSubComponent::~MoverSubComponent() = default; void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(true); - outBitStream->Write(static_cast(mState)); + outBitStream->Write(mState); outBitStream->Write(mDesiredWaypointIndex); outBitStream->Write(mShouldStopAtDesiredWaypoint); outBitStream->Write(mInReverse); @@ -90,9 +90,9 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI // Is on rail outBitStream->Write1(); - outBitStream->Write(static_cast(m_PathName.size())); + outBitStream->Write(m_PathName.size()); for (const auto& c : m_PathName) { - outBitStream->Write(static_cast(c)); + outBitStream->Write(c); } // Starting point @@ -107,7 +107,7 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI if (hasPlatform) { auto* mover = static_cast(m_MoverSubComponent); - outBitStream->Write(static_cast(m_MoverSubComponentType)); + outBitStream->Write(m_MoverSubComponentType); if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) { // TODO @@ -117,11 +117,11 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI } } -void MovingPlatformComponent::OnRebuildInitilized() { +void MovingPlatformComponent::OnQuickBuildInitilized() { StopPathing(); } -void MovingPlatformComponent::OnCompleteRebuild() { +void MovingPlatformComponent::OnCompleteQuickBuild() { if (m_NoAutoStart) return; diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index e8e1dac7..bb5821d4 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -116,12 +116,12 @@ public: /** * 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/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index df473055..bddfd009 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" @@ -69,7 +69,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::Instance().GetTable()->GetByID(componentId); // TODO: Make reference when safe m_ComponentId = componentId; m_Interaction = LWOOBJID_EMPTY; @@ -81,31 +82,17 @@ 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_Ability = ePetAbilityType::Invalid; m_StartPosition = NiPoint3::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) { @@ -113,8 +100,8 @@ void PetComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpd 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; @@ -136,12 +123,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); } @@ -190,7 +177,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(); @@ -562,7 +549,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); @@ -835,11 +822,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) { @@ -905,8 +892,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) { @@ -928,22 +913,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) { 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); }); } @@ -966,7 +951,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() { @@ -985,12 +970,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 @@ -1030,7 +1012,7 @@ uint32_t PetComponent::GetStatus() const { return m_Status; } -PetAbilityType PetComponent::GetAbility() const { +ePetAbilityType PetComponent::GetAbility() const { return m_Ability; } @@ -1042,7 +1024,7 @@ void PetComponent::SetStatus(uint32_t value) { m_Status = value; } -void PetComponent::SetAbility(PetAbilityType value) { +void PetComponent::SetAbility(ePetAbilityType value) { m_Ability = value; } @@ -1097,37 +1079,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 980bb146..24ee157b 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -1,18 +1,13 @@ -#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 @@ -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 e0a76f67..6248bfe9 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -402,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; diff --git a/dGame/dComponents/PropertyEntranceComponent.cpp b/dGame/dComponents/PropertyEntranceComponent.cpp index d8b3eb95..2bb6ea30 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" @@ -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); @@ -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/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 63c321f6..dff1e5da 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -13,13 +13,15 @@ #include "Game.h" #include "Item.h" #include "Database.h" -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "Player.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 +41,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 +56,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 +94,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(); @@ -152,14 +144,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 +160,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); } @@ -217,28 +207,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) { - LOG("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 +228,7 @@ void PropertyManagementComponent::OnStartBuilding() { if (ownerEntity == nullptr) return; - const auto players = Player::GetAllPlayers(); + const auto players = PlayerManager::GetAllPlayers(); LWOMAPID zoneId = 1100; @@ -273,7 +249,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(); @@ -360,7 +337,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; @@ -423,6 +400,25 @@ 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()) { @@ -441,14 +437,6 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet LOG("Failed to find spawner"); } - auto* model = Game::entityManager->GetEntity(id); - - if (model == nullptr) { - LOG("Failed to find model entity"); - - return; - } - Game::entityManager->DestructEntity(model); LOG("Deleting model LOT %i", model->GetLOT()); @@ -534,7 +522,7 @@ void PropertyManagementComponent::DeleteModel(const LWOOBJID id, const int delet { item->SetCount(item->GetCount() - 1); - LOG("YES IT GOES HERE"); + LOG("DLU currently does not support breaking apart brick by brick models."); break; } @@ -558,14 +546,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 +560,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 +580,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 +594,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 +603,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 +625,6 @@ void PropertyManagementComponent::Load() { models.insert_or_assign(model->GetObjectID(), spawnerId); } - - delete lookup; } void PropertyManagementComponent::Save() { @@ -669,27 +632,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) { - LOG("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 +651,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) { - LOG("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) { - LOG("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) { - LOG("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,6 +688,7 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const const auto& worldId = Game::zoneManager->GetZone()->GetZoneID(); const auto zoneId = worldId.GetMapID(); + const auto cloneId = worldId.GetCloneID(); LOG("Getting property info for %d", zoneId); GameMessages::PropertyDataMessage message = GameMessages::PropertyDataMessage(zoneId); @@ -795,45 +696,25 @@ void PropertyManagementComponent::OnQueryPropertyData(Entity* originator, const 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 2f2deea4..d38437c4 100644 --- a/dGame/dComponents/PropertyManagementComponent.h +++ b/dGame/dComponents/PropertyManagementComponent.h @@ -164,6 +164,8 @@ public: LWOCLONEID GetCloneId() { return clone_Id; }; + LWOOBJID GetId() const noexcept { return propertyId; } + private: /** * This diff --git a/dGame/dComponents/RebuildComponent.cpp b/dGame/dComponents/QuickBuildComponent.cpp similarity index 68% rename from dGame/dComponents/RebuildComponent.cpp rename to dGame/dComponents/QuickBuildComponent.cpp index 5359dee0..b8aec2bd 100644 --- a/dGame/dComponents/RebuildComponent.cpp +++ b/dGame/dComponents/QuickBuildComponent.cpp @@ -1,4 +1,4 @@ -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Entity.h" #include "DestroyableComponent.h" #include "GameMessages.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* entity) : Component(entity) { std::u16string checkPreconditions = entity->GetVar(u"CheckPrecondition"); if (!checkPreconditions.empty()) { @@ -46,18 +45,18 @@ RebuildComponent::RebuildComponent(Entity* entity) : Component(entity) { 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) { +void QuickBuildComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (m_Parent->GetComponent(eReplicaComponentType::DESTROYABLE) == nullptr) { if (bIsInitialUpdate) { outBitStream->Write(false); @@ -70,7 +69,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 +80,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 +109,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 +123,7 @@ void RebuildComponent::Update(float deltaTime) { }*/ switch (m_State) { - case eRebuildState::OPEN: { + case eQuickBuildState::OPEN: { SpawnActivator(); m_TimeBeforeDrain = 0; @@ -147,14 +146,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 +170,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 +196,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 +231,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,10 +249,10 @@ void RebuildComponent::OnUse(Entity* originator) { return; } - StartRebuild(originator); + StartQuickBuild(originator); } -void RebuildComponent::SpawnActivator() { +void QuickBuildComponent::SpawnActivator() { if (!m_SelfActivator || m_ActivatorPosition != NiPoint3::ZERO) { if (!m_Activator) { EntityInfo info; @@ -274,7 +272,7 @@ void RebuildComponent::SpawnActivator() { } } -void RebuildComponent::DespawnActivator() { +void QuickBuildComponent::DespawnActivator() { if (m_Activator) { Game::entityManager->DestructEntity(m_Activator); @@ -286,73 +284,73 @@ void RebuildComponent::DespawnActivator() { } } -Entity* RebuildComponent::GetActivator() { +Entity* QuickBuildComponent::GetActivator() { return Game::entityManager->GetEntity(m_ActivatorId); } -NiPoint3 RebuildComponent::GetActivatorPosition() { +NiPoint3 QuickBuildComponent::GetActivatorPosition() { return m_ActivatorPosition; } -float RebuildComponent::GetResetTime() { +float QuickBuildComponent::GetResetTime() { return m_ResetTime; } -float RebuildComponent::GetCompleteTime() { +float QuickBuildComponent::GetCompleteTime() { return m_CompleteTime; } -int RebuildComponent::GetTakeImagination() { +int QuickBuildComponent::GetTakeImagination() { return m_TakeImagination; } -bool RebuildComponent::GetInterruptible() { +bool QuickBuildComponent::GetInterruptible() { return m_Interruptible; } -bool RebuildComponent::GetSelfActivator() { +bool QuickBuildComponent::GetSelfActivator() { return m_SelfActivator; } -std::vector RebuildComponent::GetCustomModules() { +std::vector QuickBuildComponent::GetCustomModules() { return m_CustomModules; } -int RebuildComponent::GetActivityId() { +int QuickBuildComponent::GetActivityId() { return m_ActivityId; } -int RebuildComponent::GetPostImaginationCost() { +int QuickBuildComponent::GetPostImaginationCost() { return m_PostImaginationCost; } -float RebuildComponent::GetTimeBeforeSmash() { +float QuickBuildComponent::GetTimeBeforeSmash() { return m_TimeBeforeSmash; } -eRebuildState RebuildComponent::GetState() { +eQuickBuildState QuickBuildComponent::GetState() { return m_State; } -Entity* RebuildComponent::GetBuilder() const { +Entity* QuickBuildComponent::GetBuilder() const { auto* builder = Game::entityManager->GetEntity(m_Builder); return builder; } -bool RebuildComponent::GetRepositionPlayer() const { +bool QuickBuildComponent::GetRepositionPlayer() const { return m_RepositionPlayer; } -void RebuildComponent::SetActivatorPosition(NiPoint3 value) { +void QuickBuildComponent::SetActivatorPosition(NiPoint3 value) { m_ActivatorPosition = value; } -void RebuildComponent::SetResetTime(float value) { +void QuickBuildComponent::SetResetTime(float value) { m_ResetTime = value; } -void RebuildComponent::SetCompleteTime(float value) { +void QuickBuildComponent::SetCompleteTime(float value) { if (value < 0) { m_CompleteTime = 4.5f; } else { @@ -360,31 +358,31 @@ void RebuildComponent::SetCompleteTime(float value) { } } -void RebuildComponent::SetTakeImagination(int value) { +void QuickBuildComponent::SetTakeImagination(int value) { m_TakeImagination = value; } -void RebuildComponent::SetInterruptible(bool value) { +void QuickBuildComponent::SetInterruptible(bool value) { m_Interruptible = value; } -void RebuildComponent::SetSelfActivator(bool value) { +void QuickBuildComponent::SetSelfActivator(bool value) { m_SelfActivator = value; } -void RebuildComponent::SetCustomModules(std::vector value) { +void QuickBuildComponent::SetCustomModules(std::vector value) { m_CustomModules = value; } -void RebuildComponent::SetActivityId(int value) { +void QuickBuildComponent::SetActivityId(int value) { m_ActivityId = value; } -void RebuildComponent::SetPostImaginationCost(int value) { +void QuickBuildComponent::SetPostImaginationCost(int value) { m_PostImaginationCost = value; } -void RebuildComponent::SetTimeBeforeSmash(float value) { +void QuickBuildComponent::SetTimeBeforeSmash(float value) { if (value < 0) { m_TimeBeforeSmash = 10.0f; } else { @@ -392,12 +390,12 @@ void RebuildComponent::SetTimeBeforeSmash(float value) { } } -void RebuildComponent::SetRepositionPlayer(bool value) { +void QuickBuildComponent::SetRepositionPlayer(bool value) { 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* user) { + if (m_State == eQuickBuildState::OPEN || m_State == eQuickBuildState::COMPLETED || m_State == eQuickBuildState::INCOMPLETE) { m_Builder = user->GetObjectID(); auto* character = user->GetComponent(); @@ -405,31 +403,31 @@ 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) { +void QuickBuildComponent::CompleteQuickBuild(Entity* user) { if (user == nullptr) { return; } @@ -437,7 +435,7 @@ void RebuildComponent::CompleteRebuild(Entity* user) { auto* characterComponent = user->GetComponent(); if (characterComponent != nullptr) { characterComponent->SetCurrentActivity(eGameActivity::NONE); - characterComponent->TrackRebuildComplete(); + characterComponent->TrackQuickBuildComplete(); } else { LOG("Some user tried to finish the rebuild but they didn't have a character somehow."); return; @@ -445,13 +443,13 @@ void RebuildComponent::CompleteRebuild(Entity* user) { 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; @@ -491,21 +489,21 @@ void RebuildComponent::CompleteRebuild(Entity* user) { // 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 +519,20 @@ void RebuildComponent::CompleteRebuild(Entity* user) { RenderComponent::PlayAnimation(user, u"rebuild-celebrate", 1.09f); } -void RebuildComponent::ResetRebuild(bool failed) { +void QuickBuildComponent::ResetQuickBuild(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 +543,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,29 +554,29 @@ void RebuildComponent::ResetRebuild(bool failed) { } } -void RebuildComponent::CancelRebuild(Entity* entity, eQuickBuildFailReason failReason, bool skipChecks) { - if (m_State != eRebuildState::COMPLETED || skipChecks) { +void QuickBuildComponent::CancelQuickBuild(Entity* entity, eQuickBuildFailReason failReason, 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); @@ -595,10 +593,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..f1106a61 --- /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 : public Component { +public: + inline static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; + + QuickBuildComponent(Entity* 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 + */ + Entity* GetActivator(); + + /** + * Returns the spawn position of the activator for this quickbuild, if any + * @return the spawn position of the activator for this quickbuild, if any + */ + NiPoint3 GetActivatorPosition(); + + /** + * Sets the spawn position for the activator of this quickbuild + * @param value the spawn position to set for the activator + */ + void SetActivatorPosition(NiPoint3 value); + + /** + * 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 + */ + float GetResetTime(); + + /** + * Sets the time it takes for the quickbuild to reset after being built + * @param value the reset time to set + */ + void SetResetTime(float value); + + /** + * Returns the time it takes to complete the quickbuild + * @return the time it takes to complete the quickbuild + */ + float GetCompleteTime(); + + /** + * Sets the time it takes to complete the quickbuild + * @param value the completion time to set + */ + void SetCompleteTime(float value); + + /** + * Returns the imagination that's taken when completing the quickbuild + * @return the imagination that's taken when completing the quickbuild + */ + int GetTakeImagination(); + + /** + * Sets the imagination that's taken when completing the quickbuild + * @param value the imagination deduction to set + */ + void SetTakeImagination(int value); + + /** + * Returns if the quickbuild can be interrupted, currently unused + * @return if the quickbuild can be interrupted + */ + bool GetInterruptible(); + + /** + * Sets whether or not the quickbuild can be interrupted, currently unused + * @param value true if the quickbuild 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 quickbuild. + * @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 quickbuild + * @return the activity ID for participating in this quickbuild + */ + int GetActivityId(); + + /** + * Sets the activity ID for participating in this quickbuild + * @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 quickbuild to be smashed automatically + * @return the time it takes for an incomplete quickbuild to be smashed automatically + */ + float GetTimeBeforeSmash(); + + /** + * Sets the time it takes for an incomplete quickbuild to be smashed automatically + * @param value the time to set + */ + void SetTimeBeforeSmash(float value); + + /** + * Returns the current quickbuild state + * @return the current quickbuild state + */ + eQuickBuildState GetState(); + + /** + * Returns the player that is currently building this quickbuild + * @return the player that is currently building this quickbuild + */ + 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 + */ + bool GetRepositionPlayer() const; + + /** + * 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(bool value); + + /** + * 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(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* 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 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 = NiPoint3::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 + */ + int 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 + */ + int m_ActivityId = 0; + + /** + * Currently unused + */ + int 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 + */ + 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 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* user); + + /** + * Completes the quickbuild for an entity, dropping loot and despawning the activator + * @param user the entity that completed the quickbuild + */ + void CompleteQuickBuild(Entity* user); +}; + +#endif // QUICKBUILDCOMPONENT_H diff --git a/dGame/dComponents/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index e5f2dbe1..5e3d81f4 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -17,7 +17,7 @@ #include "PossessorComponent.h" #include "eRacingTaskParam.h" #include "Spawner.h" -#include "VehiclePhysicsComponent.h" +#include "HavokVehiclePhysicsComponent.h" #include "dServer.h" #include "dZoneManager.h" #include "dConfig.h" @@ -51,7 +51,7 @@ 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(); @@ -71,10 +71,8 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) { // If the race has already started, send the player back to the main world. if (m_Loaded || !vehicle) { - auto* playerInstance = dynamic_cast(player); - if(playerInstance){ - playerInstance->SendToZone(m_MainWorld); - } + auto* characterComponent = player->GetComponent(); + if (characterComponent) characterComponent->SendToZone(m_MainWorld); return; } @@ -105,13 +103,14 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, if (item == nullptr) { LOG("Failed to find item"); - auto* playerInstance = dynamic_cast(player); - if(playerInstance){ + auto* characterComponent = player->GetComponent(); + + if (characterComponent) { m_LoadedPlayers--; - playerInstance->SendToZone(m_MainWorld); + characterComponent->SendToZone(m_MainWorld); } return; - + } // Calculate the vehicle's starting position. @@ -213,6 +212,7 @@ void RacingControlComponent::LoadPlayerVehicle(Entity* player, 0, 0, 0 }); + m_AllPlayersReady = false; } // Construct and serialize everything when done. @@ -311,7 +311,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, @@ -330,7 +330,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) { @@ -384,11 +384,11 @@ 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; + const auto score = playersRating * 10 + data->finished; Loot::GiveActivityLoot(player, m_Parent, m_ActivityID, score); // Giving rewards @@ -400,18 +400,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) { @@ -426,9 +426,9 @@ 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(); } @@ -436,64 +436,82 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu 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) { @@ -542,9 +560,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(); @@ -569,7 +587,7 @@ void RacingControlComponent::Update(float deltaTime) { LoadPlayerVehicle(player, positionNumber + 1, true); - m_Loaded = true; + Game::entityManager->SerializeEntity(m_Parent); } m_Loaded = true; @@ -604,9 +622,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; @@ -757,6 +775,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; @@ -773,7 +793,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; @@ -827,7 +847,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++; @@ -843,14 +863,12 @@ void RacingControlComponent::Update(float deltaTime) { 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. } } @@ -865,28 +883,3 @@ void RacingControlComponent::Update(float deltaTime) { } } } - -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 3dcb730e..47341aae 100644 --- a/dGame/dComponents/RacingControlComponent.h +++ b/dGame/dComponents/RacingControlComponent.h @@ -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/RailActivatorComponent.cpp b/dGame/dComponents/RailActivatorComponent.cpp index 5662303b..e0eb035a 100644 --- a/dGame/dComponents/RailActivatorComponent.cpp +++ b/dGame/dComponents/RailActivatorComponent.cpp @@ -4,7 +4,7 @@ #include "CDRailActivatorComponent.h" #include "Entity.h" #include "GameMessages.h" -#include "RebuildComponent.h" +#include "QuickBuildComponent.h" #include "Game.h" #include "Logger.h" #include "RenderComponent.h" @@ -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/RebuildComponent.h b/dGame/dComponents/RebuildComponent.h deleted file mode 100644 index bb097edd..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: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::QUICK_BUILD; - - RebuildComponent(Entity* entity); - ~RebuildComponent() 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 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 a81a3458..1cc9e75e 100644 --- a/dGame/dComponents/RenderComponent.cpp +++ b/dGame/dComponents/RenderComponent.cpp @@ -5,7 +5,6 @@ #include #include "Entity.h" -#include "PacketUtils.h" #include "CDClientManager.h" #include "GameMessages.h" diff --git a/dGame/dComponents/RenderComponent.h b/dGame/dComponents/RenderComponent.h index a159f1cc..adff3c1e 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 diff --git a/dGame/dComponents/ScriptedActivityComponent.h b/dGame/dComponents/ScriptedActivityComponent.h index 3bc8114f..79a9593d 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 { -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 ScriptedActivityComponent : public Component { +class ScriptedActivityComponent : public ActivityComponent { public: inline 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) 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; - - /** - * 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; + ScriptedActivityComponent(Entity* parent, int activityID) : ActivityComponent(parent, activityID){}; }; -#endif // SCRIPTEDACTIVITYCOMPONENT_H +#endif //!__SCRIPTEDACTIVITYCOMPONENT__H__ diff --git a/dGame/dComponents/ShootingGalleryComponent.cpp b/dGame/dComponents/ShootingGalleryComponent.cpp index 92208598..45cd9342 100644 --- a/dGame/dComponents/ShootingGalleryComponent.cpp +++ b/dGame/dComponents/ShootingGalleryComponent.cpp @@ -24,6 +24,7 @@ void ShootingGalleryComponent::Serialize(RakNet::BitStream* outBitStream, bool i 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/SkillComponent.cpp b/dGame/dComponents/SkillComponent.cpp index 7762345e..b65c7d21 100644 --- a/dGame/dComponents/SkillComponent.cpp +++ b/dGame/dComponents/SkillComponent.cpp @@ -89,7 +89,7 @@ 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(); @@ -299,7 +299,7 @@ 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; @@ -409,7 +409,7 @@ 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()) { @@ -430,7 +430,7 @@ 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; diff --git a/dGame/dComponents/SwitchComponent.cpp b/dGame/dComponents/SwitchComponent.cpp index 41dff9f5..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() { @@ -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 25f694ba..7f2c3498 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" @@ -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 f7ef7c54..6d7ce3dd 100644 --- a/dGame/dComponents/TriggerComponent.cpp +++ b/dGame/dComponents/TriggerComponent.cpp @@ -10,9 +10,10 @@ #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) { @@ -175,7 +176,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*/ } @@ -205,12 +206,12 @@ void TriggerComponent::HandleToggleTrigger(Entity* targetEntity, std::string arg } void TriggerComponent::HandleResetRebuild(Entity* targetEntity, std::string args){ - auto* rebuildComponent = targetEntity->GetComponent(); - if (!rebuildComponent) { + 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){ diff --git a/dGame/dComponents/ZoneControlComponent.cpp b/dGame/dComponents/ZoneControlComponent.cpp deleted file mode 100644 index ca9c6626..00000000 --- a/dGame/dComponents/ZoneControlComponent.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "ZoneControlComponent.h" - -void ZoneControlComponent::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) { - outBitStream->Write(0x40000000); -} diff --git a/dGame/dComponents/ZoneControlComponent.h b/dGame/dComponents/ZoneControlComponent.h deleted file mode 100644 index ba235a80..00000000 --- a/dGame/dComponents/ZoneControlComponent.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef __ZONECONTROLCOMPONENT__H__ -#define __ZONECONTROLCOMPONENT__H__ - -#include "Component.h" -#include "eReplicaComponentType.h" - -class ZoneControlComponent final : public Component { -public: - inline static const eReplicaComponentType ComponentType = eReplicaComponentType::ZONE_CONTROL; - - ZoneControlComponent(Entity* parent) : Component(parent) {} - void Serialize(RakNet::BitStream* outBitStream, bool isConstruction); -}; - -#endif //!__ZONECONTROLCOMPONENT__H__ 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/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 9a50a746..fa11c086 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -7,7 +7,7 @@ #include "MissionComponent.h" #include "BitStreamUtils.h" #include "dServer.h" -#include "../thirdparty/raknet/Source/RakNetworkFactory.h" +#include "RakNetworkFactory.h" #include #include "User.h" #include "UserManager.h" @@ -34,8 +34,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 +50,11 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System User* usr = UserManager::Instance()->GetUser(sysAddr); if (!entity) { - LOG("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) LOG_DEBUG("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 +109,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 +138,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); } } } @@ -173,6 +176,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System 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 +197,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 +254,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 +270,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); @@ -294,7 +297,7 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System 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(); @@ -339,24 +342,19 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System RakNet::BitStream bitStreamLocal; 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 +423,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 +691,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: - LOG_DEBUG("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 821d80be..aed3d496 100644 --- a/dGame/dGameMessages/GameMessageHandler.h +++ b/dGame/dGameMessages/GameMessageHandler.h @@ -18,9 +18,8 @@ #include "Game.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 9d120d1c..c91b7107 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -1,7 +1,6 @@ #include "GameMessages.h" #include "User.h" #include "Entity.h" -#include "PacketUtils.h" #include "BitStreamUtils.h" #include "BitStream.h" #include "Game.h" @@ -14,7 +13,7 @@ #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" @@ -43,13 +42,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" @@ -62,7 +62,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" @@ -70,7 +70,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" @@ -78,6 +78,7 @@ #include "RailActivatorComponent.h" #include "LevelProgressionComponent.h" #include "DonationVendorComponent.h" +#include "GhostComponent.h" // Message includes: #include "dZoneManager.h" @@ -94,7 +95,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" @@ -112,7 +115,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(); @@ -228,13 +231,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); @@ -325,17 +328,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]); } - bitStream.Write(uint32_t(0)); + bitStream.Write(0); bitStream.Write0(); bitStream.Write0(); @@ -352,6 +355,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, bool bStopAtDesiredWaypoint, int iIndex, int iDesiredWaypointIndex, int nextIndex, eMovementPlatformState movementState) { @@ -369,7 +385,7 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd } bitStream.Write(entity->GetObjectID()); - bitStream.Write((uint16_t)eGameMessageType::PLATFORM_RESYNC); + bitStream.Write(eGameMessageType::PLATFORM_RESYNC); bool bReverse = false; int eCommand = 0; @@ -383,7 +399,7 @@ void GameMessages::SendPlatformResync(Entity* entity, const SystemAddress& sysAd bitStream.Write(bReverse); bitStream.Write(bStopAtDesiredWaypoint); bitStream.Write(eCommand); - bitStream.Write(static_cast(movementState)); + bitStream.Write(static_cast(movementState)); bitStream.Write(eUnexpectedCommand); bitStream.Write(fIdleTimeElapsed); bitStream.Write(fMoveTimeElapsed); @@ -426,7 +442,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; } @@ -435,7 +451,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; @@ -466,9 +482,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()); @@ -505,7 +521,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); @@ -517,7 +533,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 @@ -572,11 +588,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]); @@ -590,7 +606,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); @@ -604,14 +620,14 @@ 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; @@ -623,14 +639,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; @@ -641,11 +657,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); @@ -662,7 +678,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); @@ -710,22 +726,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; @@ -761,12 +777,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); @@ -775,12 +791,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); @@ -800,7 +816,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); @@ -836,7 +852,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); @@ -847,7 +863,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); @@ -951,14 +967,7 @@ void GameMessages::SendResurrect(Entity* entity) { destroyableComponent->SetImagination(imaginationToRestore); } } - }); - - - auto cont = static_cast(entity->GetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS)); - if (cont && entity->GetLOT() == 1) { - cont->SetPosition(entity->GetRespawnPosition()); - cont->SetRotation(entity->GetRespawnRotation()); - } + }); CBITSTREAM; CMSGHEADER; @@ -977,7 +986,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(); @@ -985,7 +994,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); @@ -1000,13 +1009,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); @@ -1019,7 +1027,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 @@ -1028,9 +1036,9 @@ 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; @@ -1047,7 +1055,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; @@ -1145,23 +1153,21 @@ 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 != NiQuaternion::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; @@ -1179,7 +1185,7 @@ void GameMessages::SendAddSkill(Entity* entity, TSkillID skillID, BehaviorSlot s 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); @@ -1348,8 +1354,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(); @@ -1472,11 +1478,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; @@ -1560,7 +1566,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); @@ -1730,8 +1736,6 @@ void GameMessages::SendStartCelebrationEffect(Entity* entity, const SystemAddres bitStream.Write(0); //subtext SEND_PACKET; - - //PacketUtils::SavePacket("StartCelebrationEffect.bin", (char*)bitStream.GetData(), bitStream.GetNumberOfBytesUsed()); } @@ -1746,7 +1750,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); } @@ -1785,14 +1789,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); } @@ -1815,12 +1819,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); } @@ -1838,7 +1842,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); } @@ -1849,7 +1853,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); } @@ -1867,7 +1871,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); } @@ -1876,7 +1880,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); } @@ -1893,9 +1897,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); @@ -1954,7 +1958,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 @@ -2019,7 +2022,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); } @@ -2054,7 +2057,7 @@ void GameMessages::SendGetModelsOnProperty(LWOOBJID objectId, std::map(models.size())); + bitStream.Write(models.size()); for (const auto& pair : models) { bitStream.Write(pair.first); @@ -2436,7 +2439,7 @@ void GameMessages::HandleBBBLoadItemRequest(RakNet::BitStream* inStream, Entity* void GameMessages::SendBlueprintLoadItemResponse(const SystemAddress& sysAddr, bool success, LWOOBJID oldItemId, LWOOBJID newItemId) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::BLUEPRINT_LOAD_RESPONSE_ITEMID); - bitStream.Write(static_cast(success)); + bitStream.Write(success); bitStream.Write(oldItemId); bitStream.Write(newItemId); SEND_PACKET; @@ -2569,12 +2572,12 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent //But we don't want the server to go unresponsive, because then the client would disconnect. //We need to get a new ID for our model first: - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t newID) { + ObjectIDManager::RequestPersistentID([=](uint32_t newID) { LWOOBJID newIDL = newID; GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER); GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT); - uint32_t blueprintIDSmall = ObjectIDManager::Instance()->GenerateRandomObjectID(); + uint32_t blueprintIDSmall = ObjectIDManager::GenerateRandomObjectID(); LWOOBJID blueprintID = blueprintIDSmall; GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER); GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT); @@ -2585,70 +2588,23 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent 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 result = query.execQuery(); - - if (result.eof() || result.fieldIsNull(0)) return; - - int templateId = result.getIntField(0); - - 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(); - uint64_t propertyId = 0; - - if (propertyEntry->next()) { - propertyId = propertyEntry->getUInt64(1); - } - - delete propertyEntry; - delete propertyLookup; + auto propertyInfo = Database::Get()->GetPropertyInfo(zoneId, cloneId); + LWOOBJID propertyId = LWOOBJID_EMPTY; + if (propertyInfo) propertyId = propertyInfo->id; //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); - - //whacky stream biz - std::string s(sd0Data.get(), sd0Size); - std::istringstream iss(s); - - ugcs->setBlob(5, &iss); - ugcs->setBoolean(6, false); - ugcs->setString(7, "weedeater.lxfml"); - ugcs->execute(); - delete ugcs; + std::string str(sd0Data.get(), sd0Size); + std::istringstream sd0DataStream(str); + Database::Get()->InsertNewUgcModel(sd0DataStream, blueprintIDSmall, entity->GetParentUser()->GetAccountID(), entity->GetCharacter()->GetID()); //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; + IPropertyContents::Model model; + model.id = newIDL; + model.ugcId = blueprintIDSmall; + model.position = NiPoint3::ZERO; + model.rotation = NiQuaternion(0.0f, 0.0f, 0.0f, 0.0f); + model.lot = 14; + Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_14_name"); /* Commented out until UGC server would be updated to use a sd0 file instead of lxfml stream. @@ -2716,7 +2672,7 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent PropertyManagementComponent::Instance()->AddModel(newEntity->GetObjectID(), newIDL); } - }); + }); } void GameMessages::HandlePropertyEntranceSync(RakNet::BitStream* inStream, Entity* entity, const SystemAddress& sysAddr) { @@ -2747,7 +2703,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 +2730,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) { @@ -3108,7 +3064,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 +3121,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 +3139,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 +3157,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 +3199,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); @@ -3452,7 +3408,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 +3457,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 +3527,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 +3591,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 +3630,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); } @@ -3957,19 +3913,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 +3942,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); } @@ -4177,6 +4133,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); } } @@ -4233,20 +4195,12 @@ 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) { @@ -4296,7 +4250,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 +4395,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 +4495,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 +4503,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 +4590,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 +4611,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 +4627,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 +4639,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; @@ -4934,7 +4892,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 +4925,7 @@ void GameMessages::HandleFireEventServerSide(RakNet::BitStream* inStream, Entity mapId = Game::zoneManager->GetZoneID().GetMapID(); // Fallback to sending the player back to the same zone. } - LOG("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(); @@ -4997,17 +4955,17 @@ void GameMessages::HandleRequestPlatformResync(RakNet::BitStream* inStream, Enti GameMessages::SendPlatformResync(entity, sysAddr); } -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) { @@ -5072,6 +5030,14 @@ void GameMessages::HandlePlayEmote(RakNet::BitStream* inStream, Entity* entity) if (emoteID == 0) return; std::string sAnimationName = "deaded"; //Default name in case we fail to get the emote + CDEmoteTableTable* emotes = CDClientManager::Instance().GetTable(); + if (emotes) { + CDEmoteTable* emote = emotes->GetEmote(emoteID); + if (emote) sAnimationName = emote->animationName; + } + + RenderComponent::PlayAnimation(entity, sAnimationName); + MissionComponent* missionComponent = entity->GetComponent(); if (!missionComponent) return; @@ -5097,14 +5063,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) { @@ -5127,6 +5085,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); } @@ -5213,6 +5175,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) { @@ -5321,7 +5291,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); @@ -5461,10 +5431,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); @@ -5584,13 +5552,6 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity* InventoryComponent* inv = static_cast(character->GetComponent(eReplicaComponentType::INVENTORY)); if (!inv) return; - 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 - - //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 @@ -5625,50 +5586,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); - LWOOBJID newIdBig; - // Make sure a subkey isnt already in use. Persistent Ids do not make sense here since this only needs to be unique for - // this character. Because of that, we just generate a random id and check for a collision. - do { - newIdBig = ObjectIDManager::Instance()->GenerateRandomObjectID(); + const auto moduleAssembly = new LDFData(u"assemblyPartLOTs", modules); + + std::vector config; + config.push_back(moduleAssembly); + + LWOOBJID newIdBig = newId; GeneralUtils::SetBit(newIdBig, eObjectBits::CHARACTER); - } while (inv->FindItemBySubKey(newIdBig)); - 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); - } - - auto* missionComponent = character->GetComponent(); - - 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); + 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); + } + }); } } @@ -5993,31 +5965,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; @@ -6025,32 +5993,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) { - LOG("Couldn't save bug report! (%s)", e.what()); - } + Database::Get()->InsertNewBugReport(reportInfo); } void @@ -6137,7 +6091,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)))); } } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 6a572d97..9c8d183e 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -32,10 +32,11 @@ 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 { @@ -74,6 +75,7 @@ namespace GameMessages { int iIndex = 0, int iDesiredWaypointIndex = 1, int nextIndex = 1, eMovementPlatformState movementState = eMovementPlatformState::Moving); + 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); @@ -99,8 +101,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); @@ -206,7 +208,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); @@ -385,7 +387,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); @@ -601,7 +603,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 e39cfeca..29bc8ea9 100644 --- a/dGame/dGameMessages/PropertyDataMessage.cpp +++ b/dGame/dGameMessages/PropertyDataMessage.cpp @@ -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); 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/dInventory/CMakeLists.txt b/dGame/dInventory/CMakeLists.txt index 60cfca75..a663a97d 100644 --- a/dGame/dInventory/CMakeLists.txt +++ b/dGame/dInventory/CMakeLists.txt @@ -1,5 +1,9 @@ -set(DGAME_DINVENTORY_SOURCES "EquippedItem.cpp" +set(DGAME_DINVENTORY_SOURCES + "EquippedItem.cpp" "Inventory.cpp" "Item.cpp" "ItemSet.cpp" - "ItemSetPassiveAbility.cpp" PARENT_SCOPE) + "ItemSetPassiveAbility.cpp") + +add_library(dInventory STATIC ${DGAME_DINVENTORY_SOURCES}) +target_precompile_headers(dInventory REUSE_FROM dGameBase) diff --git a/dGame/dInventory/Item.cpp b/dGame/dInventory/Item.cpp index 6a0fd82b..4a13cd92 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -2,7 +2,7 @@ #include -#include "../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "GeneralUtils.h" #include "GameMessages.h" #include "Entity.h" @@ -249,9 +249,9 @@ bool Item::IsEquipped() const { bool Item::Consume() { auto* skillsTable = CDClientManager::Instance().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; @@ -294,13 +294,13 @@ void Item::UseNonEquip(Item* item) { const auto type = static_cast(info->itemType); if (type == eItemType::MOUNT) { - if (Game::zoneManager->GetMountsAllowed()){ + 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 ) { - if (Game::zoneManager->GetPetsAllowed()){ + } else if (type == eItemType::PET_INVENTORY_ITEM && subKey != LWOOBJID_EMPTY) { + if (Game::zoneManager->GetPetsAllowed()) { const auto& databasePet = playerInventoryComponent->GetDatabasePet(subKey); if (databasePet.lot != LOT_NULL) { playerInventoryComponent->SpawnPet(this); @@ -395,14 +395,13 @@ void Item::Disassemble(const eInventoryType inventoryType) { } } -void Item::DisassembleModel() { +void Item::DisassembleModel(uint32_t numToDismantle) { auto* table = CDClientManager::Instance().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(); @@ -411,66 +410,72 @@ 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) { - LOG("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; } - std::unique_ptr 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"))); + auto* designID = currentBrick->Attribute("designID"); + if (designID) { + uint32_t designId; + if (!GeneralUtils::TryParse(designID, designId)) { + LOG("Failed to parse designID %s", designID); + continue; + } + parts[designId]++; } currentBrick = currentBrick->NextSiblingElement(searchTerm.c_str()); @@ -478,16 +483,16 @@ void Item::DisassembleModel() { auto* brickIDTable = CDClientManager::Instance().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 16f85feb..04d05d7c 100644 --- a/dGame/dInventory/Item.h +++ b/dGame/dInventory/Item.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..3364b63b 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(); 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 c190ec50..6799b834 100644 --- a/dGame/dMission/Mission.cpp +++ b/dGame/dMission/Mission.cpp @@ -257,7 +257,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() { @@ -329,9 +329,9 @@ void Mission::Complete(const bool yieldRewards) { 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(); 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.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.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.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h index db75aef7..0b9a09e3 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/AddStripMessage.h @@ -19,9 +19,9 @@ class AMFArrayValue; class AddStripMessage : public BehaviorMessageBase { public: AddStripMessage(AMFArrayValue* arguments); - StripUiPosition GetPosition() { return position; }; - ActionContext GetActionContext() { return actionContext; }; - std::vector GetActionsToAdd() { return actionsToAdd; }; + StripUiPosition GetPosition() const { return position; }; + ActionContext GetActionContext() const { return actionContext; }; + std::vector GetActionsToAdd() const { return actionsToAdd; }; private: StripUiPosition position; ActionContext actionContext; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp index 3286504a..cc817cd7 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/BehaviorMessageBase.cpp @@ -5,29 +5,27 @@ #include "dCommonVars.h" BehaviorMessageBase::BehaviorMessageBase(AMFArrayValue* arguments) { - behaviorId = 0; behaviorId = GetBehaviorIdFromArgument(arguments); } int32_t BehaviorMessageBase::GetBehaviorIdFromArgument(AMFArrayValue* arguments) { const auto* key = "BehaviorID"; auto* behaviorIDValue = arguments->Get(key); - int32_t behaviorID = -1; if (behaviorIDValue && behaviorIDValue->GetValueType() == eAmf::String) { - behaviorID = std::stoul(behaviorIDValue->GetValue()); - } else if (arguments->Get(key)->GetValueType() != eAmf::Undefined) { + GeneralUtils::TryParse(behaviorIDValue->GetValue(), behaviorId); + } else if (arguments->Get(key) && arguments->Get(key)->GetValueType() != eAmf::Undefined) { throw std::invalid_argument("Unable to find behavior ID"); } - return behaviorID; + return behaviorId; } -uint32_t BehaviorMessageBase::GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName) { +int32_t BehaviorMessageBase::GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName) { auto* 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 39246c9d..8a841d7f 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 "Logger.h" - enum class BehaviorState : uint32_t; /** @@ -18,12 +15,14 @@ enum class BehaviorState : uint32_t; */ class BehaviorMessageBase { public: - const uint32_t GetBehaviorId() { return behaviorId; }; -protected: + static inline int32_t DefaultBehaviorId = -1; + const int32_t GetBehaviorId() const { return behaviorId; }; + bool IsDefaultBehaviorId() { return behaviorId == DefaultBehaviorId; }; BehaviorMessageBase(AMFArrayValue* arguments); +protected: int32_t GetBehaviorIdFromArgument(AMFArrayValue* arguments); - uint32_t GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName = "actionIndex"); - int32_t behaviorId = -1; + int32_t GetActionIndexFromArgument(AMFArrayValue* arguments, const std::string& keyName = "actionIndex"); + 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.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.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.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.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.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.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.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.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.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.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 3dbd777a..c541257c 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviors.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviors.cpp @@ -5,7 +5,7 @@ #include "Game.h" #include "GameMessages.h" #include "ModelComponent.h" -#include "../../dWorldServer/ObjectIDManager.h" +#include "ObjectIDManager.h" #include "Logger.h" #include "BehaviorStates.h" #include "AssetManager.h" @@ -30,221 +30,49 @@ #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++) { - // LOG("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++) { - // LOG("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()) { - // LOG("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) { LOG("Received undefined block type %s. Ignoring.", updateActionMessage.GetAction().GetType().c_str()); @@ -266,83 +94,86 @@ void ControlBehaviors::UpdateAction(AMFArrayValue* arguments) { } } -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 - LOG("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); + + AMFArrayValue args; + args.Insert("BehaviorID", std::to_string(msg.GetBehaviorId())); + GameMessages::SendUIMessageServerToSingleClient(modelOwner, modelOwner->GetParentUser()->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) { - LOG("failed to open blocksdef"); - return; - } - std::istream blocksBuffer(&blocksDefStreamBuffer); - if (!blocksBuffer.good()) { - LOG("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("