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/.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 700e7536..4ba6a43b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -14,9 +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 aa7eb9b2..91a26dc5 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,14 +15,13 @@ 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) @@ -53,15 +53,16 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE) if(UNIX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -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() @@ -97,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}) @@ -145,10 +163,11 @@ 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() @@ -160,6 +179,7 @@ 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() @@ -167,6 +187,7 @@ 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) configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}) @@ -174,6 +195,7 @@ 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) configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}) @@ -215,78 +237,6 @@ set(INCLUDED_DIRECTORIES "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/MINIGAME/Objects" - "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" - "thirdparty/magic_enum/include/magic_enum" "thirdparty/raknet/Source" "thirdparty/tinyxml2" @@ -301,20 +251,20 @@ set(INCLUDED_DIRECTORIES ) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux) -if (APPLE) +if(APPLE) include_directories("/usr/local/include/") endif() # 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) +if(NOT WIN32) include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt") endif() -include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include") +include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include") # Add linking directories: link_directories(${PROJECT_BINARY_DIR}) @@ -368,10 +318,10 @@ add_subdirectory(dPhysics) 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() @@ -382,12 +332,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} @@ -409,6 +353,6 @@ target_precompile_headers( "$<$:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" ) -if (${ENABLE_TESTING}) +if(${ENABLE_TESTING}) add_subdirectory(tests) endif() 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 f647aaa5..72bacc95 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ If you would like a setup for a single player server only on a Windows machine, * [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) @@ -347,6 +348,42 @@ certutil -hashfile SHA1 Known good *SHA1* checksum of the Darkflame Universe client: - `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed) +# Docker + +## Standalone + +For standalone deployment, you can use the image provided via Github's Container Repository: +`ghcr.io/darkflameuniverse/darkflameserver` + +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` + +## Compose + +See the [compose](docker-compose.yml) file in the root of the repo. + +This compose file is for a full deployment: MariaDB, DarkflameServer, and Nexus Dashboard. + +All of the environmental options are listed in the compose file so the can be pass through, or you can edit the compose file to suit your specific needs + # 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 52b35678..974ee27e 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -28,7 +29,7 @@ namespace Game { Logger* logger = nullptr; dServer* server = nullptr; dConfig* config = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; } @@ -42,6 +43,9 @@ int main(int argc, char** argv) { Diagnostics::SetProcessFileName(argv[0]); Diagnostics::Initialize(); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); + //Create all the objects we need to run our service: Game::logger = SetupLogger(); if (!Game::logger) return EXIT_FAILURE; @@ -74,6 +78,7 @@ int main(int argc, char** argv) { masterIP = masterInfo->ip; masterPort = masterInfo->port; } + LOG("Master is at %s:%d", masterIP.c_str(), masterPort); Game::randomEngine = std::mt19937(time(0)); @@ -81,9 +86,9 @@ int main(int argc, char** argv) { uint32_t maxClients = 50; uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default. if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + if (Game::config->GetValue("auth_server_port") != "") ourPort = std::atoi(Game::config->GetValue("auth_server_port").c_str()); - 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(Game::config->GetValue("external_ip"), 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(); @@ -96,13 +101,16 @@ int main(int argc, char** argv) { AuthPackets::LoadClaimCodes(); - 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++; - 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. @@ -141,6 +149,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; diff --git a/dChatServer/ChatServer.cpp b/dChatServer/ChatServer.cpp index c9bdcd9c..1642ff9c 100644 --- a/dChatServer/ChatServer.cpp +++ b/dChatServer/ChatServer.cpp @@ -33,7 +33,7 @@ namespace Game { dConfig* config = nullptr; dChatFilter* chatFilter = nullptr; AssetManager* assetManager = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; std::mt19937 randomEngine; PlayerContainer playerContainer; } @@ -48,6 +48,9 @@ int main(int argc, char** argv) { Diagnostics::SetProcessFileName(argv[0]); Diagnostics::Initialize(); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); + //Create all the objects we need to run our service: Game::logger = SetupLogger(); if (!Game::logger) return EXIT_FAILURE; @@ -99,9 +102,9 @@ int main(int argc, char** argv) { uint32_t maxClients = 50; uint32_t ourPort = 1501; if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); + if (Game::config->GetValue("chat_server_port") != "") ourPort = std::atoi(Game::config->GetValue("chat_server_port").c_str()); - 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(Game::config->GetValue("external_ip"), 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")))); @@ -118,7 +121,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++; 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/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 972eea46..d8113497 100644 --- a/dCommon/Game.h +++ b/dCommon/Game.h @@ -2,6 +2,7 @@ #include #include +#include class dServer; class Logger; @@ -16,6 +17,7 @@ class dZoneManager; class PlayerContainer; namespace Game { + using signal_t = volatile std::sig_atomic_t; extern Logger* logger; extern dServer* server; extern InstanceManager* im; @@ -25,9 +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/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/dGame/CMakeLists.txt b/dGame/CMakeLists.txt index 80f16042..ac7f38cc 100644 --- a/dGame/CMakeLists.txt +++ b/dGame/CMakeLists.txt @@ -8,58 +8,27 @@ set(DGAME_SOURCES "Character.cpp" "User.cpp" "UserManager.cpp") +include_directories( + ${PROJECT_SOURCE_DIR}/dScripts + ${PROJECT_SOURCE_DIR}/dGame +) + +add_library(dGameBase ${DGAME_SOURCES}) +target_precompile_headers(dGameBase PRIVATE ${HEADERS_DGAME}) +target_link_libraries(dGameBase + PUBLIC dDatabase dPhysics + INTERFACE dComponents dEntity) + add_subdirectory(dBehaviors) - -foreach(file ${DGAME_DBEHAVIORS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dBehaviors/${file}") -endforeach() - add_subdirectory(dComponents) - -foreach(file ${DGAME_DCOMPONENTS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dComponents/${file}") -endforeach() - add_subdirectory(dEntity) - -foreach(file ${DGAME_DENTITY_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dEntity/${file}") -endforeach() - add_subdirectory(dGameMessages) - -foreach(file ${DGAME_DGAMEMESSAGES_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dGameMessages/${file}") -endforeach() - add_subdirectory(dInventory) - -foreach(file ${DGAME_DINVENTORY_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dInventory/${file}") -endforeach() - add_subdirectory(dMission) - -foreach(file ${DGAME_DMISSION_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dMission/${file}") -endforeach() - add_subdirectory(dPropertyBehaviors) - -foreach(file ${DGAME_DPROPERTYBEHAVIORS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dPropertyBehaviors/${file}") -endforeach() - add_subdirectory(dUtilities) -foreach(file ${DGAME_DUTILITIES_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "dUtilities/${file}") -endforeach() - -foreach(file ${DSCRIPTS_SOURCES}) - set(DGAME_SOURCES ${DGAME_SOURCES} "${PROJECT_SOURCE_DIR}/dScripts/${file}") -endforeach() - -add_library(dGame STATIC ${DGAME_SOURCES}) - -target_link_libraries(dGame dDatabase Recast Detour) +add_library(dGame INTERFACE) +target_link_libraries(dGame INTERFACE + dGameBase dBehaviors dComponents dEntity dGameMessages dInventory dMission dPropertyBehaviors dUtilities dScripts +) diff --git a/dGame/UserManager.cpp b/dGame/UserManager.cpp index 18387366..9afd38b2 100644 --- a/dGame/UserManager.cpp +++ b/dGame/UserManager.cpp @@ -263,7 +263,7 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) } //Now that the name is ok, we can get an objectID from Master: - ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t objectID) { + ObjectIDManager::Instance()->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); diff --git a/dGame/dBehaviors/CMakeLists.txt b/dGame/dBehaviors/CMakeLists.txt index d1926525..c8cb0be0 100644 --- a/dGame/dBehaviors/CMakeLists.txt +++ b/dGame/dBehaviors/CMakeLists.txt @@ -54,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/dComponents/ActivityComponent.cpp b/dGame/dComponents/ActivityComponent.cpp index f4865a03..55bc4c08 100644 --- a/dGame/dComponents/ActivityComponent.cpp +++ b/dGame/dComponents/ActivityComponent.cpp @@ -33,7 +33,7 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo if (activityID > 0) m_ActivityID = activityID; else m_ActivityID = parent->GetVar(u"activityID"); 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 (CDActivities activity : activities) { m_ActivityInfo = activity; @@ -93,7 +93,7 @@ void ActivityComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIniti 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 (static_cast(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") { @@ -532,7 +532,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/BuffComponent.cpp b/dGame/dComponents/BuffComponent.cpp index 32d7d1d4..1819438b 100644 --- a/dGame/dComponents/BuffComponent.cpp +++ b/dGame/dComponents/BuffComponent.cpp @@ -95,10 +95,16 @@ 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) { @@ -216,7 +222,7 @@ void BuffComponent::RemoveBuff(int32_t id, bool fromUnEquip, bool removeImmunity GameMessages::SendRemoveBuff(m_Parent, fromUnEquip, removeImmunity, id); - m_Buffs.erase(iter); + m_BuffsToRemove.push_back(id); RemoveBuffEffect(id); } diff --git a/dGame/dComponents/BuffComponent.h b/dGame/dComponents/BuffComponent.h index 7187f8f7..7f7b44d8 100644 --- a/dGame/dComponents/BuffComponent.h +++ b/dGame/dComponents/BuffComponent.h @@ -140,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 d0bb89d3..b14d0eb8 100644 --- a/dGame/dComponents/CMakeLists.txt +++ b/dGame/dComponents/CMakeLists.txt @@ -1,4 +1,5 @@ -set(DGAME_DCOMPONENTS_SOURCES "ActivityComponent.cpp" +set(DGAME_DCOMPONENTS_SOURCES + "ActivityComponent.cpp" "BaseCombatAIComponent.cpp" "BouncerComponent.cpp" "BuffComponent.cpp" @@ -46,5 +47,11 @@ set(DGAME_DCOMPONENTS_SOURCES "ActivityComponent.cpp" "HavokVehiclePhysicsComponent.cpp" "VendorComponent.cpp" "MiniGameControlComponent.cpp" - PARENT_SCOPE ) + +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/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/RacingControlComponent.cpp b/dGame/dComponents/RacingControlComponent.cpp index ef1d0f32..5b93dbb7 100644 --- a/dGame/dComponents/RacingControlComponent.cpp +++ b/dGame/dComponents/RacingControlComponent.cpp @@ -312,7 +312,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, 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/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/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 06c6900f..e68cdebc 100644 --- a/dGame/dInventory/Item.cpp +++ b/dGame/dInventory/Item.cpp @@ -249,7 +249,7 @@ 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); }); 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/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 b143cbc8..248ce4e7 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviors.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviors.cpp @@ -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::Instance()->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,75 +94,83 @@ 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 blocksBuffer = Game::assetManager->GetFile("ui\\ingame\\blocksdef.xml"); if (!blocksBuffer) { - LOG("Failed to open blocksdef.xml"); + 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. @@ -371,14 +207,14 @@ ControlBehaviors::ControlBehaviors() { while (block) { blockName = block->Name(); - BlockDefinition* blockDefinition = new BlockDefinition(); + auto& blockDefinition = blockTypes[blockName]; std::string name{}; std::string typeName{}; auto* argument = block->FirstChildElement("Argument"); if (argument) { auto* defaultDefinition = argument->FirstChildElement("DefaultValue"); - if (defaultDefinition) blockDefinition->SetDefaultValue(defaultDefinition->GetText()); + if (defaultDefinition) blockDefinition.SetDefaultValue(defaultDefinition->GetText()); auto* typeDefinition = argument->FirstChildElement("Type"); if (typeDefinition) typeName = typeDefinition->GetText(); @@ -388,23 +224,23 @@ ControlBehaviors::ControlBehaviors() { // Now we parse the blocksdef file for the relevant information if (typeName == "String") { - blockDefinition->SetMaximumValue(50); // The client has a hardcoded limit of 50 characters in a string field + blockDefinition.SetMaximumValue(50); // The client has a hardcoded limit of 50 characters in a string field } else if (typeName == "Float" || typeName == "Integer") { auto* maximumDefinition = argument->FirstChildElement("Maximum"); - if (maximumDefinition) blockDefinition->SetMaximumValue(std::stof(maximumDefinition->GetText())); + if (maximumDefinition) blockDefinition.SetMaximumValue(std::stof(maximumDefinition->GetText())); auto* minimumDefinition = argument->FirstChildElement("Minimum"); - if (minimumDefinition) blockDefinition->SetMinimumValue(std::stof(minimumDefinition->GetText())); + if (minimumDefinition) blockDefinition.SetMinimumValue(std::stof(minimumDefinition->GetText())); } else if (typeName == "Enumeration") { auto* values = argument->FirstChildElement("Values"); if (values) { auto* value = values->FirstChildElement("Value"); while (value) { - if (value->GetText() == blockDefinition->GetDefaultValue()) blockDefinition->GetDefaultValue() = std::to_string(blockDefinition->GetMaximumValue()); - blockDefinition->SetMaximumValue(blockDefinition->GetMaximumValue() + 1); + if (value->GetText() == blockDefinition.GetDefaultValue()) blockDefinition.GetDefaultValue() = std::to_string(blockDefinition.GetMaximumValue()); + blockDefinition.SetMaximumValue(blockDefinition.GetMaximumValue() + 1); value = value->NextSiblingElement("Value"); } - blockDefinition->SetMaximumValue(blockDefinition->GetMaximumValue() - 1); // Maximum value is 0 indexed + blockDefinition.SetMaximumValue(blockDefinition.GetMaximumValue() - 1); // Maximum value is 0 indexed } else { values = argument->FirstChildElement("EnumerationSource"); if (!values) { @@ -421,8 +257,8 @@ ControlBehaviors::ControlBehaviors() { std::string serviceName = serviceNameNode->GetText(); if (serviceName == "GetBehaviorSoundList") { auto res = CDClientDatabase::ExecuteQuery("SELECT MAX(id) as countSounds FROM UGBehaviorSounds;"); - blockDefinition->SetMaximumValue(res.getIntField("countSounds")); - blockDefinition->SetDefaultValue("0"); + blockDefinition.SetMaximumValue(res.getIntField("countSounds")); + blockDefinition.SetDefaultValue("0"); } else { LOG("Unsupported Enumeration ServiceType (%s)", serviceName.c_str()); continue; @@ -433,19 +269,18 @@ ControlBehaviors::ControlBehaviors() { continue; } } - blockTypes.insert(std::make_pair(blockName, blockDefinition)); block = block->NextSiblingElement(); } blockSections = blockSections->NextSiblingElement(); } isInitialized = true; LOG_DEBUG("Created all base block classes"); - for (auto b : blockTypes) { - LOG_DEBUG("block name is %s default %s min %f max %f", b.first.c_str(), b.second->GetDefaultValue().c_str(), b.second->GetMinimumValue(), b.second->GetMaximumValue()); + for (auto& [name, block] : blockTypes) { + LOG_DEBUG("block name is %s default %s min %f max %f", name.c_str(), block.GetDefaultValue().c_str(), block.GetMinimumValue(), block.GetMaximumValue()); } } -BlockDefinition* ControlBehaviors::GetBlockInfo(const BlockName& blockName) { +std::optional ControlBehaviors::GetBlockInfo(const BlockName& blockName) { auto blockDefinition = blockTypes.find(blockName); - return blockDefinition != blockTypes.end() ? blockDefinition->second : nullptr; + return blockDefinition != blockTypes.end() ? std::optional(blockDefinition->second) : std::nullopt; } diff --git a/dGame/dPropertyBehaviors/ControlBehaviors.h b/dGame/dPropertyBehaviors/ControlBehaviors.h index a562aafe..ab739408 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviors.h +++ b/dGame/dPropertyBehaviors/ControlBehaviors.h @@ -4,12 +4,13 @@ #define __CONTROLBEHAVIORS__H__ #include +#include #include +#include "BlockDefinition.h" #include "Singleton.h" class AMFArrayValue; -class BlockDefinition; class Entity; class ModelComponent; class SystemAddress; @@ -17,6 +18,18 @@ class SystemAddress; // Type definition to clarify what is used where typedef std::string BlockName; //! A block name +struct ControlBehaviorContext { + ControlBehaviorContext(AMFArrayValue* args, ModelComponent* modelComponent, Entity* modelOwner) : arguments(args), modelComponent(modelComponent), modelOwner(modelOwner) {}; + + operator bool() const { + return arguments != nullptr && modelComponent != nullptr && modelOwner != nullptr; + } + + AMFArrayValue* arguments; + Entity* modelOwner; + ModelComponent* modelComponent; +}; + class ControlBehaviors: public Singleton { public: ControlBehaviors(); @@ -39,27 +52,13 @@ public: * * @return A pair of the block parameter name to its typing */ - BlockDefinition* GetBlockInfo(const BlockName& blockName); + std::optional GetBlockInfo(const BlockName& blockName); private: - void RequestUpdatedID(int32_t behaviorID, ModelComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr); - void SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner); - void ModelTypeChanged(AMFArrayValue* arguments, ModelComponent* ModelComponent); - void ToggleExecutionUpdates(); - void AddStrip(AMFArrayValue* arguments); - void RemoveStrip(AMFArrayValue* arguments); - void MergeStrips(AMFArrayValue* arguments); - void SplitStrip(AMFArrayValue* arguments); - void UpdateStripUI(AMFArrayValue* arguments); - void AddAction(AMFArrayValue* arguments); - void MigrateActions(AMFArrayValue* arguments); - void RearrangeStrip(AMFArrayValue* arguments); - void Add(AMFArrayValue* arguments); - void RemoveActions(AMFArrayValue* arguments); - void Rename(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments); - void SendBehaviorBlocksToClient(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments); + void RequestUpdatedID(ControlBehaviorContext& context); + void SendBehaviorListToClient(const ControlBehaviorContext& context); + void SendBehaviorBlocksToClient(ControlBehaviorContext& context); void UpdateAction(AMFArrayValue* arguments); - void MoveToInventory(ModelComponent* modelComponent, const SystemAddress& sysAddr, Entity* modelOwner, AMFArrayValue* arguments); - std::map blockTypes{}; + std::map blockTypes{}; // If false, property behaviors will not be able to be edited. bool isInitialized = false; diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp new file mode 100644 index 00000000..f6f6e4f1 --- /dev/null +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -0,0 +1,131 @@ +#include "PropertyBehavior.h" + +#include "Amf3.h" +#include "BehaviorStates.h" +#include "ControlBehaviorMsgs.h" + +PropertyBehavior::PropertyBehavior() { + m_LastEditedState = BehaviorState::HOME_STATE; +} + +template<> +void PropertyBehavior::HandleMsg(AddStripMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(AddActionMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(RearrangeStripMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(UpdateActionMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(UpdateStripUiMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(RemoveStripMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(RemoveActionsMessage& msg) { + m_States[msg.GetActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(SplitStripMessage& msg) { + m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg); + m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetDestinationActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(MigrateActionsMessage& msg) { + m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg); + m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetDestinationActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(MergeStripsMessage& msg) { + m_States[msg.GetSourceActionContext().GetStateId()].HandleMsg(msg); + m_States[msg.GetDestinationActionContext().GetStateId()].HandleMsg(msg); + m_LastEditedState = msg.GetDestinationActionContext().GetStateId(); +}; + +template<> +void PropertyBehavior::HandleMsg(RenameMessage& msg) { + m_Name = msg.GetName(); +}; + +template<> +void PropertyBehavior::HandleMsg(AddMessage& msg) { + // TODO Parse the corresponding behavior xml file. + m_BehaviorId = msg.GetBehaviorId(); + isLoot = m_BehaviorId != 7965; +}; + +void PropertyBehavior::SetBehaviorId(int32_t behaviorId) { + m_BehaviorId = behaviorId; +} + +void PropertyBehavior::SendBehaviorListToClient(AMFArrayValue& args) const { + args.Insert("id", std::to_string(m_BehaviorId)); + args.Insert("name", m_Name); + args.Insert("isLocked", isLocked); + args.Insert("isLoot", isLoot); +} + +void PropertyBehavior::VerifyLastEditedState() { + if (!m_States[m_LastEditedState].IsEmpty()) return; + + for (const auto& [stateId, state] : m_States) { + if (state.IsEmpty()) continue; + + LOG_DEBUG("Updating last edited state to %i because %i is empty.", stateId, m_LastEditedState); + m_LastEditedState = stateId; + return; + } + + LOG_DEBUG("No states found, sending default state"); + + m_LastEditedState = BehaviorState::HOME_STATE; +} + +void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const { + auto* stateArray = args.InsertArray("states"); + + auto lastState = BehaviorState::HOME_STATE; + for (auto& [stateId, state] : m_States) { + if (state.IsEmpty()) continue; + + LOG_DEBUG("Serializing state %i", stateId); + auto* stateArgs = stateArray->PushArray(); + stateArgs->Insert("id", static_cast(stateId)); + state.SendBehaviorBlocksToClient(*stateArgs); + } + + auto* executionState = args.InsertArray("executionState"); + executionState->Insert("stateID", static_cast(m_LastEditedState)); + executionState->InsertArray("strips"); + + // TODO Serialize the execution state of the behavior +} diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h new file mode 100644 index 00000000..dc53bbed --- /dev/null +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -0,0 +1,49 @@ +#ifndef __PROPERTYBEHAVIOR__H__ +#define __PROPERTYBEHAVIOR__H__ + +#include "State.h" + +enum class BehaviorState : uint32_t; + +class AMFArrayValue; + +/** + * Represents the Entity of a Property Behavior and holds data associated with the behavior + */ +class PropertyBehavior { +public: + PropertyBehavior(); + template + void HandleMsg(Msg& msg); + + // If the last edited state has no strips, this method will set the last edited state to the first state that has strips. + void VerifyLastEditedState(); + void SendBehaviorListToClient(AMFArrayValue& args) const; + void SendBehaviorBlocksToClient(AMFArrayValue& args) const; + + int32_t GetBehaviorId() const { return m_BehaviorId; } + void SetBehaviorId(int32_t id); +private: + + // The states this behavior has. + std::map m_States; + + // The name of this behavior. + std::string m_Name = "New Behavior"; + + // Whether this behavior is locked and cannot be edited. + bool isLocked = false; + + // Whether this behavior is custom or pre-fab. + bool isLoot = false; + + // The last state that was edited. This is used so when the client re-opens the behavior editor, it will open to the last edited state. + // If the last edited state has no strips, it will open to the first state that has strips. + BehaviorState m_LastEditedState; + + // The behavior id for this behavior. This is expected to be fully unique, however an id of -1 means this behavior was just created + // and needs to be assigned an id. + int32_t m_BehaviorId = -1; +}; + +#endif //!__PROPERTYBEHAVIOR__H__ diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp new file mode 100644 index 00000000..59a9aa8b --- /dev/null +++ b/dGame/dPropertyBehaviors/State.cpp @@ -0,0 +1,137 @@ +#include "State.h" + +#include "Amf3.h" +#include "ControlBehaviorMsgs.h" + +template<> +void State::HandleMsg(AddStripMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + m_Strips.resize(msg.GetActionContext().GetStripId() + 1); + } + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(AddActionMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(UpdateStripUiMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(RemoveActionsMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(RearrangeStripMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(UpdateActionMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(RemoveStripMessage& msg) { + if (m_Strips.size() <= msg.GetActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetActionContext().GetStripId()).HandleMsg(msg); +}; + +template<> +void State::HandleMsg(SplitStripMessage& msg) { + if (msg.GetTransferredActions().empty()) { + if (m_Strips.size() <= msg.GetSourceActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetSourceActionContext().GetStripId()).HandleMsg(msg); + } else { + if (m_Strips.size() <= msg.GetDestinationActionContext().GetStripId()) { + m_Strips.resize(msg.GetDestinationActionContext().GetStripId() + 1); + } + + m_Strips.at(msg.GetDestinationActionContext().GetStripId()).HandleMsg(msg); + } +}; + +template<> +void State::HandleMsg(MergeStripsMessage& msg) { + if (msg.GetMigratedActions().empty()) { + if (m_Strips.size() <= msg.GetSourceActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetSourceActionContext().GetStripId()).HandleMsg(msg); + } else { + if (m_Strips.size() <= msg.GetDestinationActionContext().GetStripId()) { + m_Strips.resize(msg.GetDestinationActionContext().GetStripId() + 1); + } + + m_Strips.at(msg.GetDestinationActionContext().GetStripId()).HandleMsg(msg); + } +}; + +template<> +void State::HandleMsg(MigrateActionsMessage& msg) { + if (msg.GetMigratedActions().empty()) { + if (m_Strips.size() <= msg.GetSourceActionContext().GetStripId()) { + return; + } + + m_Strips.at(msg.GetSourceActionContext().GetStripId()).HandleMsg(msg); + } else { + if (m_Strips.size() <= msg.GetDestinationActionContext().GetStripId()) { + m_Strips.resize(msg.GetDestinationActionContext().GetStripId() + 1); + } + + m_Strips.at(msg.GetDestinationActionContext().GetStripId()).HandleMsg(msg); + } +}; + +bool State::IsEmpty() const { + for (auto& strip : m_Strips) { + if (!strip.IsEmpty()) return false; + } + return true; +} + +void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const { + auto* strips = args.InsertArray("strips"); + for (int32_t stripId = 0; stripId < m_Strips.size(); stripId++) { + auto& strip = m_Strips.at(stripId); + if (strip.IsEmpty()) continue; + + auto* stripArgs = strips->PushArray(); + stripArgs->Insert("id", static_cast(stripId)); + + strip.SendBehaviorBlocksToClient(*stripArgs); + } +}; diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h new file mode 100644 index 00000000..a6a6d23b --- /dev/null +++ b/dGame/dPropertyBehaviors/State.h @@ -0,0 +1,19 @@ +#ifndef __STATE__H__ +#define __STATE__H__ + +#include "Strip.h" + +class AMFArrayValue; + +class State { +public: + template + void HandleMsg(Msg& msg); + + void SendBehaviorBlocksToClient(AMFArrayValue& args) const; + bool IsEmpty() const; +private: + std::vector m_Strips; +}; + +#endif //!__STATE__H__ diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp new file mode 100644 index 00000000..7d27cacd --- /dev/null +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -0,0 +1,87 @@ +#include "Strip.h" + +#include "Amf3.h" +#include "ControlBehaviorMsgs.h" + +template<> +void Strip::HandleMsg(AddStripMessage& msg) { + m_Actions = msg.GetActionsToAdd(); + m_Position = msg.GetPosition(); +}; + +template<> +void Strip::HandleMsg(AddActionMessage& msg) { + if (msg.GetActionIndex() == -1) return; + + m_Actions.insert(m_Actions.begin() + msg.GetActionIndex(), msg.GetAction()); +}; + +template<> +void Strip::HandleMsg(UpdateStripUiMessage& msg) { + m_Position = msg.GetPosition(); +}; + +template<> +void Strip::HandleMsg(RemoveStripMessage& msg) { + m_Actions.clear(); +}; + +template<> +void Strip::HandleMsg(RemoveActionsMessage& msg) { + if (msg.GetActionIndex() >= m_Actions.size()) return; + m_Actions.erase(m_Actions.begin() + msg.GetActionIndex(), m_Actions.end()); +}; + +template<> +void Strip::HandleMsg(UpdateActionMessage& msg) { + if (msg.GetActionIndex() >= m_Actions.size()) return; + m_Actions.at(msg.GetActionIndex()) = msg.GetAction(); +}; + +template<> +void Strip::HandleMsg(RearrangeStripMessage& msg) { + if (msg.GetDstActionIndex() >= m_Actions.size() || msg.GetSrcActionIndex() >= m_Actions.size() || msg.GetSrcActionIndex() <= msg.GetDstActionIndex()) return; + std::rotate(m_Actions.begin() + msg.GetDstActionIndex(), m_Actions.begin() + msg.GetSrcActionIndex(), m_Actions.end()); +}; + +template<> +void Strip::HandleMsg(SplitStripMessage& msg) { + if (msg.GetTransferredActions().empty() && !m_Actions.empty()) { + auto startToMove = m_Actions.begin() + msg.GetSrcActionIndex(); + msg.SetTransferredActions(startToMove, m_Actions.end()); + m_Actions.erase(startToMove, m_Actions.end()); + } else { + m_Actions = msg.GetTransferredActions(); + m_Position = msg.GetPosition(); + } +}; + +template<> +void Strip::HandleMsg(MergeStripsMessage& msg) { + if (msg.GetMigratedActions().empty() && !m_Actions.empty()) { + msg.SetMigratedActions(m_Actions.begin(), m_Actions.end()); + m_Actions.erase(m_Actions.begin(), m_Actions.end()); + } else { + m_Actions.insert(m_Actions.begin() + msg.GetDstActionIndex(), msg.GetMigratedActions().begin(), msg.GetMigratedActions().end()); + } +}; + +template<> +void Strip::HandleMsg(MigrateActionsMessage& msg) { + if (msg.GetMigratedActions().empty() && !m_Actions.empty()) { + auto startToMove = m_Actions.begin() + msg.GetSrcActionIndex(); + msg.SetMigratedActions(startToMove, m_Actions.end()); + m_Actions.erase(startToMove, m_Actions.end()); + } else { + m_Actions.insert(m_Actions.begin() + msg.GetDstActionIndex(), msg.GetMigratedActions().begin(), msg.GetMigratedActions().end()); + } +}; + +void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const { + m_Position.SendBehaviorBlocksToClient(args); + + auto* actions = args.InsertArray("actions"); + for (auto& action : m_Actions) { + action.SendBehaviorBlocksToClient(*actions); + } +}; diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h new file mode 100644 index 00000000..f3e10964 --- /dev/null +++ b/dGame/dPropertyBehaviors/Strip.h @@ -0,0 +1,23 @@ +#ifndef __STRIP__H__ +#define __STRIP__H__ + +#include "Action.h" +#include "StripUiPosition.h" + +#include + +class AMFArrayValue; + +class Strip { +public: + template + void HandleMsg(Msg& msg); + + void SendBehaviorBlocksToClient(AMFArrayValue& args) const; + bool IsEmpty() const { return m_Actions.empty(); } +private: + std::vector m_Actions; + StripUiPosition m_Position; +}; + +#endif //!__STRIP__H__ diff --git a/dGame/dUtilities/CMakeLists.txt b/dGame/dUtilities/CMakeLists.txt index 639f9cf4..b6bb6059 100644 --- a/dGame/dUtilities/CMakeLists.txt +++ b/dGame/dUtilities/CMakeLists.txt @@ -5,4 +5,10 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp" "Mail.cpp" "Preconditions.cpp" "SlashCommandHandler.cpp" - "VanityUtilities.cpp" PARENT_SCOPE) + "VanityUtilities.cpp") + +add_library(dUtilities STATIC ${DGAME_DUTILITIES_SOURCES}) +target_precompile_headers(dUtilities REUSE_FROM dGameBase) +target_link_libraries(dUtilities + PUBLIC dDatabase dPhysics + INTERFACE dZoneManager) diff --git a/dGame/dUtilities/VanityUtilities.cpp b/dGame/dUtilities/VanityUtilities.cpp index 99087365..6d5f996c 100644 --- a/dGame/dUtilities/VanityUtilities.cpp +++ b/dGame/dUtilities/VanityUtilities.cpp @@ -294,21 +294,20 @@ void VanityUtilities::ParseXML(const std::string& file) { auto* partyPhrases = npcs->FirstChildElement("partyphrases"); if (partyPhrases == nullptr) { - LOG("Failed to parse party phrases"); - return; - } - - for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr; - phrase = phrase->NextSiblingElement("phrase")) { - // Get the phrase - auto* text = phrase->GetText(); - - if (text == nullptr) { - LOG("Failed to parse party phrase"); - continue; + LOG("No party phrases found"); + } else { + for (auto* phrase = partyPhrases->FirstChildElement("phrase"); phrase != nullptr; + phrase = phrase->NextSiblingElement("phrase")) { + // Get the phrase + auto* text = phrase->GetText(); + + if (text == nullptr) { + LOG("Failed to parse party phrase"); + continue; + } + + m_PartyPhrases.push_back(text); } - - m_PartyPhrases.push_back(text); } for (auto* npc = npcs->FirstChildElement("npc"); npc != nullptr; npc = npc->NextSiblingElement("npc")) { diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index 51aad2cc..193ff940 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -134,7 +134,7 @@ void InstanceManager::RemoveInstance(Instance* instance) { if (m_Instances[i] == instance) { instance->SetShutdownComplete(true); - if (!Game::shouldShutdown) RedirectPendingRequests(instance); + if (!Game::ShouldShutdown()) RedirectPendingRequests(instance); delete m_Instances[i]; diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index 2de212a0..19a06706 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -47,12 +47,13 @@ namespace Game { InstanceManager* im = nullptr; dConfig* config = nullptr; AssetManager* assetManager = nullptr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; + bool universeShutdownRequested = false; std::mt19937 randomEngine; } //namespace Game bool shutdownSequenceStarted = false; -void ShutdownSequence(int32_t signal = -1); +int ShutdownSequence(int32_t signal = -1); int32_t FinalizeShutdown(int32_t signal = -1); Logger* SetupLogger(); void HandlePacket(Packet* packet); @@ -73,8 +74,8 @@ int main(int argc, char** argv) { //Triggers the shutdown sequence at application exit std::atexit([]() { ShutdownSequence(); }); - signal(SIGINT, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); }); - signal(SIGTERM, [](int32_t signal) { ShutdownSequence(EXIT_FAILURE); }); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); //Create all the objects we need to run our service: Game::logger = SetupLogger(); @@ -149,31 +150,40 @@ int main(int argc, char** argv) { } MigrationRunner::RunMigrations(); - - const bool cdServerExists = std::filesystem::exists(BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite"); + const auto resServerPath = BinaryPathFinder::GetBinaryDir() / "resServer"; + const bool cdServerExists = std::filesystem::exists(resServerPath / "CDServer.sqlite"); const bool oldCDServerExists = std::filesystem::exists(Game::assetManager->GetResPath() / "CDServer.sqlite"); const bool fdbExists = std::filesystem::exists(Game::assetManager->GetResPath() / "cdclient.fdb"); + const bool resServerPathExists = std::filesystem::is_directory(resServerPath); + + if (!resServerPathExists) { + LOG("%s does not exist, creating it.", (resServerPath).c_str()); + if(!std::filesystem::create_directories(resServerPath)){ + LOG("Failed to create %s", (resServerPath).string().c_str()); + return EXIT_FAILURE; + } + } if (!cdServerExists) { if (oldCDServerExists) { // If the file doesn't exist in the new CDServer location, copy it there. We copy because we may not have write permissions from the previous directory. LOG("CDServer.sqlite is not located at resServer, but is located at res path. Copying file..."); - std::filesystem::copy_file(Game::assetManager->GetResPath() / "CDServer.sqlite", BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite"); + std::filesystem::copy_file(Game::assetManager->GetResPath() / "CDServer.sqlite", resServerPath / "CDServer.sqlite"); } else { LOG("%s could not be found in resServer or res. Looking for %s to convert to sqlite.", - (BinaryPathFinder::GetBinaryDir() / "resServer" / "CDServer.sqlite").c_str(), - (Game::assetManager->GetResPath() / "cdclient.fdb").c_str()); + (resServerPath / "CDServer.sqlite").string().c_str(), + (Game::assetManager->GetResPath() / "cdclient.fdb").string().c_str()); auto cdclientStream = Game::assetManager->GetFile("cdclient.fdb"); if (!cdclientStream) { - LOG("Failed to load %s", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str()); + LOG("Failed to load %s", (Game::assetManager->GetResPath() / "cdclient.fdb").string().c_str()); throw std::runtime_error("Aborting initialization due to missing cdclient.fdb."); } - LOG("Found %s. Converting to SQLite", (Game::assetManager->GetResPath() / "cdclient.fdb").c_str()); + LOG("Found %s. Converting to SQLite", (Game::assetManager->GetResPath() / "cdclient.fdb").string().c_str()); Game::logger->Flush(); - if (FdbToSqlite::Convert((BinaryPathFinder::GetBinaryDir() / "resServer").string()).ConvertDatabase(cdclientStream) == false) { + if (FdbToSqlite::Convert(resServerPath.string()).ConvertDatabase(cdclientStream) == false) { LOG("Failed to convert fdb to sqlite."); return EXIT_FAILURE; } @@ -284,9 +294,9 @@ int main(int argc, char** argv) { uint32_t maxClients = 999; uint32_t ourPort = 1000; if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); - if (Game::config->GetValue("port") != "") ourPort = std::stoi(Game::config->GetValue("port")); + if (Game::config->GetValue("master_server_port") != "") ourPort = std::stoi(Game::config->GetValue("master_server_port")); - Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::shouldShutdown); + Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, true, false, Game::logger, "", 0, ServerType::Master, Game::config, &Game::lastSignal); //Query for the database for a server labeled "master" @@ -321,7 +331,8 @@ int main(int argc, char** argv) { uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceKillUniverseCommand = 0; - while (true) { + Game::logger->Flush(); + while (!Game::ShouldShutdown()) { //In world we'd update our other systems here. //Check for packets here: @@ -355,10 +366,10 @@ int main(int argc, char** argv) { framesSinceLastSQLPing++; //10m shutdown for universe kill command - if (Game::shouldShutdown) { + if (Game::universeShutdownRequested) { if (framesSinceKillUniverseCommand >= shutdownUniverseTime) { //Break main loop and exit - break; + Game::lastSignal = -1; } else framesSinceKillUniverseCommand++; } @@ -402,7 +413,7 @@ int main(int argc, char** argv) { t += std::chrono::milliseconds(masterFrameDelta); std::this_thread::sleep_until(t); } - return FinalizeShutdown(EXIT_SUCCESS); + return ShutdownSequence(EXIT_SUCCESS); } Logger* SetupLogger() { @@ -799,7 +810,7 @@ void HandlePacket(Packet* packet) { case eMasterMessageType::SHUTDOWN_UNIVERSE: { LOG("Received shutdown universe command, shutting down in 10 minutes."); - Game::shouldShutdown = true; + Game::universeShutdownRequested = true; break; } @@ -809,9 +820,12 @@ void HandlePacket(Packet* packet) { } } -void ShutdownSequence(int32_t signal) { +int ShutdownSequence(int32_t signal) { + if (!Game::logger) return -1; + LOG("Recieved Signal %d", signal); if (shutdownSequenceStarted) { - return; + LOG("Duplicate Shutdown Sequence"); + return -1; } if (!Game::im) { @@ -820,7 +834,7 @@ void ShutdownSequence(int32_t signal) { Game::im->SetIsShuttingDown(true); shutdownSequenceStarted = true; - Game::shouldShutdown = true; + Game::lastSignal = -1; { CBITSTREAM; @@ -889,16 +903,20 @@ void ShutdownSequence(int32_t signal) { } } - FinalizeShutdown(signal); + return FinalizeShutdown(signal); } int32_t FinalizeShutdown(int32_t signal) { //Delete our objects here: Database::Destroy("MasterServer"); if (Game::config) delete Game::config; + Game::config = nullptr; if (Game::im) delete Game::im; + Game::im = nullptr; if (Game::server) delete Game::server; + Game::server = nullptr; if (Game::logger) delete Game::logger; + Game::logger = nullptr; if (signal != EXIT_SUCCESS) exit(signal); return signal; diff --git a/dMasterServer/Start.cpp b/dMasterServer/Start.cpp index 8ef571f3..1fb9c212 100644 --- a/dMasterServer/Start.cpp +++ b/dMasterServer/Start.cpp @@ -5,7 +5,7 @@ #include "BinaryPathFinder.h" void StartChatServer() { - if (Game::shouldShutdown) { + if (Game::ShouldShutdown()) { LOG("Currently shutting down. Chat will not be restarted."); return; } @@ -24,7 +24,7 @@ void StartChatServer() { } void StartAuthServer() { - if (Game::shouldShutdown) { + if (Game::ShouldShutdown()) { LOG("Currently shutting down. Auth will not be restarted."); return; } diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index 8f0542cc..9ddf7bf7 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -55,7 +55,9 @@ void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, c RakNet::BitStream bitStream; BitStreamUtils::WriteHeader(bitStream, eConnectionType::SERVER, eServerMessageType::VERSION_CONFIRM); uint32_t netVersion; - if (!GeneralUtils::TryParse(Game::config->GetValue("client_net_version"), netVersion)) { + const std::string& expectedVersion = Game::config->GetValue("client_net_version"); + LOG("Expected Version: '%s'", expectedVersion.c_str()); + if (!GeneralUtils::TryParse(expectedVersion, netVersion)) { LOG("Failed to parse client_net_version. Cannot authenticate to %s:%i", nextServerIP.c_str(), nextServerPort); return; } diff --git a/dNet/CMakeLists.txt b/dNet/CMakeLists.txt index 938c0449..b4b77c82 100644 --- a/dNet/CMakeLists.txt +++ b/dNet/CMakeLists.txt @@ -8,4 +8,10 @@ set(DNET_SOURCES "AuthPackets.cpp" "ZoneInstanceManager.cpp") add_library(dNet STATIC ${DNET_SOURCES}) -target_link_libraries(dNet dCommon dDatabase) +target_include_directories(dNet PRIVATE + ${PROJECT_SOURCE_DIR}/dGame/dComponents + ${PROJECT_SOURCE_DIR}/dScripts # transitive through components +) +target_link_libraries(dNet + PUBLIC dCommon dDatabase + INTERFACE dZoneManager) diff --git a/dNet/dServer.cpp b/dNet/dServer.cpp index 9a3955b6..702a2d44 100644 --- a/dNet/dServer.cpp +++ b/dNet/dServer.cpp @@ -39,7 +39,7 @@ public: } } ReceiveDownloadCompleteCB; -dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, bool* shouldShutdown, unsigned int zoneID) { +dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnections, bool isInternal, bool useEncryption, Logger* logger, const std::string masterIP, int masterPort, ServerType serverType, dConfig* config, Game::signal_t* lastSignal, unsigned int zoneID) { mIP = ip; mPort = port; mZoneID = zoneID; @@ -55,7 +55,7 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect mReplicaManager = nullptr; mServerType = serverType; mConfig = config; - mShouldShutdown = shouldShutdown; + mShouldShutdown = lastSignal; //Attempt to start our server here: mIsOkay = Startup(); @@ -75,7 +75,9 @@ dServer::dServer(const std::string& ip, int port, int instanceID, int maxConnect //Connect to master if we are not master: if (serverType != ServerType::Master) { SetupForMasterConnection(); - ConnectToMaster(); + if (!ConnectToMaster()) { + LOG("Failed ConnectToMaster!"); + } } //Set up Replica if we're a world server: @@ -129,7 +131,7 @@ Packet* dServer::ReceiveFromMaster() { break; } case eMasterMessageType::SHUTDOWN: - *mShouldShutdown = true; + *mShouldShutdown = -2; break; //When we handle these packets in World instead dServer, we just return the packet's pointer. @@ -236,10 +238,12 @@ void dServer::Shutdown() { void dServer::SetupForMasterConnection() { mMasterSocketDescriptor = SocketDescriptor(uint16_t(mPort + 1), 0); mMasterPeer = RakNetworkFactory::GetRakPeerInterface(); - mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1); + bool ret = mMasterPeer->Startup(1, 30, &mMasterSocketDescriptor, 1); + if (!ret) LOG("Failed MasterPeer Startup!"); } bool dServer::ConnectToMaster() { + //LOG("Connection to Master %s:%d", mMasterIP.c_str(), mMasterPort); return mMasterPeer->Connect(mMasterIP.c_str(), mMasterPort, "3.25 DARKFLAME1", 15); } diff --git a/dNet/dServer.h b/dNet/dServer.h index 5df5bddc..b0a3e11d 100644 --- a/dNet/dServer.h +++ b/dNet/dServer.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "RakPeerInterface.h" #include "ReplicaManager.h" #include "NetworkIDManager.h" @@ -15,6 +16,10 @@ enum class ServerType : uint32_t { World }; +namespace Game { + using signal_t = volatile std::sig_atomic_t; +} + class dServer { public: // Default constructor should only used for testing! @@ -31,7 +36,7 @@ public: int masterPort, ServerType serverType, dConfig* config, - bool* shouldShutdown, + Game::signal_t* shouldShutdown, unsigned int zoneID = 0); ~dServer(); @@ -81,9 +86,9 @@ private: NetworkIDManager* mNetIDManager = nullptr; /** - * Whether or not to shut down the server. Pointer to Game::shouldShutdown. + * Whether or not to shut down the server. Pointer to Game::lastSignal. */ - bool* mShouldShutdown = nullptr; + Game::signal_t* mShouldShutdown = nullptr; SocketDescriptor mSocketDescriptor; std::string mIP; int mPort; diff --git a/dPhysics/CMakeLists.txt b/dPhysics/CMakeLists.txt index 5fe6adaa..340e4c3c 100644 --- a/dPhysics/CMakeLists.txt +++ b/dPhysics/CMakeLists.txt @@ -7,4 +7,6 @@ set(DPHYSICS_SOURCES "dpCollisionChecks.cpp" "dpWorld.cpp") add_library(dPhysics STATIC ${DPHYSICS_SOURCES}) -target_link_libraries(dPhysics Recast Detour) +target_link_libraries(dPhysics + PUBLIC Recast Detour + INTERFACE dNavigation) diff --git a/dScripts/02_server/CMakeLists.txt b/dScripts/02_server/CMakeLists.txt index 1e38386f..51eb24c8 100644 --- a/dScripts/02_server/CMakeLists.txt +++ b/dScripts/02_server/CMakeLists.txt @@ -8,10 +8,6 @@ endforeach() add_subdirectory(Enemy) -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_ENEMY}) - set(DSCRIPTS_SOURCES_02_SERVER ${DSCRIPTS_SOURCES_02_SERVER} "Enemy/${file}") -endforeach() - add_subdirectory(Equipment) foreach(file ${DSCRIPTS_SOURCES_02_SERVER_EQUIPMENT}) @@ -20,10 +16,6 @@ endforeach() add_subdirectory(Map) -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP}) - set(DSCRIPTS_SOURCES_02_SERVER ${DSCRIPTS_SOURCES_02_SERVER} "Map/${file}") -endforeach() - add_subdirectory(Minigame) foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MINIGAME}) @@ -38,8 +30,15 @@ endforeach() add_subdirectory(Pets) -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_PETS}) - set(DSCRIPTS_SOURCES_02_SERVER ${DSCRIPTS_SOURCES_02_SERVER} "Pets/${file}") -endforeach() - -set(DSCRIPTS_SOURCES_02_SERVER ${DSCRIPTS_SOURCES_02_SERVER} PARENT_SCOPE) +add_library(dScriptsServer STATIC ${DSCRIPTS_SOURCES_02_SERVER}) +target_include_directories(dScriptsServer PUBLIC "." + "DLU" + "Equipment" + "Minigame" + "Minigame/General" + "Objects" + "Pets") +target_link_libraries(dScriptsServer INTERFACE + dScriptsServerEnemy + dScriptsServerMap) +target_precompile_headers(dScriptsServer REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Enemy/CMakeLists.txt b/dScripts/02_server/Enemy/CMakeLists.txt index 408ff733..62f61772 100644 --- a/dScripts/02_server/Enemy/CMakeLists.txt +++ b/dScripts/02_server/Enemy/CMakeLists.txt @@ -42,4 +42,15 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_ENEMY_WAVES}) set(DSCRIPTS_SOURCES_02_SERVER_ENEMY ${DSCRIPTS_SOURCES_02_SERVER_ENEMY} "Waves/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_ENEMY ${DSCRIPTS_SOURCES_02_SERVER_ENEMY} PARENT_SCOPE) +add_library(dScriptsServerEnemy STATIC ${DSCRIPTS_SOURCES_02_SERVER_ENEMY}) +target_link_libraries(dScriptsServerEnemy dScriptsBase) +target_include_directories(dScriptsServerEnemy PUBLIC "." + "AG" + "AM" + "FV" + "General" + "Survival" + "VE" + "Waves" +) +target_precompile_headers(dScriptsServerEnemy REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/AG/CMakeLists.txt b/dScripts/02_server/Map/AG/CMakeLists.txt index df26dee4..a8315398 100644 --- a/dScripts/02_server/Map/AG/CMakeLists.txt +++ b/dScripts/02_server/Map/AG/CMakeLists.txt @@ -12,5 +12,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_AG "AgMonumentRaceGoal.cpp" "AgMonumentRaceCancel.cpp" "NpcCowboyServer.cpp" - "NpcPirateServer.cpp" - PARENT_SCOPE) + "NpcPirateServer.cpp") + +add_library(dScriptsServerMapAG ${DSCRIPTS_SOURCES_02_SERVER_MAP_AG}) +target_include_directories(dScriptsServerMapAG PUBLIC ".") +target_precompile_headers(dScriptsServerMapAG REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/AG_Spider_Queen/CMakeLists.txt b/dScripts/02_server/Map/AG_Spider_Queen/CMakeLists.txt index f4204c13..65019afe 100644 --- a/dScripts/02_server/Map/AG_Spider_Queen/CMakeLists.txt +++ b/dScripts/02_server/Map/AG_Spider_Queen/CMakeLists.txt @@ -1,4 +1,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_AG_SPIDER_QUEEN "ZoneAgSpiderQueen.cpp" - "SpiderBossTreasureChestServer.cpp" - PARENT_SCOPE) + "SpiderBossTreasureChestServer.cpp") + +add_library(dScriptsServerMapAGSpiderQueen ${DSCRIPTS_SOURCES_02_SERVER_MAP_AG_SPIDER_QUEEN}) +target_include_directories(dScriptsServerMapAGSpiderQueen PUBLIC ".") +target_link_libraries(dScriptsServerMapAGSpiderQueen dScriptsServerMapProperty) +target_precompile_headers(dScriptsServerMapAGSpiderQueen REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/AM/CMakeLists.txt b/dScripts/02_server/Map/AM/CMakeLists.txt index d3d13b73..177b3c45 100644 --- a/dScripts/02_server/Map/AM/CMakeLists.txt +++ b/dScripts/02_server/Map/AM/CMakeLists.txt @@ -15,5 +15,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_AM "AmSkullkinDrillStand.cpp" "AmSkullkinTower.cpp" "AmBlueX.cpp" - "AmTeapotServer.cpp" - PARENT_SCOPE) + "AmTeapotServer.cpp") + +add_library(dScriptsServerMapAM ${DSCRIPTS_SOURCES_02_SERVER_MAP_AM}) +target_include_directories(dScriptsServerMapAM PUBLIC ".") +target_precompile_headers(dScriptsServerMapAM REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/CMakeLists.txt b/dScripts/02_server/Map/CMakeLists.txt index feed8a97..a5fb5b03 100644 --- a/dScripts/02_server/Map/CMakeLists.txt +++ b/dScripts/02_server/Map/CMakeLists.txt @@ -1,81 +1,29 @@ -set(DSCRIPTS_SOURCES_02_SERVER_MAP) - add_subdirectory(AG) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_AG}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "AG/${file}") -endforeach() - add_subdirectory(AG_Spider_Queen) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_AG_SPIDER_QUEEN}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "AG_Spider_Queen/${file}") -endforeach() - add_subdirectory(AM) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_AM}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "AM/${file}") -endforeach() - add_subdirectory(FV) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_FV}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "FV/${file}") -endforeach() - add_subdirectory(General) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "General/${file}") -endforeach() - add_subdirectory(GF) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_GF}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "GF/${file}") -endforeach() - add_subdirectory(njhub) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "njhub/${file}") -endforeach() - add_subdirectory(NS) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_NS}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "NS/${file}") -endforeach() - add_subdirectory(NT) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_NT}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "NT/${file}") -endforeach() - add_subdirectory(PR) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_PR}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "PR/${file}") -endforeach() - add_subdirectory(Property) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "Property/${file}") -endforeach() - add_subdirectory(SS) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_SS}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "SS/${file}") -endforeach() - add_subdirectory(VE) -foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_VE}) - set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} "VE/${file}") -endforeach() - -set(DSCRIPTS_SOURCES_02_SERVER_MAP ${DSCRIPTS_SOURCES_02_SERVER_MAP} PARENT_SCOPE) +add_library(dScriptsServerMap INTERFACE) +target_link_libraries(dScriptsServerMap INTERFACE + dScriptsServerMapAG + dScriptsServerMapAGSpiderQueen + dScriptsServerMapAM + dScriptsServerMapFV + dScriptsServerMapGeneral + dScriptsServerMapGF + dScriptsServerMapNJHub + dScriptsServerMapNS + dScriptsServerMapNT + dScriptsServerMapPR + dScriptsServerMapProperty + dScriptsServerMapSS + dScriptsServerMapVE) diff --git a/dScripts/02_server/Map/FV/CMakeLists.txt b/dScripts/02_server/Map/FV/CMakeLists.txt index 505d97c4..6f774c98 100644 --- a/dScripts/02_server/Map/FV/CMakeLists.txt +++ b/dScripts/02_server/Map/FV/CMakeLists.txt @@ -11,4 +11,6 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_FV_RACING}) set(DSCRIPTS_SOURCES_02_SERVER_MAP_FV ${DSCRIPTS_SOURCES_02_SERVER_MAP_FV} "Racing/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_MAP_FV ${DSCRIPTS_SOURCES_02_SERVER_MAP_FV} PARENT_SCOPE) +add_library(dScriptsServerMapFV ${DSCRIPTS_SOURCES_02_SERVER_MAP_FV}) +target_include_directories(dScriptsServerMapFV PUBLIC "." "Racing") +target_precompile_headers(dScriptsServerMapFV REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/GF/CMakeLists.txt b/dScripts/02_server/Map/GF/CMakeLists.txt index 90cc38a5..45ec871a 100644 --- a/dScripts/02_server/Map/GF/CMakeLists.txt +++ b/dScripts/02_server/Map/GF/CMakeLists.txt @@ -2,5 +2,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_GF "GfTikiTorch.cpp" "GfCaptainsCannon.cpp" "MastTeleport.cpp" - "SpawnLionServer.cpp" - PARENT_SCOPE) + "SpawnLionServer.cpp") + +add_library(dScriptsServerMapGF ${DSCRIPTS_SOURCES_02_SERVER_MAP_GF}) +target_include_directories(dScriptsServerMapGF PUBLIC ".") +target_precompile_headers(dScriptsServerMapGF REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/General/CMakeLists.txt b/dScripts/02_server/Map/General/CMakeLists.txt index 797b53ed..4fe5aae8 100644 --- a/dScripts/02_server/Map/General/CMakeLists.txt +++ b/dScripts/02_server/Map/General/CMakeLists.txt @@ -7,7 +7,6 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL "ForceVolumeServer.cpp" "GrowingFlower.cpp" "ImaginationBackpackHealServer.cpp" - "InvalidScript.cpp" "MailBoxServer.cpp" "NjRailSwitch.cpp" "PetDigServer.cpp" @@ -28,4 +27,6 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL_NINJAGO}) set(DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL ${DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL} "Ninjago/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL ${DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL} PARENT_SCOPE) +add_library(dScriptsServerMapGeneral ${DSCRIPTS_SOURCES_02_SERVER_MAP_GENERAL}) +target_include_directories(dScriptsServerMapGeneral PUBLIC "." "Ninjago") +target_precompile_headers(dScriptsServerMapGeneral REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/NS/CMakeLists.txt b/dScripts/02_server/Map/NS/CMakeLists.txt index c815a8c5..4927f0c8 100644 --- a/dScripts/02_server/Map/NS/CMakeLists.txt +++ b/dScripts/02_server/Map/NS/CMakeLists.txt @@ -10,4 +10,6 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_NS_WAVES}) set(DSCRIPTS_SOURCES_02_SERVER_MAP_NS ${DSCRIPTS_SOURCES_02_SERVER_MAP_NS} "Waves/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_MAP_NS ${DSCRIPTS_SOURCES_02_SERVER_MAP_NS} PARENT_SCOPE) +add_library(dScriptsServerMapNS ${DSCRIPTS_SOURCES_02_SERVER_MAP_NS}) +target_include_directories(dScriptsServerMapNS PUBLIC "." "Waves") +target_precompile_headers(dScriptsServerMapNS REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/NT/CMakeLists.txt b/dScripts/02_server/Map/NT/CMakeLists.txt index 533a70c9..49c6a5ae 100644 --- a/dScripts/02_server/Map/NT/CMakeLists.txt +++ b/dScripts/02_server/Map/NT/CMakeLists.txt @@ -25,5 +25,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_NT "NtVentureSpeedPadServer.cpp" "NtVentureCannonServer.cpp" "NtBcSubmitServer.cpp" - "NtNaomiBreadcrumbServer.cpp" - PARENT_SCOPE) + "NtNaomiBreadcrumbServer.cpp") + +add_library(dScriptsServerMapNT ${DSCRIPTS_SOURCES_02_SERVER_MAP_NT}) +target_include_directories(dScriptsServerMapNT PUBLIC ".") +target_precompile_headers(dScriptsServerMapNT REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/PR/CMakeLists.txt b/dScripts/02_server/Map/PR/CMakeLists.txt index 3a32e9f3..13b3fd35 100644 --- a/dScripts/02_server/Map/PR/CMakeLists.txt +++ b/dScripts/02_server/Map/PR/CMakeLists.txt @@ -1,5 +1,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_PR "HydrantBroken.cpp" "PrSeagullFly.cpp" - "SpawnGryphonServer.cpp" - PARENT_SCOPE) + "SpawnGryphonServer.cpp") + +add_library(dScriptsServerMapPR ${DSCRIPTS_SOURCES_02_SERVER_MAP_PR}) +target_include_directories(dScriptsServerMapPR PUBLIC ".") +target_precompile_headers(dScriptsServerMapPR REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/Property/CMakeLists.txt b/dScripts/02_server/Map/Property/CMakeLists.txt index 74badb32..52b91d0b 100644 --- a/dScripts/02_server/Map/Property/CMakeLists.txt +++ b/dScripts/02_server/Map/Property/CMakeLists.txt @@ -19,4 +19,9 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY_NS_MED}) set(DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY ${DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY} "NS_Med/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY ${DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY} PARENT_SCOPE) +add_library(dScriptsServerMapProperty ${DSCRIPTS_SOURCES_02_SERVER_MAP_PROPERTY}) +target_precompile_headers(dScriptsServerMapProperty REUSE_FROM dScriptsBase) +target_include_directories(dScriptsServerMapProperty PUBLIC "." + "AG_Med" + "AG_Small" + "NS_Med") diff --git a/dScripts/02_server/Map/SS/CMakeLists.txt b/dScripts/02_server/Map/SS/CMakeLists.txt index 49db031f..ed6a7596 100644 --- a/dScripts/02_server/Map/SS/CMakeLists.txt +++ b/dScripts/02_server/Map/SS/CMakeLists.txt @@ -1,3 +1,6 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_SS - "SsModularBuildServer.cpp" - PARENT_SCOPE) + "SsModularBuildServer.cpp") + +add_library(dScriptsServerMapSS ${DSCRIPTS_SOURCES_02_SERVER_MAP_SS}) +target_include_directories(dScriptsServerMapSS PUBLIC ".") +target_precompile_headers(dScriptsServerMapSS REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/VE/CMakeLists.txt b/dScripts/02_server/Map/VE/CMakeLists.txt index ac3a6461..2dbcaaff 100644 --- a/dScripts/02_server/Map/VE/CMakeLists.txt +++ b/dScripts/02_server/Map/VE/CMakeLists.txt @@ -1,5 +1,8 @@ set(DSCRIPTS_SOURCES_02_SERVER_MAP_VE "VeMissionConsole.cpp" "VeEpsilonServer.cpp" - "VeBricksampleServer.cpp" - PARENT_SCOPE) + "VeBricksampleServer.cpp") + +add_library(dScriptsServerMapVE ${DSCRIPTS_SOURCES_02_SERVER_MAP_VE}) +target_include_directories(dScriptsServerMapVE PUBLIC ".") +target_precompile_headers(dScriptsServerMapVE REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/njhub/CMakeLists.txt b/dScripts/02_server/Map/njhub/CMakeLists.txt index 2527d1ad..0f287ce3 100644 --- a/dScripts/02_server/Map/njhub/CMakeLists.txt +++ b/dScripts/02_server/Map/njhub/CMakeLists.txt @@ -28,4 +28,11 @@ foreach(file ${DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB_BOSS_INSTANCE}) set(DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB ${DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB} "boss_instance/${file}") endforeach() -set(DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB ${DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB} PARENT_SCOPE) +add_library(dScriptsServerMapNJHub ${DSCRIPTS_SOURCES_02_SERVER_MAP_NJHUB}) +target_include_directories(dScriptsServerMapNJHub PUBLIC "." "boss_instance") +target_link_libraries(dScriptsServerMapNJHub + dScriptsServerPets + dScriptsServerMapAM + dScriptsServerMapGeneral +) +target_precompile_headers(dScriptsServerMapNJHub REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Pets/CMakeLists.txt b/dScripts/02_server/Pets/CMakeLists.txt index 8820a82e..79123ebe 100644 --- a/dScripts/02_server/Pets/CMakeLists.txt +++ b/dScripts/02_server/Pets/CMakeLists.txt @@ -1,5 +1,9 @@ set(DSCRIPTS_SOURCES_02_SERVER_PETS "PetFromDigServer.cpp" "PetFromObjectServer.cpp" - "DamagingPets.cpp" - PARENT_SCOPE) + "DamagingPets.cpp") + +add_library(dScriptsServerPets STATIC ${DSCRIPTS_SOURCES_02_SERVER_PETS}) +target_include_directories(dScriptsServerPets PUBLIC ".") +target_precompile_headers(dScriptsServerPets REUSE_FROM dScriptsBase) + diff --git a/dScripts/CMakeLists.txt b/dScripts/CMakeLists.txt index ac600dbc..b3fb7d44 100644 --- a/dScripts/CMakeLists.txt +++ b/dScripts/CMakeLists.txt @@ -7,48 +7,44 @@ set(DSCRIPTS_SOURCES "BaseWavesGenericEnemy.cpp" "BaseWavesServer.cpp" "ChooseYourDestinationNsToNt.cpp" - "CppScripts.cpp" "Darkitect.cpp" + "InvalidScript.cpp" "NPCAddRemoveItem.cpp" "NtFactionSpyServer.cpp" "ScriptComponent.cpp" "ScriptedPowerupSpawner.cpp" "SpawnPetBaseServer.cpp") +link_libraries(dDatabase dPhysics) + +add_library(dScriptsBase STATIC ${DSCRIPTS_SOURCES}) +target_include_directories(dScriptsBase PUBLIC .) +target_link_libraries(dScriptsBase + INTERFACE dGameBase) +target_precompile_headers(dScriptsBase PRIVATE ${HEADERS_DGAME}) + +include_directories( + ${PROJECT_SOURCE_DIR}/dScripts + ${PROJECT_SOURCE_DIR}/dGame +) +link_libraries(dScriptsBase) +# dComponents + add_subdirectory(02_server) - -foreach(file ${DSCRIPTS_SOURCES_02_SERVER}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "02_server/${file}") -endforeach() - add_subdirectory(ai) - -foreach(file ${DSCRIPTS_SOURCES_AI}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "ai/${file}") -endforeach() - add_subdirectory(client) - -foreach(file ${DSCRIPTS_SOURCES_CLIENT}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "client/${file}") -endforeach() - add_subdirectory(EquipmentScripts) - -foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTSCRIPTS}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentScripts/${file}") -endforeach() - add_subdirectory(EquipmentTriggers) - -foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentTriggers/${file}") -endforeach() - add_subdirectory(zone) -foreach(file ${DSCRIPTS_SOURCES_ZONE}) - set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "zone/${file}") -endforeach() - -set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} PARENT_SCOPE) +add_library(dScripts STATIC "CppScripts.cpp") +target_precompile_headers(dScripts REUSE_FROM dScriptsBase) +target_include_directories(dScripts PUBLIC ".") +target_link_libraries(dScripts + dScriptsBase + dScriptsServer + dScriptsAI + dScriptsClient + dScriptsEquipmentScripts + dScriptsEquipmentTriggers + dScriptsZone) diff --git a/dScripts/EquipmentScripts/CMakeLists.txt b/dScripts/EquipmentScripts/CMakeLists.txt index 696be03d..08966e08 100644 --- a/dScripts/EquipmentScripts/CMakeLists.txt +++ b/dScripts/EquipmentScripts/CMakeLists.txt @@ -6,5 +6,8 @@ set(DSCRIPTS_SOURCES_EQUIPMENTSCRIPTS "PersonalFortress.cpp" "BuccaneerValiantShip.cpp" "FireFirstSkillonStartup.cpp" - "StunImmunity.cpp" - PARENT_SCOPE) + "StunImmunity.cpp") + +add_library(dScriptsEquipmentScripts STATIC ${DSCRIPTS_SOURCES_EQUIPMENTSCRIPTS}) +target_include_directories(dScriptsEquipmentScripts PUBLIC ".") +target_precompile_headers(dScriptsEquipmentScripts REUSE_FROM dScriptsBase) diff --git a/dScripts/EquipmentTriggers/CMakeLists.txt b/dScripts/EquipmentTriggers/CMakeLists.txt index 416ef553..cb6e81a8 100644 --- a/dScripts/EquipmentTriggers/CMakeLists.txt +++ b/dScripts/EquipmentTriggers/CMakeLists.txt @@ -1,3 +1,6 @@ set(DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS - "CoilBackpackBase.cpp" - PARENT_SCOPE) + "CoilBackpackBase.cpp") + +add_library(dScriptsEquipmentTriggers STATIC ${DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS}) +target_include_directories(dScriptsEquipmentTriggers PUBLIC ".") +target_precompile_headers(dScriptsEquipmentTriggers REUSE_FROM dScriptsBase) diff --git a/dScripts/02_server/Map/General/InvalidScript.cpp b/dScripts/InvalidScript.cpp similarity index 100% rename from dScripts/02_server/Map/General/InvalidScript.cpp rename to dScripts/InvalidScript.cpp diff --git a/dScripts/02_server/Map/General/InvalidScript.h b/dScripts/InvalidScript.h similarity index 100% rename from dScripts/02_server/Map/General/InvalidScript.h rename to dScripts/InvalidScript.h diff --git a/dScripts/ai/ACT/CMakeLists.txt b/dScripts/ai/ACT/CMakeLists.txt index 79deeded..5071afa0 100644 --- a/dScripts/ai/ACT/CMakeLists.txt +++ b/dScripts/ai/ACT/CMakeLists.txt @@ -9,4 +9,6 @@ foreach(file ${DSCRIPTS_SOURCES_AI_ACT_FOOTRACE}) set(DSCRIPTS_SOURCES_AI_ACT ${DSCRIPTS_SOURCES_AI_ACT} "FootRace/${file}") endforeach() -set(DSCRIPTS_SOURCES_AI_ACT ${DSCRIPTS_SOURCES_AI_ACT} PARENT_SCOPE) +add_library(dScriptsAiAct STATIC ${DSCRIPTS_SOURCES_AI_ACT}) +target_include_directories(dScriptsAiAct PUBLIC "." "FootRace") +target_precompile_headers(dScriptsAiAct REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/AG/CMakeLists.txt b/dScripts/ai/AG/CMakeLists.txt index 092b8de7..e031e386 100644 --- a/dScripts/ai/AG/CMakeLists.txt +++ b/dScripts/ai/AG/CMakeLists.txt @@ -14,5 +14,8 @@ set(DSCRIPTS_SOURCES_AI_AG "AgDarkSpiderling.cpp" "AgPicnicBlanket.cpp" "AgStagePlatforms.cpp" - "AgQbWall.cpp" - PARENT_SCOPE) + "AgQbWall.cpp") + +add_library(dScriptsAiAG STATIC ${DSCRIPTS_SOURCES_AI_AG}) +target_include_directories(dScriptsAiAG PUBLIC ".") +target_precompile_headers(dScriptsAiAG REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/CMakeLists.txt b/dScripts/ai/CMakeLists.txt index 44944b90..7e94cd34 100644 --- a/dScripts/ai/CMakeLists.txt +++ b/dScripts/ai/CMakeLists.txt @@ -1,81 +1,32 @@ set(DSCRIPTS_SOURCES_AI) add_subdirectory(ACT) - -foreach(file ${DSCRIPTS_SOURCES_AI_ACT}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "ACT/${file}") -endforeach() - add_subdirectory(AG) - -foreach(file ${DSCRIPTS_SOURCES_AI_AG}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "AG/${file}") -endforeach() - add_subdirectory(FV) - -foreach(file ${DSCRIPTS_SOURCES_AI_FV}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "FV/${file}") -endforeach() - add_subdirectory(GENERAL) - -foreach(file ${DSCRIPTS_SOURCES_AI_GENERAL}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "GENERAL/${file}") -endforeach() - add_subdirectory(GF) - -foreach(file ${DSCRIPTS_SOURCES_AI_GF}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "GF/${file}") -endforeach() - add_subdirectory(MINIGAME) - -foreach(file ${DSCRIPTS_SOURCES_AI_MINIGAME}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "MINIGAME/${file}") -endforeach() - add_subdirectory(NP) - -foreach(file ${DSCRIPTS_SOURCES_AI_NP}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "NP/${file}") -endforeach() - add_subdirectory(NS) - -foreach(file ${DSCRIPTS_SOURCES_AI_NS}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "NS/${file}") -endforeach() - add_subdirectory(PETS) - -foreach(file ${DSCRIPTS_SOURCES_AI_PETS}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "PETS/${file}") -endforeach() - add_subdirectory(PROPERTY) - -foreach(file ${DSCRIPTS_SOURCES_AI_PROPERTY}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "PROPERTY/${file}") -endforeach() - add_subdirectory(RACING) - -foreach(file ${DSCRIPTS_SOURCES_AI_RACING}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "RACING/${file}") -endforeach() - add_subdirectory(SPEC) - -foreach(file ${DSCRIPTS_SOURCES_AI_SPEC}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "SPEC/${file}") -endforeach() - add_subdirectory(WILD) -foreach(file ${DSCRIPTS_SOURCES_AI_WILD}) - set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} "WILD/${file}") -endforeach() - -set(DSCRIPTS_SOURCES_AI ${DSCRIPTS_SOURCES_AI} PARENT_SCOPE) +add_library(dScriptsAI INTERFACE) +target_link_libraries(dScriptsAI INTERFACE + dScriptsAiAct + dScriptsAiAG + dScriptsAiFV + dScriptsAiGeneral + dScriptsAiGF + dScriptsAiMinigame + dScriptsAiNP + dScriptsAiNS + dScriptsAiPets + dScriptsAiProperty + dScriptsAiRacing + dScriptsAiSpec + dScriptsAiWild +) diff --git a/dScripts/ai/FV/CMakeLists.txt b/dScripts/ai/FV/CMakeLists.txt index 56418706..55c68a27 100644 --- a/dScripts/ai/FV/CMakeLists.txt +++ b/dScripts/ai/FV/CMakeLists.txt @@ -16,5 +16,9 @@ set(DSCRIPTS_SOURCES_AI_FV "FvPassThroughWall.cpp" "FvBounceOverWall.cpp" "FvMaelstromGeyser.cpp" - "TriggerGas.cpp" - PARENT_SCOPE) + "TriggerGas.cpp") + +add_library(dScriptsAiFV STATIC ${DSCRIPTS_SOURCES_AI_FV}) +target_include_directories(dScriptsAiFV PUBLIC ".") +target_precompile_headers(dScriptsAiFV REUSE_FROM dScriptsBase) + diff --git a/dScripts/ai/GENERAL/CMakeLists.txt b/dScripts/ai/GENERAL/CMakeLists.txt index da973658..9b7f2a2a 100644 --- a/dScripts/ai/GENERAL/CMakeLists.txt +++ b/dScripts/ai/GENERAL/CMakeLists.txt @@ -1,4 +1,8 @@ set(DSCRIPTS_SOURCES_AI_GENERAL "InstanceExitTransferPlayerToLastNonInstance.cpp" - "LegoDieRoll.cpp" - PARENT_SCOPE) + "LegoDieRoll.cpp") + +add_library(dScriptsAiGeneral STATIC ${DSCRIPTS_SOURCES_AI_GENERAL}) +target_include_directories(dScriptsAiGeneral PUBLIC ".") +target_precompile_headers(dScriptsAiGeneral REUSE_FROM dScriptsBase) + diff --git a/dScripts/ai/GF/CMakeLists.txt b/dScripts/ai/GF/CMakeLists.txt index 9937618c..7a7a1723 100644 --- a/dScripts/ai/GF/CMakeLists.txt +++ b/dScripts/ai/GF/CMakeLists.txt @@ -10,5 +10,8 @@ set(DSCRIPTS_SOURCES_AI_GF "GfArchway.cpp" "GfMaelstromGeyser.cpp" "PirateRep.cpp" - "GfParrotCrash.cpp" - PARENT_SCOPE) + "GfParrotCrash.cpp") + +add_library(dScriptsAiGF STATIC ${DSCRIPTS_SOURCES_AI_GF}) +target_include_directories(dScriptsAiGF PUBLIC ".") +target_precompile_headers(dScriptsAiGF REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/MINIGAME/CMakeLists.txt b/dScripts/ai/MINIGAME/CMakeLists.txt index f0187057..0e07d526 100644 --- a/dScripts/ai/MINIGAME/CMakeLists.txt +++ b/dScripts/ai/MINIGAME/CMakeLists.txt @@ -12,4 +12,6 @@ foreach(file ${DSCRIPTS_SOURCES_AI_MINIGAME_OBJECTS}) set(DSCRIPTS_SOURCES_AI_MINIGAME ${DSCRIPTS_SOURCES_AI_MINIGAME} "Objects/${file}") endforeach() -set(DSCRIPTS_SOURCES_AI_MINIGAME ${DSCRIPTS_SOURCES_AI_MINIGAME} PARENT_SCOPE) +add_library(dScriptsAiMinigame STATIC ${DSCRIPTS_SOURCES_AI_MINIGAME}) +target_include_directories(dScriptsAiMinigame PUBLIC "." "Objects" "SG_GF" "SG_GF/SERVER") +target_precompile_headers(dScriptsAiMinigame REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/NP/CMakeLists.txt b/dScripts/ai/NP/CMakeLists.txt index 39a7301a..9f3f6a65 100644 --- a/dScripts/ai/NP/CMakeLists.txt +++ b/dScripts/ai/NP/CMakeLists.txt @@ -1,3 +1,6 @@ set(DSCRIPTS_SOURCES_AI_NP - "NpcNpSpacemanBob.cpp" - PARENT_SCOPE) + "NpcNpSpacemanBob.cpp") + +add_library(dScriptsAiNP STATIC ${DSCRIPTS_SOURCES_AI_NP}) +target_include_directories(dScriptsAiNP PUBLIC ".") +target_precompile_headers(dScriptsAiNP REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/NS/CMakeLists.txt b/dScripts/ai/NS/CMakeLists.txt index e8ec84b1..0f0aa0c7 100644 --- a/dScripts/ai/NS/CMakeLists.txt +++ b/dScripts/ai/NS/CMakeLists.txt @@ -21,4 +21,8 @@ foreach(file ${DSCRIPTS_SOURCES_AI_NS_WH}) set(DSCRIPTS_SOURCES_AI_NS ${DSCRIPTS_SOURCES_AI_NS} "WH/${file}") endforeach() -set(DSCRIPTS_SOURCES_AI_NS ${DSCRIPTS_SOURCES_AI_NS} PARENT_SCOPE) +add_library(dScriptsAiNS STATIC ${DSCRIPTS_SOURCES_AI_NS}) +target_include_directories(dScriptsAiNS PUBLIC "." "NS_PP_01" "WH" +PRIVATE + ${PROJECT_SOURCE_DIR}/dScripts/02_server/Map/NS) # NsConcertChoiceBuildManager.h +target_precompile_headers(dScriptsAiNS REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/PETS/CMakeLists.txt b/dScripts/ai/PETS/CMakeLists.txt index 93a9012d..8bd8630c 100644 --- a/dScripts/ai/PETS/CMakeLists.txt +++ b/dScripts/ai/PETS/CMakeLists.txt @@ -1,3 +1,6 @@ set(DSCRIPTS_SOURCES_AI_PETS - "HydrantSmashable.cpp" - PARENT_SCOPE) + "HydrantSmashable.cpp") + +add_library(dScriptsAiPets STATIC ${DSCRIPTS_SOURCES_AI_PETS}) +target_include_directories(dScriptsAiPets PUBLIC "." "NS_PP_01" "WH") +target_precompile_headers(dScriptsAiPets REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/PROPERTY/CMakeLists.txt b/dScripts/ai/PROPERTY/CMakeLists.txt index 295137b4..a31b9257 100644 --- a/dScripts/ai/PROPERTY/CMakeLists.txt +++ b/dScripts/ai/PROPERTY/CMakeLists.txt @@ -8,4 +8,6 @@ foreach(file ${DSCRIPTS_SOURCES_AI_PROPERTY_AG}) set(DSCRIPTS_SOURCES_AI_PROPERTY ${DSCRIPTS_SOURCES_AI_PROPERTY} "AG/${file}") endforeach() -set(DSCRIPTS_SOURCES_AI_PROPERTY ${DSCRIPTS_SOURCES_AI_PROPERTY} PARENT_SCOPE) +add_library(dScriptsAiProperty STATIC ${DSCRIPTS_SOURCES_AI_PROPERTY}) +target_include_directories(dScriptsAiProperty PUBLIC "." "AG") +target_precompile_headers(dScriptsAiProperty REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/RACING/CMakeLists.txt b/dScripts/ai/RACING/CMakeLists.txt index 0c1918de..b7343c61 100644 --- a/dScripts/ai/RACING/CMakeLists.txt +++ b/dScripts/ai/RACING/CMakeLists.txt @@ -6,4 +6,6 @@ foreach(file ${DSCRIPTS_SOURCES_AI_RACING_OBJECTS}) set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} "OBJECTS/${file}") endforeach() -set(DSCRIPTS_SOURCES_AI_RACING ${DSCRIPTS_SOURCES_AI_RACING} PARENT_SCOPE) +add_library(dScriptsAiRacing STATIC ${DSCRIPTS_SOURCES_AI_RACING}) +target_include_directories(dScriptsAiRacing PUBLIC "." "OBJECTS") +target_precompile_headers(dScriptsAiRacing REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/SPEC/CMakeLists.txt b/dScripts/ai/SPEC/CMakeLists.txt index 42dbf8f8..c60372ac 100644 --- a/dScripts/ai/SPEC/CMakeLists.txt +++ b/dScripts/ai/SPEC/CMakeLists.txt @@ -1,5 +1,8 @@ set(DSCRIPTS_SOURCES_AI_SPEC "SpecialCoinSpawner.cpp" "SpecialPowerupSpawner.cpp" - "SpecialSpeedBuffSpawner.cpp" - PARENT_SCOPE) + "SpecialSpeedBuffSpawner.cpp") + +add_library(dScriptsAiSpec STATIC ${DSCRIPTS_SOURCES_AI_SPEC}) +target_include_directories(dScriptsAiSpec PUBLIC ".") +target_precompile_headers(dScriptsAiSpec REUSE_FROM dScriptsBase) diff --git a/dScripts/ai/WILD/CMakeLists.txt b/dScripts/ai/WILD/CMakeLists.txt index 446ce0d4..418ffb5f 100644 --- a/dScripts/ai/WILD/CMakeLists.txt +++ b/dScripts/ai/WILD/CMakeLists.txt @@ -7,5 +7,8 @@ set(DSCRIPTS_SOURCES_AI_WILD "WildNinjaBricks.cpp" "WildNinjaStudent.cpp" "WildNinjaSensei.cpp" - "WildPants.cpp" - PARENT_SCOPE) + "WildPants.cpp") + +add_library(dScriptsAiWild STATIC ${DSCRIPTS_SOURCES_AI_WILD}) +target_include_directories(dScriptsAiWild PUBLIC ".") +target_precompile_headers(dScriptsAiWild REUSE_FROM dScriptsBase) diff --git a/dScripts/client/CMakeLists.txt b/dScripts/client/CMakeLists.txt index c2777508..b3ad08fb 100644 --- a/dScripts/client/CMakeLists.txt +++ b/dScripts/client/CMakeLists.txt @@ -6,4 +6,6 @@ foreach(file ${DSCRIPTS_SOURCES_CLIENT_AI}) set(DSCRIPTS_SOURCES_CLIENT ${DSCRIPTS_SOURCES_CLIENT} "ai/${file}") endforeach() -set(DSCRIPTS_SOURCES_CLIENT ${DSCRIPTS_SOURCES_CLIENT} PARENT_SCOPE) +add_library(dScriptsClient STATIC ${DSCRIPTS_SOURCES_CLIENT}) +target_include_directories(dScriptsClient PUBLIC "." "ai" "ai/PR") +target_precompile_headers(dScriptsClient REUSE_FROM dScriptsBase) diff --git a/dScripts/zone/CMakeLists.txt b/dScripts/zone/CMakeLists.txt index 5d800031..93ea70ca 100644 --- a/dScripts/zone/CMakeLists.txt +++ b/dScripts/zone/CMakeLists.txt @@ -18,4 +18,12 @@ foreach(file ${DSCRIPTS_SOURCES_ZONE_PROPERTY}) set(DSCRIPTS_SOURCES_ZONE ${DSCRIPTS_SOURCES_ZONE} "PROPERTY/${file}") endforeach() -set(DSCRIPTS_SOURCES_ZONE ${DSCRIPTS_SOURCES_ZONE} PARENT_SCOPE) +add_library(dScriptsZone STATIC ${DSCRIPTS_SOURCES_ZONE}) +target_include_directories(dScriptsZone PUBLIC "." + "AG" + "LUPs" + "PROPERTY" + "PROPERTY/FV" + "PROPERTY/GF" + "PROPERTY/NS") +target_precompile_headers(dScriptsZone REUSE_FROM dScriptsBase) diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index cbab98d0..31fd488c 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -88,7 +88,7 @@ namespace Game { RakPeerInterface* chatServer = nullptr; std::mt19937 randomEngine; SystemAddress chatSysAddr; - bool shouldShutdown = false; + Game::signal_t lastSignal = 0; EntityManager* entityManager = nullptr; dZoneManager* zoneManager = nullptr; std::string projectVersion = PROJECT_VERSION; @@ -124,8 +124,8 @@ int main(int argc, char** argv) { // Triggers the shutdown sequence at application exit std::atexit(WorldShutdownSequence); - signal(SIGINT, [](int) { WorldShutdownSequence(); }); - signal(SIGTERM, [](int) { WorldShutdownSequence(); }); + std::signal(SIGINT, Game::OnSignal); + std::signal(SIGTERM, Game::OnSignal); uint32_t zoneID = 1000; uint32_t cloneID = 0; @@ -212,7 +212,7 @@ int main(int argc, char** argv) { UserManager::Instance()->Initialize(); Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); - Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::shouldShutdown, zoneID); + Game::server = new dServer(masterIP, ourPort, instanceID, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::World, Game::config, &Game::lastSignal, zoneID); //Connect to the chat server: uint32_t chatPort = 1501; @@ -313,6 +313,8 @@ int main(int argc, char** argv) { uint32_t saveTime = 10 * 60 * currentFramerate; // 10 minutes in frames uint32_t sqlPingTime = 10 * 60 * currentFramerate; // 10 minutes in frames uint32_t emptyShutdownTime = (cloneID == 0 ? 30 : 5) * 60 * currentFramerate; // 30 minutes for main worlds, 5 for all others. + + Game::logger->Flush(); // once immediately before the main loop while (true) { Metrics::StartMeasurement(MetricVariable::Frame); Metrics::StartMeasurement(MetricVariable::GameLoop); @@ -363,9 +365,9 @@ int main(int argc, char** argv) { if (!Game::server->GetIsConnectedToMaster()) { framesSinceMasterDisconnect++; - if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::shouldShutdown) { + if (framesSinceMasterDisconnect >= noMasterConnectionTimeout && !Game::ShouldShutdown()) { LOG("Game loop running but no connection to master for %d frames, shutting down", noMasterConnectionTimeout); - Game::shouldShutdown = true; + Game::lastSignal = -1; } } else framesSinceMasterDisconnect = 0; @@ -460,7 +462,7 @@ int main(int argc, char** argv) { //If we haven't had any players for a while, time out and shut down: if (framesSinceLastUser >= emptyShutdownTime) { - Game::shouldShutdown = true; + Game::lastSignal = -1; } } else { framesSinceLastUser = 0; @@ -513,7 +515,7 @@ int main(int argc, char** argv) { } } - if (Game::shouldShutdown && !worldShutdownSequenceComplete) { + if (Game::ShouldShutdown() && !worldShutdownSequenceComplete) { WorldShutdownProcess(zoneID); break; } @@ -822,7 +824,7 @@ void HandlePacket(Packet* packet) { } case eMasterMessageType::SHUTDOWN: { - Game::shouldShutdown = true; + Game::lastSignal = -1; LOG("Got shutdown request from master, zone (%i), instance (%i)", Game::server->GetZoneID(), Game::server->GetInstanceID()); break; } @@ -1289,14 +1291,17 @@ void WorldShutdownProcess(uint32_t zoneId) { } void WorldShutdownSequence() { - Game::shouldShutdown = true; + bool shouldShutdown = Game::ShouldShutdown() || worldShutdownSequenceComplete; + Game::lastSignal = -1; #ifndef DARKFLAME_PLATFORM_WIN32 - if (Game::shouldShutdown || worldShutdownSequenceComplete) + if (shouldShutdown) #endif { return; } + if (!Game::logger) return; + LOG("Zone (%i) instance (%i) shutting down outside of main loop!", Game::server->GetZoneID(), instanceID); WorldShutdownProcess(Game::server->GetZoneID()); FinalizeShutdown(); diff --git a/dZoneManager/CMakeLists.txt b/dZoneManager/CMakeLists.txt index bbb22ba7..e7382367 100644 --- a/dZoneManager/CMakeLists.txt +++ b/dZoneManager/CMakeLists.txt @@ -4,4 +4,6 @@ set(DZONEMANAGER_SOURCES "dZoneManager.cpp" "Zone.cpp") add_library(dZoneManager STATIC ${DZONEMANAGER_SOURCES}) -target_link_libraries(dZoneManager dPhysics) +target_link_libraries(dZoneManager + PUBLIC dPhysics + INTERFACE dWorldServer) diff --git a/dZoneManager/Spawner.cpp b/dZoneManager/Spawner.cpp index b602c17c..31188907 100644 --- a/dZoneManager/Spawner.cpp +++ b/dZoneManager/Spawner.cpp @@ -51,20 +51,20 @@ Spawner::Spawner(const SpawnerInfo info) { std::vector spawnSmashSpawnersN = Game::zoneManager->GetSpawnersByName(m_Info.spawnOnSmashGroupName); for (Entity* ssEntity : spawnSmashEntities) { m_SpawnSmashFoundGroup = true; - ssEntity->AddDieCallback([=]() { + ssEntity->AddDieCallback([=, this]() { Spawn(); }); } for (Spawner* ssSpawner : spawnSmashSpawners) { m_SpawnSmashFoundGroup = true; - ssSpawner->AddSpawnedEntityDieCallback([=]() { + ssSpawner->AddSpawnedEntityDieCallback([=, this]() { Spawn(); }); } for (Spawner* ssSpawner : spawnSmashSpawnersN) { m_SpawnSmashFoundGroup = true; m_SpawnOnSmash = ssSpawner; - ssSpawner->AddSpawnedEntityDieCallback([=]() { + ssSpawner->AddSpawnedEntityDieCallback([=, this]() { Spawn(); }); } diff --git a/docker-compose.yml b/docker-compose.yml index d8c3c40a..1d83f010 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,104 +1,66 @@ version: "3" services: - setup: - container_name: DarkflameSetup - build: - context: . - dockerfile: ./docker/setup.Dockerfile - environment: - - DATABASE=${MARIADB_DATABASE:-darkflame} - - DATABASE_HOST=database - - DATABASE_USER=${MARIADB_USER:-darkflame} - - DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame} - - EXTERNAL_IP=${EXTERNAL_IP:-darkflame} - - BUILD_VERSION=${BUILD_VERSION:?171022} - volumes: - - ${CLIENT_PATH:?missing_client_path}:/client - - shared_configs:/docker/ - - database: - container_name: DarkflameDatabase - build: - context: . - dockerfile: ./docker/database.Dockerfile + darkflamedb: + image: mariadb:latest environment: - MARIADB_USER=${MARIADB_USER:-darkflame} - MARIADB_PASSWORD=${MARIADB_PASSWORD:-darkflame} - MARIADB_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD:-darkflame} - MARIADB_DATABASE=${MARIADB_DATABASE:-darkflame} - volumes: - - database:/var/lib/mysql networks: - darkflame - # You can expose these so that DB management tools can connect (WARNING: INSECURE) - # ports: - # - 3306:3306 - darkflame: - container_name: DarkflameServer + darkflameserver: networks: - darkflame - build: - context: . - dockerfile: ./docker/Dockerfile - args: - - BUILD_THREADS=${BUILD_THREADS:-1} - - BUILD_VERSION=${BUILD_VERSION:-171022} + image: ghcr.io/darkflameuniverse/darkflameserver:latest volumes: - - ${CLIENT_PATH:?missing_client_path}:/client:ro - - shared_configs:/shared_configs:ro + - $HOST_CONFIG_DIR:/app/configs/ + - $HOST_CLIENT_LOCATION:$CLIENT_LOCATION:ro + - $HOST_RESSERVER_LOCATION:/app/resServer/ + - $HOST_DUMP_FOLDER:$DUMP_FOLDER + - $HOST_LOGS_DIR:/app/logs/ + environment: + - DLU_CONFIG_DIR=/app/configs + - MYSQL_HOST=darkflamedb + - MYSQL_DATABASE=${MARIADB_DATABASE:-darkflame} + - MYSQL_USERNAME=${MARIADB_USER:-darkflame} + - MYSQL_PASSWORD=${MARIADB_PASSWORD:-darkflame} + - EXTERNAL_IP=${EXTERNAL_IP:-localhost} + - DUMP_FOLDER=$DUMP_FOLDER + - CLIENT_LOCATION=$CLIENT_LOCATION depends_on: - - database + - darkflamedb ports: - "1001:1001/udp" - - "2000:2000/udp" - "2005:2005/udp" - "3000-3300:3000-3300/udp" - brickbuildfix: - container_name: DarkflameBrickBuildFix + darkflameweb: networks: - darkflame - build: - context: . - dockerfile: ./docker/brickfix.Dockerfile - ports: - - 80:80 - healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:80"] - interval: 2m - timeout: 3s - retries: 3 - start_period: 40s - - account-manager: - container_name: DarkflameAccountManager - networks: - - darkflame - build: - context: . - dockerfile: ./docker/AccountManager.Dockerfile + image: ghcr.io/darkflameuniverse/nexusdashboard:latest + volumes: + - $HOST_CLIENT_LOCATION:/app/luclient:ro + - $HOST_CACHE_LOCATION:/app/cache + - $HOST_CD_SQLITE_LOCATION:/app/cdclient:ro environment: - - DATABASE=${MARIADB_DATABASE:-darkflame} - - DATABASE_HOST=database - - DATABASE_USER=${MARIADB_USER:-darkflame} - - DATABASE_PASSWORD=${MARIADB_PASSWORD:-darkflame} - - ACCOUNT_SECRET=${ACCOUNT_MANAGER_SECRET:?missing_account_secret} + - APP_SECRET_KEY=$APP_SECRET_KEY + - APP_DATABASE_URI=mysql+pymysql://${MARIADB_USER:-darkflame}:${MARIADB_PASSWORD:-darkflame}@darkflamedb:3306/${MARIADB_DATABASE:-darkflame} + - CLIENT_LOCATION=/app/luclient + - CACHE_LOCATION=/app/cache + - CD_SQLITE_LOCATION=/app/cdclient + depends_on: + - darkflamedb ports: - - 5000:5000 + - 8000:8000 healthcheck: - test: ["CMD", "curl", "-f", "http://localhost:5000"] + test: ["CMD", "curl", "-f", "http://localhost:8000"] interval: 2m timeout: 3s retries: 3 start_period: 40s - depends_on: - - database networks: darkflame: - -volumes: - database: - shared_configs: diff --git a/docker/AccountManager.Dockerfile b/docker/AccountManager.Dockerfile deleted file mode 100644 index 60226339..00000000 --- a/docker/AccountManager.Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM python:3.10-alpine3.14 - -WORKDIR /usr/local/share/AccountManager - -COPY ./thirdparty/AccountManager . - -ADD docker/credentials_example.py credentials.py -ADD docker/resources_example.py resources.py - -RUN apk add curl libffi-dev build-base --no-cache && pip3 install -r requirements.txt - -EXPOSE 5000 -CMD python3 app.py diff --git a/docker/Dockerfile b/docker/Dockerfile deleted file mode 100644 index fd211291..00000000 --- a/docker/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -FROM gcc:12 as build - -WORKDIR /build - -RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \ - echo "Install build dependencies" && \ - apt update && \ - apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \ - apt install cmake zlib1g zlib1g-dev -yqq --no-install-recommends && \ - rm -rf /var/lib/apt/lists/* - -COPY dAuthServer/ /build/dAuthServer -COPY dChatServer/ /build/dChatServer -COPY dCommon/ /build/dCommon -COPY dChatFilter/ /build/dChatFilter -COPY dDatabase/ /build/dDatabase -COPY dGame/ /build/dGame -COPY dMasterServer/ /build/dMasterServer -COPY dNet/ /build/dNet -COPY dPhysics/ /build/dPhysics -COPY dScripts/ /build/dScripts -COPY dWorldServer/ /build/dWorldServer -COPY dZoneManager/ /build/dZoneManager -COPY dNavigation/ /build/dNavigation -COPY migrations/ /build/migrations -COPY resources/ /build/resources -COPY thirdparty/ /build/thirdparty -COPY vanity /build/vanity -COPY tests/ /build/tests -COPY .clang-* CMake* LICENSE /build/ - -ARG BUILD_THREADS=1 -ARG BUILD_VERSION=171022 - -RUN echo "Build server" -RUN sed -i -e "s/MARIADB_CONNECTOR_COMPILE_JOBS=.*/MARIADB_CONNECTOR_COMPILE_JOBS=${BUILD_THREADS}/g" CMakeVariables.txt -RUN mkdir -p cmake_build -RUN cd cmake_build && \ - cmake .. -DCMAKE_BUILD_RPATH_USE_ORIGIN=TRUE && \ - make -j$BUILD_THREADS - -FROM gcc:12 as runtime - -RUN --mount=type=cache,id=runtime-apt-cache,target=/var/cache/apt \ - apt update && \ - apt install sudo -yqq --no-install-recommends && \ - apt remove -y libmysqlcppconn7v5 libmysqlcppconn-dev && \ - rm -rf /var/lib/apt/lists/* - -WORKDIR /app - -COPY --from=build /build/cmake_build /app - -RUN mkdir -p /build/cmake_build && ln -s /app/_deps /build/cmake_build/_deps - -COPY docker/start_server.sh /start_server.sh - -CMD [ "/start_server.sh" ] diff --git a/docker/brickfix.Dockerfile b/docker/brickfix.Dockerfile deleted file mode 100644 index 3323cd65..00000000 --- a/docker/brickfix.Dockerfile +++ /dev/null @@ -1,7 +0,0 @@ -# syntax=docker/dockerfile:1 -FROM python:3.9.9-slim -RUN apt-get update && \ - apt-get install curl -y -WORKDIR /empty_dir -EXPOSE 80 -CMD python -m http.server 80 diff --git a/docker/credentials_example.py b/docker/credentials_example.py deleted file mode 100644 index 2112fbed..00000000 --- a/docker/credentials_example.py +++ /dev/null @@ -1,4 +0,0 @@ -import os - -DB_URL = f'mysql+pymysql://{os.environ["DATABASE_USER"]}:{os.environ["DATABASE_PASSWORD"]}@{os.environ["DATABASE_HOST"]}/{os.environ["DATABASE"]}' -SECRET_KEY = os.environ["ACCOUNT_SECRET"] diff --git a/docker/database.Dockerfile b/docker/database.Dockerfile deleted file mode 100644 index 68261e44..00000000 --- a/docker/database.Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mariadb:10.6 - -COPY ./migrations/dlu /docker-entrypoint-initdb.d \ No newline at end of file diff --git a/docker/images/Account_Creation.png b/docker/images/Account_Creation.png deleted file mode 100755 index 1b8135dd..00000000 Binary files a/docker/images/Account_Creation.png and /dev/null differ diff --git a/docker/images/DD_General_Settings.png b/docker/images/DD_General_Settings.png deleted file mode 100755 index d0e694aa..00000000 Binary files a/docker/images/DD_General_Settings.png and /dev/null differ diff --git a/docker/images/DD_Server_Startstop.png b/docker/images/DD_Server_Startstop.png deleted file mode 100755 index 8f482bb7..00000000 Binary files a/docker/images/DD_Server_Startstop.png and /dev/null differ diff --git a/docker/images/Docker_Compose_Finished.png b/docker/images/Docker_Compose_Finished.png deleted file mode 100755 index 549915ee..00000000 Binary files a/docker/images/Docker_Compose_Finished.png and /dev/null differ diff --git a/docker/images/Docker_Desktop_Installer_Configuration.png b/docker/images/Docker_Desktop_Installer_Configuration.png deleted file mode 100755 index d8c07eaa..00000000 Binary files a/docker/images/Docker_Desktop_Installer_Configuration.png and /dev/null differ diff --git a/docker/images/Docker_Download_Page.png b/docker/images/Docker_Download_Page.png deleted file mode 100755 index 4c478a66..00000000 Binary files a/docker/images/Docker_Download_Page.png and /dev/null differ diff --git a/docker/images/Open_Powershell.png b/docker/images/Open_Powershell.png deleted file mode 100755 index 861e609d..00000000 Binary files a/docker/images/Open_Powershell.png and /dev/null differ diff --git a/docker/images/WSL_2_download.png b/docker/images/WSL_2_download.png deleted file mode 100755 index e9be9a31..00000000 Binary files a/docker/images/WSL_2_download.png and /dev/null differ diff --git a/docker/images/setup_finished.png b/docker/images/setup_finished.png deleted file mode 100755 index 6278ac6b..00000000 Binary files a/docker/images/setup_finished.png and /dev/null differ diff --git a/docker/resources_example.py b/docker/resources_example.py deleted file mode 100644 index 652281f5..00000000 --- a/docker/resources_example.py +++ /dev/null @@ -1,3 +0,0 @@ -LOGO = 'logo/logo.png' -PRIVACY_POLICY = 'policy/Privacy Policy.pdf' -TERMS_OF_USE = 'policy/Terms of Use.pdf' diff --git a/docker/setup.Dockerfile b/docker/setup.Dockerfile deleted file mode 100644 index 18bb2d06..00000000 --- a/docker/setup.Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM python:3.10-alpine3.14 as prep - -RUN apk add bash --no-cache - -WORKDIR /setup - -# copy needed files from repo -COPY resources/ resources/ - -COPY docker/setup.sh /setup.sh - -CMD [ "/setup.sh" ] diff --git a/docker/setup.sh b/docker/setup.sh deleted file mode 100755 index 1944bbba..00000000 --- a/docker/setup.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/bin/bash - -# fail on first error -set -e - -function update_ini() { - FILE="/docker/configs/$1" - KEY=$2 - NEW_VALUE=$3 - sed -i "s~$2=.*~$2=$3~" $FILE -} - -function update_database_ini_values_for() { - INI_FILE=$1 - - update_ini $INI_FILE mysql_host $DATABASE_HOST - update_ini $INI_FILE mysql_database $DATABASE - update_ini $INI_FILE mysql_username $DATABASE_USER - update_ini $INI_FILE mysql_password $DATABASE_PASSWORD - update_ini $INI_FILE client_net_version $BUILD_VERSION - update_ini $INI_FILE external_ip $EXTERNAL_IP -} - -function update_ini_values() { - echo "Copying and updating config files" - - mkdir -p /docker/configs - cp resources/masterconfig.ini /docker/configs/ - cp resources/authconfig.ini /docker/configs/ - cp resources/chatconfig.ini /docker/configs/ - cp resources/worldconfig.ini /docker/configs/ - cp resources/sharedconfig.ini /docker/configs/ - - # always use the internal docker hostname - update_ini masterconfig.ini master_ip "darkflame" - update_ini sharedconfig.ini client_location "/client" - - update_database_ini_values_for sharedconfig.ini -} - -update_ini_values diff --git a/docker/start_server.sh b/docker/start_server.sh deleted file mode 100755 index 2e2e8c28..00000000 --- a/docker/start_server.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash - -function symlink_config_files() { - echo "Creating symlinks for config files" - rm /app/*.ini - ln -s /shared_configs/configs/authconfig.ini /app/authconfig.ini - ln -s /shared_configs/configs/chatconfig.ini /app/chatconfig.ini - ln -s /shared_configs/configs/masterconfig.ini /app/masterconfig.ini - ln -s /shared_configs/configs/worldconfig.ini /app/worldconfig.ini - ln -s /shared_configs/configs/sharedconfig.ini /app/sharedconfig.ini -} - -if [[ ! -f "/app/initialized" ]]; then - # setup symlinks for volume files - symlink_config_files - # do not run symlinks more than once - touch /app/initialized -else - echo "Server already initialized" -fi - -# start the server -echo "Starting MasterServer" -./MasterServer -tail -f /dev/null diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100755 index 00000000..e9e7c5fb --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e + +mkdir -p /app/configs/ +cp -n /app/default-configs/* /app/configs/ +mkdir -p /app/vanity/ +cp -n /app/default-vanity/* /app/vanity/ +cd /app/ && ./MasterServer diff --git a/resources/authconfig.ini b/resources/authconfig.ini index 62a5c6de..7938b5cc 100644 --- a/resources/authconfig.ini +++ b/resources/authconfig.ini @@ -1,5 +1,5 @@ # Port number. The client has the authserver port hardcoded to 1001 -port=1001 +auth_server_port=1001 # 0 or 1, should ignore playkeys # If 1 everyone with an account will be able to login, regardless of if they have a key or not diff --git a/resources/chatconfig.ini b/resources/chatconfig.ini index 1bb1d1b2..402534ed 100644 --- a/resources/chatconfig.ini +++ b/resources/chatconfig.ini @@ -1,6 +1,3 @@ -# Port number -port=2005 - # If you would like to increase the maximum number of best friends a player can have on the server # Change the value below to what you would like this to be (5 is live accurate) max_number_of_best_friends=5 diff --git a/resources/masterconfig.ini b/resources/masterconfig.ini index 4864d8cb..ab04af99 100644 --- a/resources/masterconfig.ini +++ b/resources/masterconfig.ini @@ -2,7 +2,7 @@ master_ip=localhost # Port number -port=2000 +master_server_port=2000 # The port number to start world servers on. Will be incremented for each world world_port_start=3000 diff --git a/resources/sharedconfig.ini b/resources/sharedconfig.ini index 2e2b6120..e487058d 100644 --- a/resources/sharedconfig.ini +++ b/resources/sharedconfig.ini @@ -62,3 +62,7 @@ event_8= version_major=1 version_current=10 version_minor=64 + +# The port the chat server is started and listening on +# Used in chat and world servers +chat_server_port=2005 \ No newline at end of file diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index 7f296afe..c68b42d2 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -2,9 +2,6 @@ # If you fork this repository and/or make changes to the code, reflect that here to comply with AGPLv3 source=https://github.com/DarkflameUniverse/DarkflameServer -# Port to the chat server, same as in chatconfig.ini -chat_server_port=2005 - # 0 or 1, should disable chat disable_chat=0 diff --git a/tests/dCommonTests/TestEncoding.cpp b/tests/dCommonTests/TestEncoding.cpp index c103ccbf..54ae03d3 100644 --- a/tests/dCommonTests/TestEncoding.cpp +++ b/tests/dCommonTests/TestEncoding.cpp @@ -26,7 +26,7 @@ TEST_F(EncodingTest, TestEncodingHello) { }; TEST_F(EncodingTest, TestEncodingUmlaut) { - originalWord = u8"Frühling"; + originalWord = reinterpret_cast(u8"Frühling"); originalWordSv = originalWord; GeneralUtils::_NextUTF8Char(originalWordSv, out); EXPECT_EQ(out, U'F'); diff --git a/thirdparty/AccountManager b/thirdparty/AccountManager deleted file mode 160000 index 19e2a4fe..00000000 --- a/thirdparty/AccountManager +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 19e2a4fe9faba8ec2e40162e0019acd711967744 diff --git a/thirdparty/raknet/CMakeLists.txt b/thirdparty/raknet/CMakeLists.txt index 417e24dd..d4dd1e60 100644 --- a/thirdparty/raknet/CMakeLists.txt +++ b/thirdparty/raknet/CMakeLists.txt @@ -80,6 +80,8 @@ target_compile_options(raknet PRIVATE $<$: /w>) +set_property(TARGET raknet PROPERTY CXX_STANDARD 17) + if(WIN32) # Link Win Sockets 2 to RakNet target_link_libraries(raknet ws2_32)