Compare commits

...

26 Commits

Author SHA1 Message Date
David Markowitz
2804dc3ec2 fix: make include paths consistent (#1390)
* fix: bad header includes

tests pass

* fix-up more include paths
2024-01-05 06:33:52 -06:00
David Markowitz
870b56fe02 chore: cleanup objectIdManager overloading and classes (#1391)
* objectIdManager fixes

* Remove debug log
2024-01-05 06:31:22 -06:00
Gie "Max" Vanommeslaeghe
66ac5a1b7a Merge pull request #1388 from DarkflameUniverse/fix--make-all-settings-have-sane-defaults-where-possible
fix: don't crash if some configs values aren't present
2024-01-05 11:34:50 +01:00
321d354e96 fix: don't crash if some configs aren't present
remove not starting if ini's aren't present since everything can run from env vars now
2024-01-04 18:21:03 -06:00
72b69c7899 fix: dont crash if resServer doesn't exist (#1386)
and try to make it as well
2024-01-03 22:18:27 -06:00
Daniel Seiler
d283bbd1c4 fix: split apart big Cmake libraries (#1381)
* fix: split apart big Cmake libraries

* fix: formatting

* fix: newline

* fix: add quotes
2024-01-03 18:41:46 -06:00
David Markowitz
10baa98f00 Update build-and-push-docker.yml (#1385)
change name to be caps like the other
2024-01-03 14:41:35 +01:00
David Markowitz
c7c84c21ef feat: partially functioning property behavior ui (no saving or loading yet) (#1384)
* Add addstrip handling

add SendBehaviorBlocksToClient serialization
add id generation and auto updating
add behaviorlisttoclient serialization

* fix crash

happened if you added state 0 and 6 and nothing in between

* Section off code

Use proper encapsulation to hide code away and only let specific objects do certain jobs.

* Organize serialization

Section off into operational chunks
Write data at the level most appropriate

* Remove and simplify BlockDefinitions

Remove pointer usage for BlockDefinitions and move to optional.

* ControlBehaviors: Add addaction handling

* re-organization

remove const from return value
change to int from uint
use generic methods to reduce code clutter

* add strip ui position handling

* add split strip functionality

* fix issues

fix an issue where if you were on an empty state, the server would allow you to remain on that state
fix an issue where the ui would not open on the previously opened state
fix an issue where deleting strips in order caused the wrong strips to be deleted

* update how you remove behaviors from models

* Add remove actions and rename

* migrate actions

* update action and rearrange strip

* merge strips

* add and move to inventory

* Remove dead code

* simplify code

* nits and move finish MoveToInventory

constify serialize

further include path fixes

use const, comments

fix amf message

Update ModelComponent.cpp

replace operator subscript with at

* Update ModelComponent.cpp

* Update MigrateActionsMessage.h

* const

* Move to separate translation units

* include amf3

its precompiled, but just in case
2024-01-03 07:34:38 -06:00
David Markowitz
09fb1dfff9 Update docker-compose.yml (#1383) 2024-01-03 06:00:49 -06:00
9116317834 fix: improve our docker config (#1373)
* greatly simplify docker

* back to gcc since I can't the libs to load properly

* fix typo
add set -e to entrypoint
better copy of entrypoint.sh and use proper entrypoint

* use debian instead of gcc for runtime
comment and organize it a bit
drop gcc to 12 since we are using debian 12 as well

* explicitly include mariadb libs

* Make the server not crash in the case we are using only env-vars
make the dockerfile have configs in the expected location incase of bypassing entrypoint.sh

* remove unneede var from example, since it's in the container now

* coments to dockerfile

* Revert master server changes

* Resolve conflicting port options between chat, master, and world
move chat_server_port to shared since it's used by world and chat

* Don't error if file does not exists when updating a config option
move update before and use bin dir var
2024-01-03 02:36:17 -06:00
David Markowitz
a84ca1f00d crash and log fix (#1382) 2024-01-03 06:52:11 +00:00
David Markowitz
bb79528c0e fix: nullptr access for logger in master (#1380)
* fix nullptr access for logger

* fix nullptr access for logger

fix no save on crash

* Update MasterServer.cpp
2024-01-02 18:28:17 -06:00
David Markowitz
b1134b340f Add config update function (#1379)
Update CMakeLists.txt

FINALLY

dont ignore cmake module directory

move to separate file

very cool feature
tested that this still works

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2024-01-02 18:25:57 -06:00
David Markowitz
1941679d27 feat: Bump standard to 20 (#1376)
* Bump to 20

* fix warnings and errors

* thanks RakNet
2024-01-02 07:53:00 +00:00
Daniel Seiler
85672e060a fix: signal handling (#1375)
* fix: signal handling

* fix: flush WorldServer logger before main loop

* fix: consolidate signal code
2024-01-01 21:50:00 -06:00
Daniel Seiler
18feea5fed fix: optional party phrases (#1377)
* fix: optional party phrases

Don't return early if there are no party phrases

* Update VanityUtilities.cpp
2024-01-01 17:08:38 -06:00
David Markowitz
e54faa3820 chore: organize build flags (#1371)
* chore: organize build flags

* Remove ambiguous include path

Don't be default incluyde bcrypt so you need to specify the folder.  Allows pre-processor to find the correct file.

* Revert settings

* working

f
2023-12-31 00:26:49 -06:00
David Markowitz
4ecb6ae30e fix: joining lobby twice (#1374) 2023-12-31 00:14:58 -06:00
Gie "Max" Vanommeslaeghe
c708246f73 Merge pull request #1247 from maxdelayer/main
Fix edge case where leaderboard viewing would cause unhandled exception
2023-12-31 00:43:37 +01:00
Gie "Max" Vanommeslaeghe
295ba628c2 Merge pull request #1372 from DarkflameUniverse/feature/update-connector
fix: bump connector version
2023-12-31 00:32:52 +01:00
Xiphoseer
d8f74f008f fix: bump connector version 2023-12-31 00:22:39 +01:00
Daniel Seiler
42a71bbeab feat: add DLU_CONFIG_DIR env var (#1370)
* feat: add DLU_CONFIG_DIR env var

* fix: PascalCase
2023-12-30 07:07:49 -06:00
Daniel Seiler
98d2f25af2 feat: get & print std::current_exception (#1366) 2023-12-30 08:04:26 +01:00
Daniel Seiler
dd9d94f75f feat: allow env var override for game config (#1367) 2023-12-30 08:04:09 +01:00
Daniel Seiler
f08df25085 feat: split out system() calls from the rest of MasterServer (#1368) 2023-12-30 08:00:43 +01:00
Max
a19bead268 Fix edge case where leaderboard viewing would cause unhandled exception 2023-10-29 22:40:06 -04:00
194 changed files with 2027 additions and 1680 deletions

View File

@@ -3,8 +3,8 @@ Dockerfile
*.md
logo.png
versions.txt
build.sh
docker-compose.yml
.env
docker/__pycache__
.env.example
.env.example
build

View File

@@ -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 }}

1
.gitignore vendored
View File

@@ -122,3 +122,4 @@ docker/__pycache__
docker-compose.override.yml
!*Test.bin
!cmake/*

3
.gitmodules vendored
View File

@@ -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

View File

@@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.18)
project(Darkflame)
include(CTest)
set (CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@@ -14,30 +15,26 @@ string(REPLACE "\n" ";" variables ${variables})
foreach(variable ${variables})
# If the string contains a #, skip it
if(NOT "${variable}" MATCHES "#")
# Split the variable into name and value
string(REPLACE "=" ";" variable ${variable})
# Check that the length of the variable is 2 (name and value)
list(LENGTH variable length)
if(${length} EQUAL 2)
if(${length} EQUAL 2)
list(GET variable 0 variable_name)
list(GET variable 1 variable_value)
# Set the variable
set(${variable_name} ${variable_value})
# Add compiler definition
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
message(STATUS "Variable: ${variable_name} = ${variable_value}")
endif()
endif()
endforeach()
# Set the version
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"")
# Echo the version
message(STATUS "Version: ${PROJECT_VERSION}")
@@ -53,19 +50,22 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
# Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
if(UNIX)
if(APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
@@ -98,47 +98,64 @@ make_directory(${CMAKE_BINARY_DIR}/logs)
# Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blacklist.dcf")
message(STATUS "Checking resource file integrity")
foreach (resource_file ${RESOURCE_FILES})
include(Utils)
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port")
foreach(resource_file ${RESOURCE_FILES})
set(file_size 0)
if (EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
endif()
if (${file_size} EQUAL 0)
if(${file_size} EQUAL 0)
configure_file(
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
COPYONLY
)
message(STATUS "Moved " ${resource_file} " to project binary directory")
elseif (resource_file MATCHES ".ini")
elseif(resource_file MATCHES ".ini")
message(STATUS "Checking " ${resource_file} " for missing config options")
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "")
# Remove comment lines so they do not interfere with the variable parsing
foreach (line ${current_file_contents})
foreach(line ${current_file_contents})
string(FIND ${line} "#" is_comment)
if (NOT ${is_comment} EQUAL 0)
if(NOT ${is_comment} EQUAL 0)
string(APPEND parsed_current_file_contents ${line})
endif()
endforeach()
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents)
string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents})
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
set(line_to_add "")
foreach (line ${depot_file_contents})
foreach(line ${depot_file_contents})
string(FIND ${line} "#" is_comment)
if (NOT ${is_comment} EQUAL 0)
if(NOT ${is_comment} EQUAL 0)
string(REPLACE "=" ";" line_split ${line})
list(GET line_split 0 variable_name)
if (NOT ${parsed_current_file_contents} MATCHES ${variable_name})
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
set(line_to_add ${line_to_add} ${line})
foreach (line_to_append ${line_to_add})
foreach(line_to_append ${line_to_add})
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
endforeach()
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
endif()
set(line_to_add "")
else()
set(line_to_add ${line_to_add} ${line})
@@ -146,24 +163,23 @@ foreach (resource_file ${RESOURCE_FILES})
endforeach()
endif()
endforeach()
message(STATUS "Resource file integrity check complete")
# if navmeshes directory does not exist, create it
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
endif()
# Copy navmesh data on first build and extract it
configure_file(
${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip
COPYONLY
)
configure_file(${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip COPYONLY)
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PROJECT_BINARY_DIR}/navmeshes)
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
# Copy vanity files on first build
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
foreach(file ${VANITY_FILES})
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
endforeach()
@@ -171,26 +187,18 @@ endforeach()
# Move our migrations for MasterServer to run
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}
COPYONLY
)
endif()
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
endforeach()
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}
COPYONLY
)
endif()
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
endforeach()
# Create our list of include directories
@@ -198,7 +206,9 @@ set(INCLUDED_DIRECTORIES
"dCommon"
"dCommon/dClient"
"dCommon/dEnums"
"dChatFilter"
"dGame"
"dGame/dBehaviors"
"dGame/dComponents"
@@ -209,10 +219,14 @@ set(INCLUDED_DIRECTORIES
"dGame/dPropertyBehaviors"
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
"dGame/dUtilities"
"dPhysics"
"dNavigation"
"dNavigation/dTerrain"
"dZoneManager"
"dDatabase"
"dDatabase/CDClientDatabase"
"dDatabase/CDClientDatabase/CDClientTables"
@@ -220,78 +234,8 @@ set(INCLUDED_DIRECTORIES
"dDatabase/GameDatabase/ITables"
"dDatabase/GameDatabase/MySQL"
"dDatabase/GameDatabase/MySQL/Tables"
"dNet"
"dScripts"
"dScripts/02_server"
"dScripts/ai"
"dScripts/client"
"dScripts/EquipmentScripts"
"dScripts/EquipmentTriggers"
"dScripts/zone"
"dScripts/02_server/DLU"
"dScripts/02_server/Enemy"
"dScripts/02_server/Equipment"
"dScripts/02_server/Map"
"dScripts/02_server/Minigame"
"dScripts/02_server/Objects"
"dScripts/02_server/Pets"
"dScripts/02_server/Enemy/AG"
"dScripts/02_server/Enemy/AM"
"dScripts/02_server/Enemy/FV"
"dScripts/02_server/Enemy/General"
"dScripts/02_server/Enemy/Survival"
"dScripts/02_server/Enemy/VE"
"dScripts/02_server/Enemy/Waves"
"dScripts/02_server/Map/AG"
"dScripts/02_server/Map/AG_Spider_Queen"
"dScripts/02_server/Map/AM"
"dScripts/02_server/Map/FV"
"dScripts/02_server/Map/General"
"dScripts/02_server/Map/GF"
"dScripts/02_server/Map/njhub"
"dScripts/02_server/Map/NS"
"dScripts/02_server/Map/NT"
"dScripts/02_server/Map/PR"
"dScripts/02_server/Map/Property"
"dScripts/02_server/Map/SS"
"dScripts/02_server/Map/VE"
"dScripts/02_server/Map/FV/Racing"
"dScripts/02_server/Map/General/Ninjago"
"dScripts/02_server/Map/njhub/boss_instance"
"dScripts/02_server/Map/NS/Waves"
"dScripts/02_server/Map/Property/AG_Med"
"dScripts/02_server/Map/Property/AG_Small"
"dScripts/02_server/Map/Property/NS_Med"
"dScripts/02_server/Minigame/General"
"dScripts/ai/ACT"
"dScripts/ai/AG"
"dScripts/ai/FV"
"dScripts/ai/GENERAL"
"dScripts/ai/GF"
"dScripts/ai/MINIGAME"
"dScripts/ai/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"
@@ -299,33 +243,30 @@ set(INCLUDED_DIRECTORIES
"thirdparty/recastnavigation"
"thirdparty/SQLite"
"thirdparty/cpplinq"
"thirdparty/cpp-httplib"
"tests"
"tests/dCommonTests"
"tests/dGameTests"
"tests/dGameTests/dComponentsTests"
)
)
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
if (APPLE)
if(APPLE)
include_directories("/usr/local/include/")
endif()
if (WIN32)
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
elseif (UNIX)
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
endif()
# Add binary directory as an include directory
include_directories(${PROJECT_BINARY_DIR})
# Actually include the directories from our list
foreach (dir ${INCLUDED_DIRECTORIES})
foreach(dir ${INCLUDED_DIRECTORIES})
include_directories(${PROJECT_SOURCE_DIR}/${dir})
endforeach()
if(NOT WIN32)
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include/bcrypt")
endif()
include_directories("${PROJECT_SOURCE_DIR}/thirdparty/libbcrypt/include")
# Add linking directories:
link_directories(${PROJECT_BINARY_DIR})
@@ -378,10 +319,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()
@@ -392,12 +333,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}
@@ -419,6 +354,6 @@ target_precompile_headers(
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
)
if (${__enable_testing__} MATCHES "1")
if(${ENABLE_TESTING})
add_subdirectory(tests)
endif()

View File

@@ -1,22 +1,32 @@
PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=1
# LICENSE
LICENSE=AGPL-3.0
# Debugging
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
__dynamic=1
# Set __ggdb to 1 to enable the -ggdb flag for the linker, including more debug info.
# __ggdb=1
# Set __include_backtrace__ to 1 to includes the backtrace library for better crashlogs.
# __include_backtrace__=1
# Set __compile_backtrace__ to 1 to compile the backtrace library instead of using system libraries.
# __compile_backtrace__=1
# Set DYNAMIC to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
DYNAMIC=1
# Set GGDB to 1 to enable the -ggdb flag for the linker, including more debug info.
# Do note, changing this will re-build the whole server
GGDB=0
# Set INCLUDE_BACKTRACE to 1 to includes the backtrace library for better crashlogs.
# Do note, changing this will re-build the whole server
INCLUDE_BACKTRACE=0
# Set COMPILE_BACKTRACE to 1 to compile the backtrace library instead of using system libraries.
# Do note, changing this will re-build the whole server
COMPILE_BACKTRACE=0
# Set to the number of jobs (make -j equivalent) to compile the mariadbconn files with.
__maria_db_connector_compile_jobs__=1
MARIADB_CONNECTOR_COMPILE_JOBS=1
# When set to 1 and uncommented, compiling and linking testing folders and libraries will be done.
__enable_testing__=1
ENABLE_TESTING=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# Uncomment the below line to cache the entire CDClient into memory
# CDCLIENT_CACHE_ALL=1
# Whether or not to cache the entire CDClient Database into memory instead of lazy loading.
# 0 means to lazy load, all other values mean load the entire database.
CDCLIENT_CACHE_ALL=0

View File

@@ -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://<EXTERNAL_IP>: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`

View File

@@ -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$\<your linux OS>\...` 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)

51
Dockerfile Normal file
View File

@@ -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" ]

View File

@@ -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 <file> 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)

51
cmake/Utils.cmake Normal file
View File

@@ -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()

View File

@@ -1,6 +1,7 @@
#include <iostream>
#include <string>
#include <ctime>
#include <csignal>
#include <chrono>
#include <thread>
@@ -15,7 +16,7 @@
//RakNet includes:
#include "RakNetDefines.h"
#include <MessageIdentifiers.h>
#include "MessageIdentifiers.h"
//Auth includes:
#include "AuthPackets.h"
@@ -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,17 +43,20 @@ 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;
//Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string());
Game::config = new dConfig("authconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting Auth server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
LOG("Version: %s", PROJECT_VERSION);
LOG("Compiled on: %s", __TIMESTAMP__);
try {
@@ -74,16 +78,20 @@ 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));
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 50;
uint32_t maxClients = 999;
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default.
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
std::string ourIP = "localhost";
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
GeneralUtils::TryParse(Game::config->GetValue("auth_server_port"), ourPort);
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();
@@ -96,13 +104,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 +152,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;

View File

@@ -1,2 +1,3 @@
add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES})
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@@ -6,6 +6,7 @@ set(DCHATSERVER_SOURCES
add_executable(ChatServer "ChatServer.cpp")
add_library(dChatServer ${DCHATSERVER_SOURCES})
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer)

View File

@@ -25,7 +25,7 @@
//RakNet includes:
#include "RakNetDefines.h"
#include <MessageIdentifiers.h>
#include "MessageIdentifiers.h"
namespace Game {
Logger* logger = nullptr;
@@ -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,17 +48,20 @@ 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;
//Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string());
Game::config = new dConfig("chatconfig.ini");
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
LOG("Starting Chat server...");
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
LOG("Version: %s", PROJECT_VERSION);
LOG("Compiled on: %s", __TIMESTAMP__);
try {
@@ -96,14 +99,19 @@ int main(int argc, char** argv) {
masterPort = masterInfo->port;
}
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 50;
uint32_t maxClients = 999;
uint32_t ourPort = 1501;
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
std::string ourIP = "localhost";
GeneralUtils::TryParse(Game::config->GetValue("max_clients"), maxClients);
GeneralUtils::TryParse(Game::config->GetValue("chat_server_port"), ourPort);
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
bool dontGenerateDCF = false;
GeneralUtils::TryParse(Game::config->GetValue("dont_generate_dcf"), dontGenerateDCF);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::randomEngine = std::mt19937(time(0));
@@ -118,7 +126,8 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0;
while (!Game::shouldShutdown) {
Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++;

View File

@@ -4,7 +4,7 @@
#include "Amf3.h"
// RakNet
#include <BitStream.h>
#include "BitStream.h"
/*!
\file AmfSerialize.h

View File

@@ -5,6 +5,7 @@ set(DCOMMON_SOURCES
"dConfig.cpp"
"Diagnostics.cpp"
"Logger.cpp"
"Game.cpp"
"GeneralUtils.cpp"
"LDFFormat.cpp"
"MD5.cpp"

View File

@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
#include <cstring>
#include <exception>
#if defined(__include_backtrace__)
#if defined(INCLUDE_BACKTRACE)
#include <backtrace.h>
#include <backtrace-supported.h>
@@ -115,7 +115,14 @@ void GenerateDump() {
}
void CatchUnhandled(int sig) {
#ifndef __include_backtrace__
std::exception_ptr eptr = std::current_exception();
try {
if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what());
}
#ifndef INCLUDE_BACKTRACE
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
@@ -167,7 +174,7 @@ void CatchUnhandled(int sig) {
backtrace_symbols_fd(array, size, STDOUT_FILENO);
# endif // defined(__GNUG__)
#else // __include_backtrace__
#else // INCLUDE_BACKTRACE
struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(),
@@ -178,7 +185,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 };
Bt(state);
#endif // __include_backtrace__
#endif // INCLUDE_BACKTRACE
exit(EXIT_FAILURE);
}

7
dCommon/Game.cpp Normal file
View File

@@ -0,0 +1,7 @@
#include "Game.h"
namespace Game {
void OnSignal(int signal) {
lastSignal = signal;
}
}

View File

@@ -1,6 +1,8 @@
#pragma once
#include <string>
#include <random>
#include <csignal>
class dServer;
class Logger;
@@ -15,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;
@@ -24,8 +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);
}

View File

@@ -9,7 +9,7 @@
#include <functional>
#include <type_traits>
#include <stdexcept>
#include <BitStream.h>
#include "BitStream.h"
#include "NiPoint3.h"
#include "Game.h"

View File

@@ -6,6 +6,8 @@
#include <stdarg.h>
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);

View File

@@ -1,6 +1,6 @@
#include "ZCompression.h"
#include <zlib.h>
#include "zlib.h"
namespace ZCompression {
int32_t GetMaxCompressedLength(int32_t nLenSrc) {

View File

@@ -4,7 +4,7 @@
#include "Game.h"
#include "Logger.h"
#include <zlib.h>
#include "zlib.h"
AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) {

View File

@@ -10,8 +10,23 @@ dConfig::dConfig(const std::string& filepath) {
LoadConfig();
}
std::filesystem::path GetConfigDir() {
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
config_dir /= env_p;
}
return config_dir;
}
const bool dConfig::Exists(const std::string& filepath) {
std::filesystem::path config_dir = GetConfigDir();
return std::filesystem::exists(config_dir / filepath);
}
void dConfig::LoadConfig() {
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath);
std::filesystem::path config_dir = GetConfigDir();
std::ifstream in(config_dir / m_ConfigFilePath);
if (!in.good()) return;
std::string line{};
@@ -19,7 +34,7 @@ void dConfig::LoadConfig() {
if (!line.empty() && line.front() != '#') ProcessLine(line);
}
std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in);
std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in);
if (!sharedConfig.good()) return;
line.clear();
@@ -34,6 +49,11 @@ void dConfig::ReloadConfig() {
}
const std::string& dConfig::GetValue(std::string key) {
std::string upper_key(key);
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
if (const char* env_p = std::getenv(upper_key.c_str())) {
this->m_ConfigValues[key] = env_p;
}
return this->m_ConfigValues[key];
}

View File

@@ -7,6 +7,11 @@ class dConfig {
public:
dConfig(const std::string& filepath);
/**
* Checks whether the specified filepath exists
*/
static const bool Exists(const std::string& filepath);
/**
* Gets the specified key from the config. Returns an empty string if the value is not found.
*

View File

@@ -39,9 +39,11 @@
#include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h"
#ifndef CDCLIENT_CACHE_ALL
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
// #define CDCLIENT_CACHE_ALL
// # define CDCLIENT_CACHE_ALL
#endif // CDCLIENT_CACHE_ALL
#ifdef CDCLIENT_CACHE_ALL
#define CDCLIENT_DONT_CACHE_TABLE(x) x

View File

@@ -14,3 +14,7 @@ endforeach()
add_library(dDatabase STATIC ${DDATABASE_SOURCES})
target_link_libraries(dDatabase sqlite3 mariadbConnCpp)
if (${CDCLIENT_CACHE_ALL})
add_compile_definitions(dDatabase CDCLIENT_CACHE_ALL=${CDCLIENT_CACHE_ALL})
endif()

View File

@@ -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
)

View File

@@ -3,7 +3,7 @@
#include "dCommonVars.h"
#include <vector>
#include "../thirdparty/tinyxml2/tinyxml2.h"
#include "tinyxml2.h"
#include <unordered_map>
#include <map>

View File

@@ -3,7 +3,7 @@
#include "CDClientManager.h"
#include "Game.h"
#include "Logger.h"
#include <PacketUtils.h>
#include "PacketUtils.h"
#include <functional>
#include "CDDestructibleComponentTable.h"
#include "CDClientDatabase.h"

View File

@@ -2,7 +2,7 @@
#include "RakNetTypes.h"
#include "Game.h"
#include "User.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Character.h"
#include "GeneralUtils.h"
#include "dServer.h"
@@ -89,7 +89,7 @@ Entity* EntityManager::CreateEntity(EntityInfo info, User* user, Entity* parentE
// Entities with no ID already set, often spawned entities, we'll generate a new sequencial ID
if (info.id == 0) {
id = ObjectIDManager::Instance()->GenerateObjectID();
id = ObjectIDManager::GenerateObjectID();
}
// Entities with an ID already set, often level entities, we'll use that ID as a base

View File

@@ -193,7 +193,7 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking
BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), CAST(lowestRanking.lowestRank AS SIGNED) - 9)
AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC;

View File

@@ -2,7 +2,7 @@
#include "EntityManager.h"
#include "GameMessages.h"
#include "InventoryComponent.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Game.h"
#include "Logger.h"
#include "Item.h"
@@ -273,7 +273,7 @@ void TradingManager::CancelTrade(LWOOBJID tradeId) {
}
Trade* TradingManager::NewTrade(LWOOBJID participantA, LWOOBJID participantB) {
const LWOOBJID tradeId = ObjectIDManager::Instance()->GenerateObjectID();
const LWOOBJID tradeId = ObjectIDManager::GenerateObjectID();
auto* trade = new Trade(tradeId, participantA, participantB);

View File

@@ -3,7 +3,7 @@
#include <string>
#include <vector>
#include "../thirdparty/raknet/Source/RakNetTypes.h"
#include "RakNetTypes.h"
#include "dCommonVars.h"
#include <unordered_map>

View File

@@ -8,11 +8,11 @@
#include "Game.h"
#include "Logger.h"
#include "User.h"
#include <WorldPackets.h>
#include "WorldPackets.h"
#include "Character.h"
#include <BitStream.h>
#include "BitStream.h"
#include "PacketUtils.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Logger.h"
#include "GeneralUtils.h"
#include "ZoneInstanceManager.h"
@@ -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::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);

View File

@@ -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)

View File

@@ -5,7 +5,7 @@
#include "Game.h"
#include "Logger.h"
#include "SkillComponent.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "eObjectBits.h"
void ProjectileAttackBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
@@ -106,7 +106,7 @@ void ProjectileAttackBehavior::Calculate(BehaviorContext* context, RakNet::BitSt
const auto maxTime = this->m_maxDistance / this->m_projectileSpeed;
for (auto i = 0u; i < this->m_projectileCount; ++i) {
auto id = static_cast<LWOOBJID>(ObjectIDManager::Instance()->GenerateObjectID());
auto id = static_cast<LWOOBJID>(ObjectIDManager::GenerateObjectID());
GeneralUtils::SetBit(id, eObjectBits::SPAWNED);

View File

@@ -7,7 +7,7 @@
#include "ZoneInstanceManager.h"
#include "Game.h"
#include "Logger.h"
#include <WorldPackets.h>
#include "WorldPackets.h"
#include "EntityManager.h"
#include "ChatPackets.h"
#include "Player.h"
@@ -33,7 +33,7 @@ ActivityComponent::ActivityComponent(Entity* parent, int32_t activityID) : Compo
if (activityID > 0) m_ActivityID = activityID;
else m_ActivityID = parent->GetVar<int32_t>(u"activityID");
CDActivitiesTable* activitiesTable = CDClientManager::Instance().GetTable<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
std::vector<CDActivities> 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<CDActivitiesTable>();
std::vector<CDActivities> activities = activitiesTable->Query([=](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
std::vector<CDActivities> activities = activitiesTable->Query([this](CDActivities entry) {return (entry.ActivityID == m_ActivityID); });
for (auto activity : activities) {
auto mapID = m_ActivityInfo.instanceMapID;
if (static_cast<Leaderboard::Type>(activity.leaderboardType) == Leaderboard::Type::Racing && Game::config->GetValue("solo_racing") == "1") {
@@ -115,6 +115,7 @@ void ActivityComponent::HandleMessageBoxResponse(Entity* player, const std::stri
}
void ActivityComponent::PlayerJoin(Entity* player) {
if (PlayerIsInQueue(player)) return;
// If we have a lobby, queue the player and allow others to join, otherwise spin up an instance on the spot
if (HasLobby()) {
PlayerJoinLobby(player);
@@ -531,7 +532,7 @@ void ActivityInstance::RewardParticipant(Entity* participant) {
// First, get the activity data
auto* activityRewardsTable = CDClientManager::Instance().GetTable<CDActivityRewardsTable>();
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([=](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
std::vector<CDActivityRewards> activityRewards = activityRewardsTable->Query([this](CDActivityRewards entry) { return (entry.objectTemplate == m_ActivityInfo.ActivityID); });
if (!activityRewards.empty()) {
uint32_t minCoins = 0;

View File

@@ -1,5 +1,5 @@
#include "BaseCombatAIComponent.h"
#include <BitStream.h>
#include "BitStream.h"
#include "Entity.h"
#include "EntityManager.h"

View File

@@ -6,7 +6,7 @@
#include "Game.h"
#include "Logger.h"
#include "GameMessages.h"
#include <BitStream.h>
#include "BitStream.h"
#include "eTriggerEventType.h"
BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {

View File

@@ -1,5 +1,5 @@
#include "BuffComponent.h"
#include <BitStream.h>
#include "BitStream.h"
#include "CDClientDatabase.h"
#include <stdexcept>
#include "DestroyableComponent.h"
@@ -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);
}

View File

@@ -140,6 +140,9 @@ private:
*/
std::map<int32_t, Buff> m_Buffs;
// Buffs to remove at the end of the update frame.
std::vector<int32_t> m_BuffsToRemove;
/**
* Parameters (=effects) for each buff
*/

View File

@@ -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)

View File

@@ -1,5 +1,5 @@
#include "CharacterComponent.h"
#include <BitStream.h>
#include "BitStream.h"
#include "tinyxml2.h"
#include "Game.h"
#include "Logger.h"

View File

@@ -1,6 +1,6 @@
#pragma once
#include "../thirdparty/tinyxml2/tinyxml2.h"
#include "tinyxml2.h"
class Entity;

View File

@@ -1,5 +1,5 @@
#include "DestroyableComponent.h"
#include <BitStream.h>
#include "BitStream.h"
#include "Logger.h"
#include "Game.h"
#include "dConfig.h"

View File

@@ -7,7 +7,7 @@
#include "Game.h"
#include "Logger.h"
#include "CDClientManager.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "MissionComponent.h"
#include "GameMessages.h"
#include "SkillComponent.h"
@@ -68,7 +68,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
continue;
}
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
const LWOOBJID id = ObjectIDManager::GenerateObjectID();
const auto& info = Inventory::FindItemComponent(item.itemid);
@@ -86,7 +86,7 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
const auto proxyLOT = static_cast<LOT>(std::stoi(proxyLotAsString));
const auto& proxyInfo = Inventory::FindItemComponent(proxyLOT);
const LWOOBJID proxyId = ObjectIDManager::Instance()->GenerateObjectID();
const LWOOBJID proxyId = ObjectIDManager::GenerateObjectID();
// Use item.count since we equip item.count number of the item this is a requested proxy of
UpdateSlot(proxyInfo.equipLocation, { proxyId, proxyLOT, item.count, slot++ });
@@ -1341,7 +1341,7 @@ void InventoryComponent::SetNPCItems(const std::vector<LOT>& items) {
auto slot = 0u;
for (const auto& item : items) {
const LWOOBJID id = ObjectIDManager::Instance()->GenerateObjectID();
const LWOOBJID id = ObjectIDManager::GenerateObjectID();
const auto& info = Inventory::FindItemComponent(item);

View File

@@ -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
}

View File

@@ -1,4 +1,7 @@
#pragma once
#include <map>
#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<typename Msg>
void HandleControlBehaviorsMsg(AMFArrayValue* args) {
static_assert(std::is_base_of_v<BehaviorMessageBase, Msg>, "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<PropertyBehavior> m_Behaviors;
/**
* The original position of the model

View File

@@ -13,7 +13,7 @@
#include "DestroyableComponent.h"
#include "dpWorld.h"
#include "PetDigServer.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "eUnequippableActiveType.h"
#include "eTerminateType.h"
#include "ePetTamingNotifyType.h"
@@ -562,7 +562,7 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) {
return;
}
LWOOBJID petSubKey = ObjectIDManager::Instance()->GenerateRandomObjectID();
LWOOBJID petSubKey = ObjectIDManager::GenerateRandomObjectID();
GeneralUtils::SetBit(petSubKey, eObjectBits::CHARACTER);
GeneralUtils::SetBit(petSubKey, eObjectBits::PERSISTENT);

View File

@@ -1,6 +1,6 @@
#include "PropertyEntranceComponent.h"
#include <CDPropertyEntranceComponentTable.h>
#include "CDPropertyEntranceComponentTable.h"
#include "Character.h"
#include "Database.h"

View File

@@ -13,7 +13,7 @@
#include "Game.h"
#include "Item.h"
#include "Database.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Player.h"
#include "RocketLaunchpadControlComponent.h"
#include "PropertyEntranceComponent.h"
@@ -334,7 +334,7 @@ void PropertyManagementComponent::UpdateModelPosition(const LWOOBJID id, const N
node->position = position;
node->rotation = rotation;
ObjectIDManager::Instance()->RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) {
ObjectIDManager::RequestPersistentID([this, node, modelLOT, entity, position, rotation, originalRotation](uint32_t persistentId) {
SpawnerInfo info{};
info.templateID = modelLOT;

View File

@@ -1,7 +1,7 @@
#ifndef QUICKBUILDCOMPONENT_H
#define QUICKBUILDCOMPONENT_H
#include <BitStream.h>
#include "BitStream.h"
#include <vector>
#include <string>
#include "dCommonVars.h"

View File

@@ -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,

View File

@@ -1,7 +1,7 @@
#ifndef RENDERCOMPONENT_H
#define RENDERCOMPONENT_H
#include <BitStream.h>
#include "BitStream.h"
#include <vector>
#include <string>
#include <unordered_map>

View File

@@ -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)

View File

@@ -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)

View File

@@ -7,7 +7,7 @@
#include "MissionComponent.h"
#include "BitStreamUtils.h"
#include "dServer.h"
#include "../thirdparty/raknet/Source/RakNetworkFactory.h"
#include "RakNetworkFactory.h"
#include <future>
#include "User.h"
#include "UserManager.h"

View File

@@ -14,7 +14,7 @@
#include "EntityManager.h"
#include "Database.h"
#include "dServer.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "CppScripts.h"
#include "UserManager.h"
#include "ZoneInstanceManager.h"
@@ -50,7 +50,7 @@
#include <chrono>
#include "RakString.h"
#include "../thirdparty/cpp-httplib/httplib.h" //sorry not sorry.
#include "httplib.h" //sorry not sorry.
//CDB includes:
#include "CDClientManager.h"
@@ -1045,7 +1045,7 @@ void GameMessages::SendDropClientLoot(Entity* entity, const LWOOBJID& sourceID,
LWOOBJID owner = entity->GetObjectID();
if (item != LOT_NULL && item != 0) {
lootID = ObjectIDManager::Instance()->GenerateObjectID();
lootID = ObjectIDManager::GenerateObjectID();
Loot::Info info;
info.id = lootID;
@@ -2565,12 +2565,12 @@ void GameMessages::HandleBBBSaveRequest(RakNet::BitStream* inStream, Entity* ent
//But we don't want the server to go unresponsive, because then the client would disconnect.
//We need to get a new ID for our model first:
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t newID) {
ObjectIDManager::RequestPersistentID([=](uint32_t newID) {
LWOOBJID newIDL = newID;
GeneralUtils::SetBit(newIDL, eObjectBits::CHARACTER);
GeneralUtils::SetBit(newIDL, eObjectBits::PERSISTENT);
uint32_t blueprintIDSmall = ObjectIDManager::Instance()->GenerateRandomObjectID();
uint32_t blueprintIDSmall = ObjectIDManager::GenerateRandomObjectID();
LWOOBJID blueprintID = blueprintIDSmall;
GeneralUtils::SetBit(blueprintID, eObjectBits::CHARACTER);
GeneralUtils::SetBit(blueprintID, eObjectBits::PERSISTENT);
@@ -5565,7 +5565,7 @@ void GameMessages::HandleModularBuildFinish(RakNet::BitStream* inStream, Entity*
}
}
ObjectIDManager::Instance()->RequestPersistentID([=](uint32_t newId) {
ObjectIDManager::RequestPersistentID([=](uint32_t newId) {
LOG("Build finished");
GameMessages::SendFinishArrangingWithItem(character, entity->GetObjectID()); // kick them from modular build
GameMessages::SendModularBuildEnd(character); // i dont know if this does anything but DLUv2 did it

View File

@@ -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)

View File

@@ -2,7 +2,7 @@
#include <sstream>
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "GeneralUtils.h"
#include "GameMessages.h"
#include "Entity.h"
@@ -249,7 +249,7 @@ bool Item::IsEquipped() const {
bool Item::Consume() {
auto* skillsTable = CDClientManager::Instance().GetTable<CDObjectSkillsTable>();
auto skills = skillsTable->Query([=](const CDObjectSkills entry) {
auto skills = skillsTable->Query([this](const CDObjectSkills entry) {
return entry.objectTemplate == static_cast<uint32_t>(lot);
});

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<AMFStringValue*>(typeValueMap.second)->GetValue();
for (auto& [paramName, paramValue] : arguments->GetAssociative()) {
if (paramName == "Type") {
if (paramValue->GetValueType() != eAmf::String) continue;
type = static_cast<AMFStringValue*>(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<AMFStringValue*>(typeValueMap.second)->GetValue();
if (paramValue->GetValueType() != eAmf::String) continue;
valueParameterString = static_cast<AMFStringValue*>(paramValue)->GetValue();
} else {
if (typeValueMap.second->GetValueType() != eAmf::Double) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(typeValueMap.second)->GetValue();
if (paramValue->GetValueType() != eAmf::Double) continue;
valueParameterDouble = static_cast<AMFDoubleValue*>(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);
}
}

View File

@@ -1,7 +1,9 @@
#ifndef __ACTION__H__
#define __ACTION__H__
#include "BehaviorMessageBase.h"
#include <string>
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;

View File

@@ -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);

View File

@@ -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;
};

View File

@@ -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;
};

View File

@@ -19,9 +19,9 @@ class AMFArrayValue;
class AddStripMessage : public BehaviorMessageBase {
public:
AddStripMessage(AMFArrayValue* arguments);
StripUiPosition GetPosition() { return position; };
ActionContext GetActionContext() { return actionContext; };
std::vector<Action> GetActionsToAdd() { return actionsToAdd; };
StripUiPosition GetPosition() const { return position; };
ActionContext GetActionContext() const { return actionContext; };
std::vector<Action> GetActionsToAdd() const { return actionsToAdd; };
private:
StripUiPosition position;
ActionContext actionContext;

View File

@@ -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<std::string>(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<double>(keyName);
if (!actionIndexAmf) {
throw std::invalid_argument("Unable to find actionIndex");
}
return static_cast<uint32_t>(actionIndexAmf->GetValue());
return static_cast<int32_t>(actionIndexAmf->GetValue());
}

View File

@@ -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__

View File

@@ -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__

View File

@@ -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<Action>& GetMigratedActions() const { return migratedActions; };
void SetMigratedActions(std::vector<Action>::const_iterator start, std::vector<Action>::const_iterator end) { migratedActions.assign(start, end); };
private:
std::vector<Action> migratedActions;
ActionContext sourceActionContext;
ActionContext destinationActionContext;
uint32_t dstActionIndex;
int32_t dstActionIndex;
};
#endif //!__MERGESTRIPSMESSAGE__H__

View File

@@ -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<Action>& GetMigratedActions() const { return migratedActions; };
void SetMigratedActions(std::vector<Action>::const_iterator start, std::vector<Action>::const_iterator end) { migratedActions.assign(start, end); };
private:
std::vector<Action> migratedActions;
ActionContext sourceActionContext;
ActionContext destinationActionContext;
uint32_t srcActionIndex;
uint32_t dstActionIndex;
int32_t srcActionIndex;
int32_t dstActionIndex;
};
#endif //!__MIGRATEACTIONSMESSAGE__H__

View File

@@ -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;
};

View File

@@ -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__

View File

@@ -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__

View File

@@ -11,7 +11,7 @@
class RemoveStripMessage : public BehaviorMessageBase {
public:
RemoveStripMessage(AMFArrayValue* arguments);
ActionContext GetActionContext() { return actionContext; };
ActionContext GetActionContext() const { return actionContext; };
private:
ActionContext actionContext;
};

View File

@@ -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;
};

View File

@@ -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<Action>& GetTransferredActions() const { return transferredActions; };
void SetTransferredActions(std::vector<Action>::const_iterator begin, std::vector<Action>::const_iterator end) { transferredActions.assign(begin, end); };
private:
ActionContext sourceActionContext;
ActionContext destinationActionContext;
uint32_t srcActionIndex;
int32_t srcActionIndex;
StripUiPosition destinationPosition;
std::vector<Action> transferredActions;
};
#endif //!__SPLITSTRIPMESSAGE__H__

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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;
};

View File

@@ -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;

View File

@@ -5,7 +5,7 @@
#include "Game.h"
#include "GameMessages.h"
#include "ModelComponent.h"
#include "../../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Logger.h"
#include "BehaviorStates.h"
#include "AssetManager.h"
@@ -30,221 +30,49 @@
#include "UpdateActionMessage.h"
#include "UpdateStripUiMessage.h"
void ControlBehaviors::RequestUpdatedID(int32_t behaviorID, ModelComponent* modelComponent, Entity* modelOwner, const SystemAddress& sysAddr) {
// auto behavior = modelComponent->FindBehavior(behaviorID);
// if (behavior->GetBehaviorID() == -1 || behavior->GetShouldSetNewID()) {
// ObjectIDManager::Instance()->RequestPersistentID(
// [behaviorID, behavior, modelComponent, modelOwner, sysAddr](uint32_t persistentId) {
// behavior->SetShouldGetNewID(false);
// behavior->SetIsTemplated(false);
// behavior->SetBehaviorID(persistentId);
void ControlBehaviors::RequestUpdatedID(ControlBehaviorContext& context) {
ObjectIDManager::RequestPersistentID(
[context](uint32_t persistentId) {
if (!context) {
LOG("Model to update behavior ID for is null. Cannot update ID.");
return;
}
// This updates the behavior ID of the behavior should this be a new behavior
AMFArrayValue args;
// // This updates the behavior ID of the behavior should this be a new behavior
// AMFArrayValue args;
args.Insert("behaviorID", std::to_string(persistentId));
args.Insert("objectID", std::to_string(context.modelComponent->GetParent()->GetObjectID()));
// AMFStringValue* behaviorIDString = new AMFStringValue();
// behaviorIDString->SetValue(std::to_string(persistentId));
// args.InsertValue("behaviorID", behaviorIDString);
GameMessages::SendUIMessageServerToSingleClient(context.modelOwner, context.modelOwner->GetSystemAddress(), "UpdateBehaviorID", args);
context.modelComponent->UpdatePendingBehaviorId(persistentId);
// AMFStringValue* objectIDAsString = new AMFStringValue();
// objectIDAsString->SetValue(std::to_string(modelComponent->GetParent()->GetObjectID()));
// args.InsertValue("objectID", objectIDAsString);
// GameMessages::SendUIMessageServerToSingleClient(modelOwner, sysAddr, "UpdateBehaviorID", &args);
// ControlBehaviors::SendBehaviorListToClient(modelComponent->GetParent(), sysAddr, modelOwner);
// });
// }
ControlBehaviors::Instance().SendBehaviorListToClient(context);
});
}
void ControlBehaviors::SendBehaviorListToClient(Entity* modelEntity, const SystemAddress& sysAddr, Entity* modelOwner) {
auto* modelComponent = modelEntity->GetComponent<ModelComponent>();
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<double>("ModelType");
if (!modelTypeAmf) return;
uint32_t modelType = static_cast<uint32_t>(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<ModelComponent>();
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<double>("ModelType");
if (!modelType) return;
modelEntity->SetVar<int>(u"modelType", modelType->GetValue());
} else if (command == "toggleExecutionUpdates") {
// TODO
} else if (command == "addStrip") {
if (BehaviorMessageBase(context.arguments).IsDefaultBehaviorId()) RequestUpdatedID(context);
context.modelComponent->HandleControlBehaviorsMsg<AddStripMessage>(context.arguments);
} else if (command == "removeStrip") {
context.modelComponent->HandleControlBehaviorsMsg<RemoveStripMessage>(arguments);
} else if (command == "mergeStrips") {
context.modelComponent->HandleControlBehaviorsMsg<MergeStripsMessage>(arguments);
} else if (command == "splitStrip") {
context.modelComponent->HandleControlBehaviorsMsg<SplitStripMessage>(arguments);
} else if (command == "updateStripUI") {
context.modelComponent->HandleControlBehaviorsMsg<UpdateStripUiMessage>(arguments);
} else if (command == "addAction") {
context.modelComponent->HandleControlBehaviorsMsg<AddActionMessage>(arguments);
} else if (command == "migrateActions") {
context.modelComponent->HandleControlBehaviorsMsg<MigrateActionsMessage>(arguments);
} else if (command == "rearrangeStrip") {
context.modelComponent->HandleControlBehaviorsMsg<RearrangeStripMessage>(arguments);
} else if (command == "add") {
AddMessage msg(context.arguments);
context.modelComponent->AddBehavior(msg);
SendBehaviorListToClient(context);
} else if (command == "removeActions") {
context.modelComponent->HandleControlBehaviorsMsg<RemoveActionsMessage>(arguments);
} else if (command == "rename") {
context.modelComponent->HandleControlBehaviorsMsg<RenameMessage>(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<UpdateActionMessage>(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<BlockDefinition> 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;
}

View File

@@ -4,12 +4,13 @@
#define __CONTROLBEHAVIORS__H__
#include <map>
#include <optional>
#include <string>
#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<ControlBehaviors> {
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<BlockDefinition> 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<BlockName, BlockDefinition*> blockTypes{};
std::map<BlockName, BlockDefinition> blockTypes{};
// If false, property behaviors will not be able to be edited.
bool isInitialized = false;

View File

@@ -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<double>(stateId));
state.SendBehaviorBlocksToClient(*stateArgs);
}
auto* executionState = args.InsertArray("executionState");
executionState->Insert("stateID", static_cast<double>(m_LastEditedState));
executionState->InsertArray("strips");
// TODO Serialize the execution state of the behavior
}

View File

@@ -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<typename Msg>
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<BehaviorState, State> 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__

View File

@@ -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<double>(stripId));
strip.SendBehaviorBlocksToClient(*stripArgs);
}
};

View File

@@ -0,0 +1,19 @@
#ifndef __STATE__H__
#define __STATE__H__
#include "Strip.h"
class AMFArrayValue;
class State {
public:
template<typename Msg>
void HandleMsg(Msg& msg);
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const;
private:
std::vector<Strip> m_Strips;
};
#endif //!__STATE__H__

View File

@@ -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);
}
};

View File

@@ -0,0 +1,23 @@
#ifndef __STRIP__H__
#define __STRIP__H__
#include "Action.h"
#include "StripUiPosition.h"
#include <vector>
class AMFArrayValue;
class Strip {
public:
template<typename Msg>
void HandleMsg(Msg& msg);
void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const { return m_Actions.empty(); }
private:
std::vector<Action> m_Actions;
StripUiPosition m_Position;
};
#endif //!__STRIP__H__

View File

@@ -3,6 +3,13 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
"GUID.cpp"
"Loot.cpp"
"Mail.cpp"
"ObjectIDManager.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)

View File

@@ -0,0 +1,51 @@
#include "ObjectIDManager.h"
// Custom Classes
#include "MasterPackets.h"
#include "Database.h"
#include "Logger.h"
#include "Game.h"
//! The persistent ID request
struct PersistentIDRequest {
PersistentIDRequest(const uint64_t& requestID, const std::function<void(uint32_t)>& callback) : requestID(requestID), callback(callback) {}
uint64_t requestID;
std::function<void(uint32_t)> callback;
};
namespace {
std::vector<PersistentIDRequest> Requests; //!< All outstanding persistent ID requests
uint64_t CurrentRequestID = 0; //!< The current request ID
uint32_t CurrentObjectID = uint32_t(1152921508165007067); //!< The current object ID
std::uniform_int_distribution<int> Uni(10000000, INT32_MAX);
};
//! Requests a persistent ID
void ObjectIDManager::RequestPersistentID(const std::function<void(uint32_t)> callback) {
const auto& request = Requests.emplace_back(++CurrentRequestID, callback);
MasterPackets::SendPersistentIDRequest(Game::server, request.requestID);
}
//! Handles a persistent ID response
void ObjectIDManager::HandleRequestPersistentIDResponse(const uint64_t requestID, const uint32_t persistentID) {
auto it = std::find_if(Requests.begin(), Requests.end(), [requestID](const PersistentIDRequest& request) {
return request.requestID == requestID;
});
if (it == Requests.end()) return;
it->callback(persistentID);
Requests.erase(it);
}
//! Handles cases where we have to get a unique object ID synchronously
uint32_t ObjectIDManager::GenerateRandomObjectID() {
return Uni(Game::randomEngine);
}
//! Generates an object ID server-sided (used for regular entities like smashables)
uint32_t ObjectIDManager::GenerateObjectID() {
return ++CurrentObjectID;
}

View File

@@ -0,0 +1,40 @@
#pragma once
// C++
#include <functional>
#include <vector>
#include <stdint.h>
/*!
\file ObjectIDManager.h
\brief A manager for handling object ID generation
*/
//! The Object ID Manager
namespace ObjectIDManager {
//! Requests a persistent ID
/*!
\param callback The callback function
*/
void RequestPersistentID(const std::function<void(uint32_t)> callback);
//! Handles a persistent ID response
/*!
\param requestID The request ID
\param persistentID The persistent ID
*/
void HandleRequestPersistentIDResponse(const uint64_t requestID, const uint32_t persistentID);
//! Generates an object ID server-sided
/*!
\return A generated object ID
*/
uint32_t GenerateObjectID();
//! Generates a random object ID server-sided
/*!
\return A generated object ID
*/
uint32_t GenerateRandomObjectID();
};

View File

@@ -17,7 +17,7 @@
#include "EntityInfo.h"
#include "Spawner.h"
#include "dZoneManager.h"
#include "../dWorldServer/ObjectIDManager.h"
#include "ObjectIDManager.h"
#include "Level.h"
#include <fstream>
@@ -182,7 +182,7 @@ LWOOBJID VanityUtilities::SpawnSpawner(LOT lot, const NiPoint3& position, const
obj.lot = lot;
// guratantee we have no collisions
do {
obj.id = ObjectIDManager::Instance()->GenerateObjectID();
obj.id = ObjectIDManager::GenerateObjectID();
} while(Game::zoneManager->GetSpawner(obj.id));
obj.position = position;
obj.rotation = rotation;
@@ -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")) {
@@ -525,12 +524,12 @@ std::string VanityUtilities::ParseMarkdown(const std::string& file) {
#endif
// Replace "__TIMESTAMP__" with the __TIMESTAMP__
GeneralUtils::ReplaceInString(line, "__TIMESTAMP__", __TIMESTAMP__);
// Replace "__VERSION__" wit'h the PROJECT_VERSION
GeneralUtils::ReplaceInString(line, "__VERSION__", STRINGIFY(PROJECT_VERSION));
// Replace "__VERSION__" with the PROJECT_VERSION
GeneralUtils::ReplaceInString(line, "__VERSION__", Game::projectVersion);
// Replace "__SOURCE__" with SOURCE
GeneralUtils::ReplaceInString(line, "__SOURCE__", Game::config->GetValue("source"));
// Replace "__LICENSE__" with LICENSE
GeneralUtils::ReplaceInString(line, "__LICENSE__", STRINGIFY(LICENSE));
GeneralUtils::ReplaceInString(line, "__LICENSE__", "AGPL-3.0");
if (line.find("##") != std::string::npos) {
// Add "&lt;font size=&apos;18&apos; color=&apos;#000000&apos;&gt;" before the header

View File

@@ -1,10 +1,12 @@
set(DMASTERSERVER_SOURCES
"InstanceManager.cpp"
"ObjectIDManager.cpp"
"PersistentIDManager.cpp"
"Start.cpp"
)
add_library(dMasterServer ${DMASTERSERVER_SOURCES})
add_executable(MasterServer "MasterServer.cpp")
add_compile_definitions(MasterServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
target_link_libraries(dMasterServer ${COMMON_LIBRARIES})
target_link_libraries(MasterServer ${COMMON_LIBRARIES} dMasterServer)

View File

@@ -9,14 +9,15 @@
#include "CDZoneTableTable.h"
#include "MasterPackets.h"
#include "BitStreamUtils.h"
#include "BinaryPathFinder.h"
#include "eConnectionType.h"
#include "eMasterMessageType.h"
#include "Start.h"
InstanceManager::InstanceManager(Logger* logger, const std::string& externalIP) {
mLogger = logger;
mExternalIP = externalIP;
m_LastPort = std::atoi(Game::config->GetValue("world_port_start").c_str());
GeneralUtils::TryParse(Game::config->GetValue("world_port_start"), m_LastPort);
m_LastInstanceID = LWOINSTANCEID_INVALID;
}
@@ -57,33 +58,7 @@ Instance* InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, LW
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, softCap, maxPlayers);
//Start the actual process:
#ifdef _WIN32
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer.exe").string() + " -zone ";
#else
std::string cmd;
if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) {
cmd = "sudo " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
} else {
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
}
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef _WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
m_Instances.push_back(instance);
@@ -159,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];
@@ -318,28 +293,7 @@ Instance* InstanceManager::CreatePrivateInstance(LWOMAPID mapID, LWOCLONEID clon
instance = new Instance(mExternalIP, port, mapID, ++m_LastInstanceID, cloneID, maxPlayers, maxPlayers, true, password);
//Start the actual process:
std::string cmd = "start " + (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#ifndef _WIN32
cmd = (BinaryPathFinder::GetBinaryDir() / "WorldServer").string() + " -zone ";
#endif
cmd.append(std::to_string(mapID));
cmd.append(" -port ");
cmd.append(std::to_string(port));
cmd.append(" -instance ");
cmd.append(std::to_string(m_LastInstanceID));
cmd.append(" -maxclients ");
cmd.append(std::to_string(maxPlayers));
cmd.append(" -clone ");
cmd.append(std::to_string(cloneID));
#ifndef WIN32
cmd.append("&"); //Sends our next process to the background on Linux
#endif
auto ret = system(cmd.c_str());
StartWorldServer(mapID, port, m_LastInstanceID, maxPlayers, cloneID);
m_Instances.push_back(instance);

View File

@@ -134,7 +134,7 @@ private:
Logger* mLogger;
std::string mExternalIP;
std::vector<Instance*> m_Instances;
unsigned short m_LastPort;
unsigned short m_LastPort = 3000;
LWOINSTANCEID m_LastInstanceID;
/**

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