Compare commits

...

80 Commits

Author SHA1 Message Date
David Markowitz
1bd04fe36f use a default for optimizations 2024-12-04 22:44:38 -08:00
David Markowitz
129d9fd0b9 update exception catching (#1661) 2024-12-04 03:30:14 -06:00
David Markowitz
ec4ec2133b fix: logging uninitialized memory (#1658)
fixes an issue where the console would halt because we printed a control code which did such
2024-12-03 15:01:43 -06:00
David Markowitz
218a3f2d0d Update BaseFootRaceManager.cpp (#1657) 2024-11-27 16:26:10 -06:00
David Markowitz
c37a0c86c1 fix: Old level files not loading (#1656)
* emmo help

* fix parsing live data

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2024-11-26 22:14:07 -06:00
David Markowitz
9e7ef8c4ee fix: Player activated switches (#1655) 2024-11-25 22:55:50 -06:00
David Markowitz
ec501831e6 fix: Remove database requirements for Property Entrance Component and greatly simplify logic (#1650)
* remove complex queries and move logic to dDatabase

remove unused code

Use correct id

fix arrows

use correct parameter

fix queries

Update Property.cpp

remove unused header

remove extra include

* fix tests

* Update dGame/dComponents/PropertyEntranceComponent.h

Co-authored-by: jadebenn <jadebenn@users.noreply.github.com>

* Update dGame/User.h

Co-authored-by: jadebenn <jadebenn@users.noreply.github.com>

---------

Co-authored-by: jadebenn <jadebenn@users.noreply.github.com>
2024-11-23 15:56:31 -06:00
David Markowitz
5b8d2b19a3 fix: stack traces work again (#1653) 2024-11-23 01:33:21 -08:00
17f81d13a3 fix: normalize mixed slashes when looking for files (#1654) 2024-11-23 01:32:31 -08:00
jadebenn
53877a0bc3 refactor: Rewrite AMF and property behavior logic to use smart pointers, references, and string_views over raw pointers and std::string& (#1452)
* Rewrite AMF and behavior logic to use smart pointers, references, and string_views over raw pointers and std::string&

* fix m_BehaviorID initialization

* Fix BlockDefinition member naming

* remove redundant reset()s

* Replace UB forward template declarations with header include

* remove unneeded comment

* remove non-const ref getters

* simplify default behavior id initialization

* Fix invalidated use of Getter to set a value

* Update AddStripMessage.cpp - change push_back to emplace_back

* fix pointer to ref conversion mistake (should not have directly grabbed from the other branch commit)

* deref

* VERY experimental testing of forward declaration of templates - probably will revert

* Revert changes (as expected)

* Update BlockDefinition.h - remove extraneous semicolons

* Update BlockDefinition.h - remove linebreak

* Update Amf3.h member naming scheme

* fix duplicated code

* const iterators

* const pointers

* reviving this branch

* update read switch cases
2024-11-18 20:45:24 -06:00
jadebenn
83f8646936 fix: Cache compiler variables so external tools can recognize the compiler in use (#1649) 2024-11-17 20:55:01 -06:00
Tiernan
d8b86072d4 Fixed Misspelling of Tresure variable for petcomponent (#1617) 2024-11-17 18:48:48 -08:00
David Markowitz
112c2367cc fix: laggy property models (and probably more) (#1646)
* fix laggy property models (and probably more)

global fix correcting the initial physics motion state from 0 to 5 (confirm in client).  packet captures from a few worlds (didnt scan more than 5 files) show that the value for simple physics was either 5, or 4 for property models, or 1 for property models with behaviors.

properties with pre-built models no longer lag and values of physics types should be correct across the board

* will test this briefly
2024-11-17 20:44:35 -06:00
jadebenn
c7dd8205a4 feat: Make use of CMake presets to enable easy switching between debug and release configurations on all platforms (#1439)
* Add MSVC optimization flags

* test moving flags to json

* Update CMakePresets.json

* testing

* trying more variations on the flags

* third test

* testing if these even have any effect

* ditto

* final(?) try for now

* ONE MORE TIME

* trying 'init' flags instead

* export the compile commands so I can see if they're having any effect

* move out g++ O2 flag

* add Linux debug preset

* update CMake presets

* edit macos presets

* try adding build types back to mac

* macos refuses to work :(

* try using compiler flags for mac instead

* fix typo in windows preset

* build reorganization and experimental clang support

* temporarily remove macos build for testing purposes

* updated cmake workflows

* unexclude toolchain dir

* update .gitignore

* fix build directory issue

* edit build script

* update cmake configs

* attempted docker fix

* try zero-initializinng this struct to solve docker issue

* try fixing macos build

* one last MacOS try for the night

* try disabling an apple-specific build rule

* more fiddling with mac test builds

* try and narrow down the macos build failure cause

* try stripping out all the custom macos test logic again

* I'm really just throwing everything to the wall and seeing what sticks

* more macos tinkering

* implib

* try manual link directory specification

* save me

* aaaaaaaaa

* paths paths paths

* Revert "paths paths paths"

This reverts commit 9a7d86aa6c.

* Revert "aaaaaaaaa"

This reverts commit 338279c396.

* Revert "save me"

This reverts commit bd73aa21a9.

* Revert "try manual link directory specification"

This reverts commit 0c2d40632e.

* Revert "implib"

This reverts commit d41349d6ed.

* Revert "more macos tinkering"

This reverts commit 829ec35b57.

* Revert "I'm really just throwing everything to the wall and seeing what sticks"

This reverts commit 1a05b027fe.

* Revert "try stripping out all the custom macos test logic again"

This reverts commit cc15a26ce8.

* Revert "try and narrow down the macos build failure cause"

This reverts commit 5fd86833fa.

* Revert "more fiddling with mac test builds"

This reverts commit 0f843c02c9.

* Revert "try disabling an apple-specific build rule"

This reverts commit 45ec66e976.

* back to debug messages

* see if this re-breaks mac

* are these messages actually somehow fixing the issue?

* was not actually fixed

* add debug messages (again)

* debug try 2

* change runtime output dir

* rename gcc to gnu

* expand cmake presets

* fix preset

* change defaults

* altered cmake configuration scripts

* disable /WX on MSVC

* update github actions

* update build presets

* change gnu and clang build directories to enable consistent artifact generation

* add RelWithDebInfo presets and move -Werror flag into presets.json

* use DLU_CONFIG_DIR envvar

* CMakePresets indentation

* temp fix for MSVC debug builds
2024-11-17 19:03:54 -06:00
jadebenn
652f42ccf2 press the enter key once (#1648) 2024-11-17 19:03:09 -06:00
Wincent Holm
fedd039e00 Proposal for observers and deferred implementations (#1599) 2024-11-17 18:46:08 -06:00
jadebenn
84d7c65717 consolidate the messagetype enums into a single namespace (#1647) 2024-11-17 18:39:44 -06:00
David Markowitz
adc9cd2876 feat: Add some save data tests (#1623)
* saving from a test works

* testing works

* Update SavingTests.cpp

* test dServer stuff

* tests

* use dummy database and add missing pure fns

* add more tests

* add more tests

* add rocket tests

* Update BuffComponent.h

* Update test_xml_data.xml

* Update SavingTests.cpp

* update
2024-11-17 16:27:33 -08:00
David Markowitz
008e2d4dce add error reporting for failed charxml (#1642) 2024-11-17 18:17:29 -06:00
David Markowitz
677e7c1097 fix: remove ninjago missions items for completed missions (#1643)
* fix: ninjago missions remove items

fixes an issue where this mission was completed prior to a bug fix, causing the items to remain in the inventory.

Tested that players with the mission completed have the item correctly removed from their inventory.

* Update eCharacterVersion.h
2024-11-17 18:08:36 -06:00
David Markowitz
628ac9807e fix crash (#1644)
tested that sending an empty packet with this messageID no longer crashes the chat server
2024-11-17 18:07:40 -06:00
David Markowitz
de3fe93100 Update WorldServer.cpp (#1645) 2024-11-03 08:17:00 -06:00
David Markowitz
af943278fb Use macros so we can use more properties (#1640)
makes it so we can adjust many more settings since the segfault only happens in windows debug, why remove the functionality for all users?
Tested that windows debug, windows RelWithDebInfo and ubuntu default all build and run without issues (will contact luxaritas about pipe testing)
2024-10-30 09:45:40 -05:00
Gie "Max" Vanommeslaeghe
bfe6900c26 Merge pull request #1639 from DarkflameUniverse/FixPetCrash
fix: nullptr crashes in PetComponent AddDrainImaginationTimer and Deactivate
2024-10-27 22:23:49 +01:00
David Markowitz
0d218fc5c7 fix: PetComponent crashing due to nullptr access
Resolves an issue where item is null but is accessed but not doing that code and instead consulting the EntityManager for a valid Entity, alongside nullifying the m_Owner objectID should the pet be destroyed and timer still exist.

Update PetComponent.cpp

Add nullptr check

Add back timer

Update PetComponent.cpp

speculative fix for a different crash

Why are we accessing something before checking if its null
2024-10-26 21:44:19 -07:00
David Markowitz
102e3556cf feat: add millisecond tracking for racing (#1615)
* add millisecond tracking

Update RacingControlComponent.cpp

* remove const ig?

* is this what you wanted
2024-10-26 22:59:50 -05:00
David Markowitz
33a8efdd22 fix slow code, add bounds checks (#1606)
Tested that players with valid names up to the usual 33 character max are still added to the player container
Tested that you can still team with <= 4 players on a team
Tested that chat server no longer crashes with a bad memberSize variable
asserted that InsertPlayer is indeed much faster now and is no longer a slow point of ChatServer
2024-10-26 22:09:32 -05:00
David Markowitz
8d54db7851 Update WorldServer.cpp (#1633) 2024-09-13 09:45:55 -07:00
David Markowitz
6213aed8e5 fix ag navmesh near burtton (#1626)
tested that pets now correctly try to activate the button instead of standing idly by.
2024-09-13 09:45:46 -07:00
wincent
d57c5101f4 Amend preconditions fix 2024-09-12 15:33:09 +02:00
wincent
6be65569de Fixed mission related preconditions 2024-09-10 12:41:38 +02:00
David Markowitz
94b9731a2b Use the correct bit field for checking whether or not to decrement progress (#1631)
Tested that the cooking mission with johnny umami now no longer allows you to lose progress by deleting items.
2024-08-11 10:26:25 -07:00
David Markowitz
aaf446fe6e feat: Add Inventory Brick and Model groups (#1587)
* Added feature grouping logic

* Add saving of brick buckets

* Add edge case check for max group count

* Use vector for storing groups

* Update InventoryComponent.cpp

* Update InventoryComponent.h

* Update InventoryComponent.h

* fix string log format

* Update GameMessages.cpp
2024-08-01 23:38:21 -07:00
Gie "Max" Vanommeslaeghe
2c70f1503c Merge pull request #1616 from DarkflameUniverse/behavior
fix: add larger behavior fields for saving
2024-07-17 22:24:52 +02:00
Gie "Max" Vanommeslaeghe
49b4748ed3 Merge pull request #1614 from DarkflameUniverse/tea
fix: move mission progression location for racing
2024-07-17 22:24:17 +02:00
Gie "Max" Vanommeslaeghe
ffeb0108d0 Merge pull request #1619 from DarkflameUniverse/shield
fix: echo skill cast down to client for some server skills
2024-07-17 22:23:21 +02:00
David Markowitz
c3f6ef5a1d feat: add admin account creation options from cli (GM level) (#1620)
* add admin account creation options from cli

* use actual gm levels

felt under delivered in previous iteration.

* Update dMasterServer/MasterServer.cpp

Co-authored-by: Daniel Seiler <me@xiphoseer.de>

---------

Co-authored-by: Daniel Seiler <me@xiphoseer.de>
2024-07-03 15:37:19 -07:00
David Markowitz
999995b2fb Merge branch 'main' into shield 2024-07-02 03:14:32 -07:00
Gie "Max" Vanommeslaeghe
84fff7c380 Merge pull request #1621 from DarkflameUniverse/EmosewaMC-patch-10
fix: compiler issue on newer gcc versions
2024-07-02 12:12:30 +02:00
David Markowitz
59c4b35479 Update Preconditions.cpp 2024-07-02 01:55:42 -07:00
David Markowitz
f2d72e7ed5 Update Preconditions.cpp 2024-06-25 21:02:40 -07:00
David Markowitz
b648b43c4d ?? 2024-06-25 20:52:46 -07:00
David Markowitz
2628470482 set shield to false, add sync for done 2024-06-18 14:24:03 -07:00
David Markowitz
f82a82f254 Create 16_big_behaviors.sql 2024-06-17 19:41:27 -07:00
Gie "Max" Vanommeslaeghe
9400ee1dc0 Merge pull request #1586 from DarkflameUniverse/property_behavior_saving
feat: Property Behavior Saving
2024-06-16 12:31:21 +02:00
David Markowitz
54b8c25754 Merge branch 'main' into tea 2024-06-12 19:20:44 -07:00
David Markowitz
b984cd6a0b Merge branch 'main' into property_behavior_saving 2024-06-12 19:17:39 -07:00
Gie "Max" Vanommeslaeghe
bcf1058759 Merge pull request #1595 from DarkflameUniverse/issue-462
fix: prevent moving items between inventories under cetain circumsances
2024-06-11 20:48:09 +02:00
David Markowitz
6ad6e930c7 move mission progression
done now as soon as you cross the finish line on the last lap instead of after you click "rewards"
2024-06-11 02:29:25 -07:00
David Markowitz
fee0238e79 fix: master not using table data, remove 2 noisy logs (#1613)
Tested with logs that queries to get soft and hard cap actually succeed now
Logs about slash command handler command registration and vanity NPC creation in mis matched worlds are now removed.
2024-06-09 15:31:57 -07:00
Wincent Holm
1454fcd003 Fix g++ 14 (#1610)
* Fix g++ 14

* Update thirdparty/CMakeLists.txt

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 04:00:44 -05:00
Gie "Max" Vanommeslaeghe
af651f0d63 Merge pull request #1608 from DarkflameUniverse/feat--spectate-command
feat: spectate command
2024-06-04 09:17:18 +02:00
ff38503597 no feedback if empty 2024-06-03 22:51:46 -05:00
3f22bf5cc0 Add an easy way to stop spectating 2024-06-03 22:44:54 -05:00
9d5d2a68ee fix gm serialization 2024-06-03 22:30:57 -05:00
1a14c29c39 add returns, lol 2024-06-03 22:29:21 -05:00
2ef45bd7ee use empty 2024-06-03 22:28:37 -05:00
b56d077892 feat: spectate command 2024-06-03 21:50:12 -05:00
David Markowitz
a54600b41e busting out the multimap ig (#1602) 2024-05-31 13:46:18 -05:00
David Markowitz
342da927f5 fix dimantling items from not the model inventory (#1605) 2024-05-30 23:53:13 -05:00
David Markowitz
01086d05c8 fix: use after free and uninitialized memory (#1603)
* fix use after free and uninitialized memory

* add if check for packet lengths

* move purge down further

Its used in the if check too...
2024-05-30 23:53:03 -05:00
Remco Hofman
cce5755366 Fix Dockerfile vanity COPY (#1604)
Corrected an unintended mistake in the COPY commands for adding the
vanity files to the Docker container, causing only the last file
contents to be added to the file '/app/vanity/*'
2024-05-27 17:46:09 -07:00
TAHuntling
e966d3a644 Chore: split VE script up (#1598)
* Testing Scripts

Testing splitting AgSpaceStuff into AgSpaceStuff and AgShipShake

* fixed inclusions

* Removed DoShake

* cleaning up

* consistent if statements

* Update dScripts/ai/AG/AgShipShake.h

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-05-27 01:24:48 -05:00
Gie "Max" Vanommeslaeghe
9328021339 Merge pull request #1600 from DarkflameUniverse/add-missing-scripts
fix: add back missing scripts from scripts refactor
2024-05-26 12:02:12 +02:00
Gie "Max" Vanommeslaeghe
d1134fdd62 Merge pull request #1601 from DarkflameUniverse/fix-using-skill-in-race
fix: players using non-car skills in a race
2024-05-26 12:01:54 +02:00
David Markowitz
efa658bc31 fix players using non-car skills in a race 2024-05-25 19:59:15 -07:00
David Markowitz
e59525d2ae Update CppScripts.cpp 2024-05-25 19:32:18 -07:00
David Markowitz
0348db72a5 fiux mission (#1596) 2024-05-25 12:24:02 -05:00
TAHuntling
debc2a96e2 Update CppScripts exclusion list (#1597) 2024-05-25 01:43:32 -07:00
86f335d64b fix type 2024-05-24 21:43:54 -05:00
8ca05241f2 fix: prevent moving items between inventories under cetain circumsances 2024-05-24 21:35:14 -05:00
David Markowitz
8ae1a8ff7c fix stale reference (#1594) 2024-05-24 09:15:30 -05:00
David Markowitz
db2d4f02b5 preemptive include for windows 2024-05-18 04:16:07 -07:00
David Markowitz
00f36f3f28 missing include for windows 2024-05-18 04:15:07 -07:00
David Markowitz
a50b256689 Update IBehaviors.h 2024-05-18 03:54:09 -07:00
David Markowitz
b3548de7da debug logs and spacing 2024-05-18 03:52:36 -07:00
David Markowitz
387c37505c undo debug changes 2024-05-18 03:39:25 -07:00
David Markowitz
0c4108e730 Add loading from database
yahoo
2024-05-18 03:36:29 -07:00
David Markowitz
fd1c6ab2ea Saving actually works this time 2024-05-18 02:12:23 -07:00
David Markowitz
f2bf9a2a28 Saving to database working 2024-05-18 02:05:55 -07:00
186 changed files with 5950 additions and 3759 deletions

View File

@@ -73,4 +73,4 @@ cpp_space_around_assignment_operator=insert
cpp_space_pointer_reference_alignment=left
cpp_space_around_ternary_operator=insert
cpp_wrap_preserve_blocks=one_liners
cpp_indent_comment=fasle
cpp_indent_comment=false

View File

@@ -16,12 +16,12 @@ jobs:
os: [ windows-2022, ubuntu-22.04, macos-13 ]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
submodules: true
- name: Add msbuild to PATH (Windows only)
if: ${{ matrix.os == 'windows-2022' }}
uses: microsoft/setup-msbuild@v1.1
uses: microsoft/setup-msbuild@v2
with:
vs-version: '[17,18)'
msbuild-architecture: x64
@@ -33,21 +33,19 @@ jobs:
- name: cmake
uses: lukka/run-cmake@v10
with:
configurePreset: "ci-${{matrix.os}}"
buildPreset: "ci-${{matrix.os}}"
testPreset: "ci-${{matrix.os}}"
workflowPreset: "ci-${{matrix.os}}"
- name: artifacts
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: build-${{matrix.os}}
path: |
build/*Server*
build/*.ini
build/*.so
build/*.dll
build/vanity/
build/navmeshes/
build/migrations/
build/*.dcf
!build/*.pdb
!build/d*/
build/*/*Server*
build/*/*.ini
build/*/*.so
build/*/*.dll
build/*/vanity/
build/*/navmeshes/
build/*/migrations/
build/*/*.dcf
!build/*/*.pdb
!build/*/d*/

3
.gitignore vendored
View File

@@ -122,4 +122,7 @@ docker/__pycache__
docker-compose.override.yml
!*Test.bin
# CMake scripts
!cmake/*
!cmake/toolchains/*

View File

@@ -1,15 +1,24 @@
cmake_minimum_required(VERSION 3.25)
project(Darkflame)
project(Darkflame
HOMEPAGE_URL "https://github.com/DarkflameUniverse/DarkflameServer"
LANGUAGES C CXX
)
# check if the path to the source directory contains a space
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
endif()
if (NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "The default build type" FORCE)
endif()
include(CTest)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
@@ -61,35 +70,36 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC")
add_compile_options("-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")
# For all except Clang and Apple Clang
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-static-libgcc" "-lstdc++fs")
endif()
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
add_link_options("-export-dynamic")
endif()
if(${GGDB})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
add_compile_options("-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
# Also disable non-portable MSVC volatile behavior
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
add_compile_options("/wd4267" "/utf-8" "/volatile:iso" "/Zc:inline")
elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
# Our output dir
set(CMAKE_BINARY_DIR ${PROJECT_BINARY_DIR})
#set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) # unfortunately, forces all libraries to be built in series, which will slow down the build process
# TODO make this not have to override the build type directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
@@ -109,31 +119,39 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
# Create a /logs directory
make_directory(${CMAKE_BINARY_DIR}/logs)
# Get DLU config directory
if(DEFINED ENV{DLU_CONFIG_DIR})
set(DLU_CONFIG_DIR $ENV{DLU_CONFIG_DIR})
else()
set(DLU_CONFIG_DIR ${PROJECT_BINARY_DIR})
endif()
message(STATUS "Variable: DLU_CONFIG_DIR = ${DLU_CONFIG_DIR}")
# Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
message(STATUS "Checking resource file integrity")
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")
UpdateConfigOption(${DLU_CONFIG_DIR}/authconfig.ini "port" "auth_server_port")
UpdateConfigOption(${DLU_CONFIG_DIR}/chatconfig.ini "port" "chat_server_port")
UpdateConfigOption(${DLU_CONFIG_DIR}/masterconfig.ini "port" "master_server_port")
foreach(resource_file ${RESOURCE_FILES})
set(file_size 0)
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
if(EXISTS ${DLU_CONFIG_DIR}/${resource_file})
file(SIZE ${DLU_CONFIG_DIR}/${resource_file} file_size)
endif()
if(${file_size} EQUAL 0)
configure_file(
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${PROJECT_BINARY_DIR}/${resource_file}
${CMAKE_SOURCE_DIR}/resources/${resource_file} ${DLU_CONFIG_DIR}/${resource_file}
COPYONLY
)
message(STATUS "Moved " ${resource_file} " to project binary directory")
message(STATUS "Moved " ${resource_file} " to DLU config directory")
elseif(resource_file MATCHES ".ini")
message(STATUS "Checking " ${resource_file} " for missing config options")
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
file(READ ${DLU_CONFIG_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 "")
@@ -164,10 +182,10 @@ foreach(resource_file ${RESOURCE_FILES})
set(line_to_add ${line_to_add} ${line})
foreach(line_to_append ${line_to_add})
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
endforeach()
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
endif()
set(line_to_add "")
@@ -236,14 +254,15 @@ include_directories(
"tests/dGameTests"
"tests/dGameTests/dComponentsTests"
SYSTEM "thirdparty/magic_enum/include/magic_enum"
SYSTEM "thirdparty/raknet/Source"
SYSTEM "thirdparty/tinyxml2"
SYSTEM "thirdparty/recastnavigation"
SYSTEM "thirdparty/SQLite"
SYSTEM "thirdparty/cpplinq"
SYSTEM "thirdparty/cpp-httplib"
SYSTEM "thirdparty/MD5"
SYSTEM
"thirdparty/magic_enum/include/magic_enum"
"thirdparty/raknet/Source"
"thirdparty/tinyxml2"
"thirdparty/recastnavigation"
"thirdparty/SQLite"
"thirdparty/cpplinq"
"thirdparty/cpp-httplib"
"thirdparty/MD5"
)
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
@@ -252,10 +271,17 @@ if(APPLE)
include_directories("/usr/local/include/")
endif()
# Add linking directories:
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
# Set warning flags
if(MSVC)
# add_compile_options("/W4")
# Want to enable warnings eventually, but WAY too much noise right now
elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
add_compile_options("-Wuninitialized" "-Wold-style-cast")
else()
message(WARNING "Unknown compiler: '${CMAKE_CXX_COMPILER_ID}' - No warning flags enabled.")
endif()
# Add linking directories:
file(
GLOB HEADERS_DZONEMANAGER
LIST_DIRECTORIES false

View File

@@ -1,128 +1,641 @@
{
"version": 3,
"cmakeMinimumRequired": {
"major": 3,
"minor": 14,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build",
"generator": "Unix Makefiles"
},
{
"name": "ci-ubuntu-22.04",
"displayName": "CI configure step for Ubuntu",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "default"
},
{
"name": "ci-macos-13",
"displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow",
"inherits": "default"
},
{
"name": "ci-windows-2022",
"displayName": "CI configure step for Windows",
"description": "Set architecture to 64-bit (b/c RakNet)",
"inherits": "default",
"generator": "Visual Studio 17 2022",
"architecture": {
"value": "x64"
},
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "windows-default",
"inherits": "ci-windows-2022",
"displayName": "Windows only Configure Settings",
"description": "Sets build and install directories",
"generator": "Ninja",
"architecture": {
"value": "x64",
"strategy": "external"
}
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default",
"displayName": "Default Build",
"description": "Default Build",
"jobs": 2
},
{
"name": "ci-windows-2022",
"configurePreset": "ci-windows-2022",
"displayName": "Windows CI Build",
"description": "This preset is used by the CI build on windows",
"configuration": "RelWithDebInfo",
"jobs": 2
},
{
"name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-22.04",
"displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux",
"jobs": 2
},
{
"name": "ci-macos-13",
"configurePreset": "ci-macos-13",
"displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS",
"jobs": 2
}
],
"testPresets": [
{
"name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-22.04",
"displayName": "CI Tests on Linux",
"description": "Runs all tests on a linux configuration",
"execution": {
"jobs": 2
"version": 6,
"cmakeMinimumRequired": {
"major": 3,
"minor": 25,
"patch": 0
},
"configurePresets": [
{
"name": "default",
"displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build",
"environment": {
"DLU_CONFIG_DIR": "${sourceDir}/build"
},
"generator": "Unix Makefiles"
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-macos-13",
"configurePreset": "ci-macos-13",
"displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "ci-windows-2022",
"configurePreset": "ci-windows-2022",
"displayName": "CI Tests on windows",
"description": "Runs all tests on a windows configuration",
"configuration": "RelWithDebInfo",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
},
"filter": {
"exclude": {
"name": "((example)|(minigzip))+"
{
"name": "debug-config",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Debug"
}
},
{
"name": "relwithdebinfo-config",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo"
}
},
{
"name": "release-config",
"hidden": true,
"cacheVariables": {
"CMAKE_BUILD_TYPE": "Release"
}
},
{
"name": "clang-config",
"hidden": true,
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-clang.cmake"
},
{
"name": "gnu-config",
"hidden": true,
"toolchainFile": "${sourceDir}/cmake/toolchains/linux-gnu.cmake"
},
{
"name": "windows-msvc",
"inherits": "default",
"displayName": "[Multi] Windows (MSVC)",
"description": "Set architecture to 64-bit (b/c RakNet)",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build/msvc",
"architecture": {
"value": "x64"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows-default",
"inherits": "windows-msvc",
"displayName": "Windows only Configure Settings",
"description": "Sets build and install directories",
"generator": "Ninja",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
},
"architecture": {
"value": "x64"
}
},
{
"name": "linux-config",
"inherits": "default",
"hidden": true,
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Linux"
}
},
{
"name": "linux-clang-debug",
"inherits": [
"linux-config",
"clang-config",
"debug-config"
],
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
"description": "Create a debug build using the Clang toolchain for Linux",
"binaryDir": "${sourceDir}/build/clang-debug"
},
{
"name": "linux-clang-relwithdebinfo",
"inherits": [
"linux-config",
"clang-config",
"relwithdebinfo-config"
],
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
"description": "Create a release build with debug info using the Clang toolchain for Linux",
"binaryDir": "${sourceDir}/build/clang-relwithdebinfo"
},
{
"name": "linux-clang-release",
"inherits": [
"linux-config",
"clang-config",
"release-config"
],
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
"description": "Create a release build using the Clang toolchain for Linux",
"binaryDir": "${sourceDir}/build/clang-release"
},
{
"name": "linux-gnu-debug",
"inherits": [
"linux-config",
"gnu-config",
"debug-config"
],
"displayName": "[Debug] Linux (GNU)",
"description": "Create a debug build using the GNU toolchain for Linux",
"binaryDir": "${sourceDir}/build/gnu-debug"
},
{
"name": "linux-gnu-relwithdebinfo",
"inherits": [
"linux-config",
"gnu-config",
"relwithdebinfo-config"
],
"displayName": "[RelWithDebInfo] Linux (GNU)",
"description": "Create a release build with debug info using the GNU toolchain for Linux",
"binaryDir": "${sourceDir}/build/gnu-relwithdebinfo"
},
{
"name": "linux-gnu-release",
"inherits": [
"linux-config",
"gnu-config",
"release-config"
],
"displayName": "[Release] Linux (GNU)",
"description": "Create a release build using the GNU toolchain for Linux",
"binaryDir": "${sourceDir}/build/gnu-release"
},
{
"name": "macos",
"inherits": "default",
"displayName": "[Multi] MacOS",
"description": "Create a build for MacOS",
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Darwin"
},
"binaryDir": "${sourceDir}/build/macos"
}
}
]
}
],
"buildPresets": [
{
"name": "default",
"configurePreset": "default",
"displayName": "Default Build",
"description": "Default Build",
"jobs": 2
},
{
"name": "windows-msvc-debug",
"inherits": "default",
"configurePreset": "windows-msvc",
"displayName": "[Debug] Windows (MSVC)",
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
"configuration": "Debug"
},
{
"name": "windows-msvc-relwithdebinfo",
"inherits": "default",
"configurePreset": "windows-msvc",
"displayName": "[RelWithDebInfo] Windows (MSVC)",
"description": "This preset is used to build in debug mode using the MSVC toolchain on Windows",
"configuration": "RelWithDebInfo"
},
{
"name": "windows-msvc-release",
"inherits": "default",
"configurePreset": "windows-msvc",
"displayName": "[Release] Windows (MSVC)",
"description": "This preset is used to build in release mode using the MSVC toolchain on Windows",
"configuration": "Release"
},
{
"name": "linux-clang-debug",
"inherits": "default",
"configurePreset": "linux-clang-debug",
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
"description": "This preset is used to build in debug mode using the Clang toolchain on Linux",
"configuration": "Debug"
},
{
"name": "linux-clang-relwithdebinfo",
"inherits": "default",
"configurePreset": "linux-clang-relwithdebinfo",
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
"description": "This preset is used to build in release mode with debug info using the Clang toolchain on Linux",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-clang-release",
"inherits": "default",
"configurePreset": "linux-clang-release",
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
"description": "This preset is used to build in release mode using the Clang toolchain on Linux",
"configuration": "Release"
},
{
"name": "linux-gnu-debug",
"inherits": "default",
"configurePreset": "linux-gnu-debug",
"displayName": "[Debug] Linux (GNU)",
"description": "This preset is used to build in debug mode using the GNU toolchain on Linux",
"configuration": "Debug"
},
{
"name": "linux-gnu-relwithdebinfo",
"inherits": "default",
"configurePreset": "linux-gnu-relwithdebinfo",
"displayName": "[RelWithDebInfo] Linux (GNU)",
"description": "This preset is used to build in release mode with debug info using the GNU toolchain on Linux",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-gnu-release",
"inherits": "default",
"configurePreset": "linux-gnu-release",
"displayName": "[Release] Linux (GNU)",
"description": "This preset is used to build in release mode using the GNU toolchain on Linux",
"configuration": "Release"
},
{
"name": "macos-debug",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[Debug] MacOS",
"description": "This preset is used to build in debug mode on MacOS",
"configuration": "Debug"
},
{
"name": "macos-relwithdebinfo",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[RelWithDebInfo] MacOS",
"description": "This preset is used to build in release mode with debug info on MacOS",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-release",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[Release] MacOS",
"description": "This preset is used to build in release mode on MacOS",
"configuration": "Release"
}
],
"testPresets": [
{
"name": "default",
"configurePreset": "default",
"execution": {
"jobs": 2
},
"output": {
"outputOnFailure": true
}
},
{
"name": "windows-msvc-test",
"inherits": "default",
"configurePreset": "windows-msvc",
"hidden": true,
"filter": {
"exclude": {
"name": "((example)|(minigzip))+"
}
}
},
{
"name": "windows-msvc-debug",
"inherits": "windows-msvc-test",
"configurePreset": "windows-msvc",
"displayName": "[Debug] Windows (MSVC)",
"description": "Runs all tests on a Windows configuration",
"configuration": "Debug"
},
{
"name": "windows-msvc-relwithdebinfo",
"inherits": "windows-msvc-test",
"configurePreset": "windows-msvc",
"displayName": "[RelWithDebInfo] Windows (MSVC)",
"description": "Runs all tests on a Windows configuration",
"configuration": "RelWithDebInfo"
},
{
"name": "windows-msvc-release",
"inherits": "windows-msvc-test",
"configurePreset": "windows-msvc",
"displayName": "[Release] Windows (MSVC)",
"description": "Runs all tests on a Windows configuration",
"configuration": "Release"
},
{
"name": "linux-clang-debug",
"inherits": "default",
"configurePreset": "linux-clang-debug",
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
"description": "Runs all tests on a Linux Clang configuration",
"configuration": "Release"
},
{
"name": "linux-clang-relwithdebinfo",
"inherits": "default",
"configurePreset": "linux-clang-relwithdebinfo",
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
"description": "Runs all tests on a Linux Clang configuration",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-clang-release",
"inherits": "default",
"configurePreset": "linux-clang-release",
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
"description": "Runs all tests on a Linux Clang configuration",
"configuration": "Release"
},
{
"name": "linux-gnu-debug",
"inherits": "default",
"configurePreset": "linux-gnu-debug",
"displayName": "[Debug] Linux (GNU)",
"description": "Runs all tests on a Linux GNU configuration",
"configuration": "Release"
},
{
"name": "linux-gnu-relwithdebinfo",
"inherits": "default",
"configurePreset": "linux-gnu-relwithdebinfo",
"displayName": "[RelWithDebInfo] Linux (GNU)",
"description": "Runs all tests on a Linux GNU configuration",
"configuration": "RelWithDebInfo"
},
{
"name": "linux-gnu-release",
"inherits": "default",
"configurePreset": "linux-gnu-release",
"displayName": "[Release] Linux (GNU)",
"description": "Runs all tests on a Linux GNU configuration",
"configuration": "Release"
},
{
"name": "macos-debug",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[Debug] MacOS",
"description": "Runs all tests on a MacOS configuration",
"configuration": "Debug"
},
{
"name": "macos-relwithdebinfo",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[RelWithDebInfo] MacOS",
"description": "Runs all tests on a MacOS configuration",
"configuration": "RelWithDebInfo"
},
{
"name": "macos-release",
"inherits": "default",
"configurePreset": "macos",
"displayName": "[Release] MacOS",
"description": "Runs all tests on a MacOS configuration",
"configuration": "Release"
}
],
"workflowPresets": [
{
"name": "default",
"steps": [
{
"type": "configure",
"name": "default"
},
{
"type": "build",
"name": "default"
},
{
"type": "test",
"name": "default"
}
]
},
{
"name": "windows-msvc-debug",
"displayName": "[Debug] Windows (MSVC)",
"description": "MSVC debug workflow preset for Windows",
"steps": [
{
"type": "configure",
"name": "windows-msvc"
},
{
"type": "build",
"name": "windows-msvc-debug"
},
{
"type": "test",
"name": "windows-msvc-debug"
}
]
},
{
"name": "windows-msvc-relwithdebinfo",
"displayName": "[RelWithDebInfo] Windows (MSVC)",
"description": "MSVC release with debug info workflow preset for Windows",
"steps": [
{
"type": "configure",
"name": "windows-msvc"
},
{
"type": "build",
"name": "windows-msvc-relwithdebinfo"
},
{
"type": "test",
"name": "windows-msvc-relwithdebinfo"
}
]
},
{
"name": "ci-windows-2022",
"displayName": "[Release] Windows (MSVC)",
"description": "CI workflow preset for Windows",
"steps": [
{
"type": "configure",
"name": "windows-msvc"
},
{
"type": "build",
"name": "windows-msvc-release"
},
{
"type": "test",
"name": "windows-msvc-release"
}
]
},
{
"name": "linux-gnu-debug",
"displayName": "[Debug] Linux (GNU)",
"description": "GNU debug workflow preset for Linux",
"steps": [
{
"type": "configure",
"name": "linux-gnu-debug"
},
{
"type": "build",
"name": "linux-gnu-debug"
},
{
"type": "test",
"name": "linux-gnu-debug"
}
]
},
{
"name": "linux-gnu-relwithdebinfo",
"displayName": "[RelWithDebInfo] Linux (GNU)",
"description": "GNU release with debug info workflow preset for Linux",
"steps": [
{
"type": "configure",
"name": "linux-gnu-relwithdebinfo"
},
{
"type": "build",
"name": "linux-gnu-relwithdebinfo"
},
{
"type": "test",
"name": "linux-gnu-relwithdebinfo"
}
]
},
{
"name": "ci-ubuntu-22.04",
"displayName": "[Release] Linux (GNU)",
"description": "CI workflow preset for Ubuntu",
"steps": [
{
"type": "configure",
"name": "linux-gnu-release"
},
{
"type": "build",
"name": "linux-gnu-release"
},
{
"type": "test",
"name": "linux-gnu-release"
}
]
},
{
"name": "linux-clang-debug",
"displayName": "EXPERIMENTAL - [Debug] Linux (Clang)",
"description": "Clang debug workflow preset for Linux",
"steps": [
{
"type": "configure",
"name": "linux-clang-debug"
},
{
"type": "build",
"name": "linux-clang-debug"
},
{
"type": "test",
"name": "linux-clang-debug"
}
]
},
{
"name": "linux-clang-relwithdebinfo",
"displayName": "EXPERIMENTAL - [RelWithDebInfo] Linux (Clang)",
"description": "Clang release with debug info workflow preset for Linux",
"steps": [
{
"type": "configure",
"name": "linux-clang-relwithdebinfo"
},
{
"type": "build",
"name": "linux-clang-relwithdebinfo"
},
{
"type": "test",
"name": "linux-clang-relwithdebinfo"
}
]
},
{
"name": "linux-clang-release",
"displayName": "EXPERIMENTAL - [Release] Linux (Clang)",
"description": "Clang release workflow preset for Linux",
"steps": [
{
"type": "configure",
"name": "linux-clang-release"
},
{
"type": "build",
"name": "linux-clang-release"
},
{
"type": "test",
"name": "linux-clang-release"
}
]
},
{
"name": "macos-debug",
"displayName": "[Debug] MacOS",
"description": "Release workflow preset for MacOS",
"steps": [
{
"type": "configure",
"name": "macos"
},
{
"type": "build",
"name": "macos-debug"
},
{
"type": "test",
"name": "macos-debug"
}
]
},
{
"name": "macos-relwithdebinfo",
"displayName": "[RelWithDebInfo] MacOS",
"description": "Release with debug info workflow preset for MacOS",
"steps": [
{
"type": "configure",
"name": "macos"
},
{
"type": "build",
"name": "macos-relwithdebinfo"
},
{
"type": "test",
"name": "macos-relwithdebinfo"
}
]
},
{
"name": "ci-macos-13",
"displayName": "[Release] MacOS",
"description": "CI workflow preset for MacOS",
"steps": [
{
"type": "configure",
"name": "macos"
},
{
"type": "build",
"name": "macos-release"
},
{
"type": "test",
"name": "macos-release"
}
]
}
]
}

View File

@@ -31,7 +31,7 @@ COPY --from=build /app/build/*Server /app/
# Necessary suplimentary files
COPY --from=build /app/build/*.ini /app/configs/
COPY --from=build /app/build/vanity/*.* /app/vanity/*
COPY --from=build /app/build/vanity/*.* /app/vanity/
COPY --from=build /app/build/navmeshes /app/navmeshes
COPY --from=build /app/build/migrations /app/migrations
COPY --from=build /app/build/*.dcf /app/
@@ -39,7 +39,7 @@ COPY --from=build /app/build/*.dcf /app/
# backup of config and vanity files to copy to the host incase
# of a mount clobbering the copy from above
COPY --from=build /app/build/*.ini /app/default-configs/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/*
COPY --from=build /app/build/vanity/*.* /app/default-vanity/
# needed as the container runs with the root user
# and therefore sudo doesn't exist

View File

@@ -6,8 +6,7 @@ mkdir -p build
cd build
# Run cmake to generate make files
cmake ..
cmake -DCMAKE_BUILD_TYPE="Release" ..
# To build utilizing multiple cores, append `-j` and the amount of cores to utilize, for example `cmake --build . --config Release -j8'
cmake --build . --config Release $1

View File

@@ -0,0 +1,14 @@
# Try and find a clang-16 install, falling back to a generic clang install otherwise
find_program(CLANG_C_COMPILER clang-16 | clang REQUIRED)
find_program(CLANG_CXX_COMPILER clang++-16 | clang++ REQUIRED)
# Debug messages
message(DEBUG "CLANG_C_COMPILER = ${CLANG_C_COMPILER}")
message(DEBUG "CLANG_CXX_COMPILER = ${CLANG_CXX_COMPILER}")
# Set compilers to clang (need to cache for VSCode tools to work correctly)
set(CMAKE_C_COMPILER ${CLANG_C_COMPILER} CACHE STRING "Set C compiler")
set(CMAKE_CXX_COMPILER ${CLANG_CXX_COMPILER} CACHE STRING "Set C++ compiler")
# Set linker to lld
add_link_options("-fuse-ld=lld")

View File

@@ -0,0 +1,11 @@
# Try and find a gcc/g++ install
find_program(GNU_C_COMPILER cc | gcc REQUIRED)
find_program(GNU_CXX_COMPILER c++ | g++ REQUIRED)
# Debug messages
message(DEBUG "GNU_C_COMPILER = ${GNU_C_COMPILER}")
message(DEBUG "GNU_CXX_COMPILER = ${GNU_CXX_COMPILER}")
# Set compilers to GNU (need to cache for VSCode tools to work correctly)
set(CMAKE_C_COMPILER ${GNU_C_COMPILER} CACHE STRING "Set C compiler")
set(CMAKE_CXX_COMPILER ${GNU_CXX_COMPILER} CACHE STRING "Set C++ compiler")

View File

@@ -21,8 +21,8 @@
//Auth includes:
#include "AuthPackets.h"
#include "eConnectionType.h"
#include "eServerMessageType.h"
#include "eAuthMessageType.h"
#include "MessageType/Server.h"
#include "MessageType/Auth.h"
#include "Game.h"
#include "Server.h"
@@ -60,7 +60,7 @@ int main(int argc, char** argv) {
try {
Database::Connect();
} catch (sql::SQLException& ex) {
} catch (std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer");
delete Game::server;
@@ -102,7 +102,7 @@ int main(int argc, char** argv) {
uint32_t framesSinceLastSQLPing = 0;
AuthPackets::LoadClaimCodes();
Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master:
@@ -166,11 +166,11 @@ void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_USER_PACKET_ENUM) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) {
if (static_cast<eServerMessageType>(packet->data[3]) == eServerMessageType::VERSION_CONFIRM) {
if (static_cast<MessageType::Server>(packet->data[3]) == MessageType::Server::VERSION_CONFIRM) {
AuthPackets::HandleHandshake(Game::server, packet);
}
} else if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::AUTH) {
if (static_cast<eAuthMessageType>(packet->data[3]) == eAuthMessageType::LOGIN_REQUEST) {
if (static_cast<MessageType::Auth>(packet->data[3]) == MessageType::Auth::LOGIN_REQUEST) {
AuthPackets::HandleLoginRequest(Game::server, packet);
}
}

View File

@@ -1,6 +1,6 @@
#include "ChatIgnoreList.h"
#include "PlayerContainer.h"
#include "eChatMessageType.h"
#include "MessageType/Chat.h"
#include "BitStreamUtils.h"
#include "Game.h"
#include "Logger.h"
@@ -13,7 +13,7 @@
// The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receivingPlayer);
//portion that will get routed:

View File

@@ -13,9 +13,9 @@
#include "dConfig.h"
#include "eObjectBits.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "eClientMessageType.h"
#include "eGameMessageType.h"
#include "MessageType/Chat.h"
#include "MessageType/Client.h"
#include "MessageType/Game.h"
#include "StringifiedEnum.h"
#include "eGameMasterLevel.h"
#include "ChatPackets.h"
@@ -60,11 +60,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from:
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::GET_FRIENDS_LIST_RESPONSE);
bitStream.Write<uint8_t>(0);
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write<uint16_t>(player.friends.size());
@@ -368,10 +368,10 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
bool online = player;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::WHO_RESPONSE);
bitStream.Write<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID());
@@ -391,10 +391,10 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
if (!sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::SHOW_ALL_RESPONSE);
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount());
@@ -416,7 +416,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
SEND_PACKET;
}
// the structure the client uses to send this packet is shared in many chat messages
// the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandleChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER;
@@ -428,7 +428,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
eChatChannel channel;
uint32_t size;
inStream.IgnoreBytes(4);
inStream.Read(channel);
inStream.Read(size);
@@ -436,7 +436,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
LUWString message(size);
inStream.Read(message);
LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str());
switch (channel) {
@@ -457,7 +457,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
}
}
// the structure the client uses to send this packet is shared in many chat messages
// the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER;
@@ -484,7 +484,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
LUWString message(size);
inStream.Read(message);
LOG("Got a message from (%s) via [%s]: %s to %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str(), receiverName.c_str());
const auto& receiver = Game::playerContainer.GetPlayerData(receiverName);
@@ -515,10 +515,10 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(routeTo.playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::PRIVATE_CHAT_MESSAGE);
bitStream.Write(sender.playerID);
bitStream.Write(channel);
bitStream.Write<uint32_t>(0); // not used
@@ -757,11 +757,11 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::TEAM_INVITE);
bitStream.Write(LUWString(sender.playerName.c_str()));
bitStream.Write(sender.playerID);
@@ -772,14 +772,14 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_INVITE_CONFIRM);
bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
bitStream.Write(bLeaderIsFreeTrial);
bitStream.Write(i64LeaderID);
@@ -799,14 +799,14 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_GET_STATUS_RESPONSE);
bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
bitStream.Write(i64LeaderID);
bitStream.Write(i64LeaderZoneID);
@@ -824,14 +824,14 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_SET_LEADER);
bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
bitStream.Write(i64PlayerID);
@@ -841,14 +841,14 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_ADD_PLAYER);
bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
bitStream.Write(bIsFreeTrial);
bitStream.Write(bLocal);
@@ -870,14 +870,14 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_REMOVE_PLAYER);
bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
bitStream.Write(bDisband);
bitStream.Write(bIsKicked);
@@ -896,14 +896,14 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
CMSGHEADER;
bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_SET_OFF_WORLD_FLAG);
bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
bitStream.Write(i64PlayerID);
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
@@ -930,11 +930,11 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is FTP*/
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(friendData.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData.playerName.c_str();
@@ -967,11 +967,11 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
}
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_REQUEST);
bitStream.Write(LUWString(sender.playerName));
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
@@ -981,11 +981,11 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
// Portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
@@ -1004,11 +1004,11 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::REMOVE_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(isSuccessful); //isOnline
bitStream.Write(LUWString(personToRemove));

View File

@@ -16,8 +16,8 @@
#include "eConnectionType.h"
#include "PlayerContainer.h"
#include "ChatPacketHandler.h"
#include "eChatMessageType.h"
#include "eWorldMessageType.h"
#include "MessageType/Chat.h"
#include "MessageType/World.h"
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
@@ -81,7 +81,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database
try {
Database::Connect();
} catch (sql::SQLException& ex) {
} catch (std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer");
delete Game::server;
@@ -108,7 +108,7 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize();
@@ -179,6 +179,7 @@ int main(int argc, char** argv) {
}
void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
LOG("A server has disconnected, erasing their connected players from the list.");
} else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
@@ -189,154 +190,154 @@ void HandlePacket(Packet* packet) {
inStream.SetReadOffset(BYTES_TO_BITS(1));
eConnectionType connection;
eChatMessageType chatMessageID;
MessageType::Chat chatMessageID;
inStream.Read(connection);
if (connection != eConnectionType::CHAT) return;
inStream.Read(chatMessageID);
switch (chatMessageID) {
case eChatMessageType::GM_MUTE:
case MessageType::Chat::GM_MUTE:
Game::playerContainer.MuteUpdate(packet);
break;
case eChatMessageType::CREATE_TEAM:
case MessageType::Chat::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet);
break;
case eChatMessageType::GET_FRIENDS_LIST:
case MessageType::Chat::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet);
break;
case eChatMessageType::GET_IGNORE_LIST:
case MessageType::Chat::GET_IGNORE_LIST:
ChatIgnoreList::GetIgnoreList(packet);
break;
case eChatMessageType::ADD_IGNORE:
case MessageType::Chat::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet);
break;
case eChatMessageType::REMOVE_IGNORE:
case MessageType::Chat::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet);
break;
case eChatMessageType::TEAM_GET_STATUS:
case MessageType::Chat::TEAM_GET_STATUS:
ChatPacketHandler::HandleTeamStatusRequest(packet);
break;
case eChatMessageType::ADD_FRIEND_REQUEST:
case MessageType::Chat::ADD_FRIEND_REQUEST:
//this involves someone sending the initial request, the response is below, response as in from the other player.
//We basically just check to see if this player is online or not and route the packet.
ChatPacketHandler::HandleFriendRequest(packet);
break;
case eChatMessageType::ADD_FRIEND_RESPONSE:
case MessageType::Chat::ADD_FRIEND_RESPONSE:
//This isn't the response a server sent, rather it is a player's response to a received request.
//Here, we'll actually have to add them to eachother's friend lists depending on the response code.
ChatPacketHandler::HandleFriendResponse(packet);
break;
case eChatMessageType::REMOVE_FRIEND:
case MessageType::Chat::REMOVE_FRIEND:
ChatPacketHandler::HandleRemoveFriend(packet);
break;
case eChatMessageType::GENERAL_CHAT_MESSAGE:
case MessageType::Chat::GENERAL_CHAT_MESSAGE:
ChatPacketHandler::HandleChatMessage(packet);
break;
case eChatMessageType::PRIVATE_CHAT_MESSAGE:
case MessageType::Chat::PRIVATE_CHAT_MESSAGE:
//This message is supposed to be echo'd to both the sender and the receiver
//BUT: they have to have different responseCodes, so we'll do some of the ol hacky wacky to fix that right up.
ChatPacketHandler::HandlePrivateChatMessage(packet);
break;
case eChatMessageType::TEAM_INVITE:
case MessageType::Chat::TEAM_INVITE:
ChatPacketHandler::HandleTeamInvite(packet);
break;
case eChatMessageType::TEAM_INVITE_RESPONSE:
case MessageType::Chat::TEAM_INVITE_RESPONSE:
ChatPacketHandler::HandleTeamInviteResponse(packet);
break;
case eChatMessageType::TEAM_LEAVE:
case MessageType::Chat::TEAM_LEAVE:
ChatPacketHandler::HandleTeamLeave(packet);
break;
case eChatMessageType::TEAM_SET_LEADER:
case MessageType::Chat::TEAM_SET_LEADER:
ChatPacketHandler::HandleTeamPromote(packet);
break;
case eChatMessageType::TEAM_KICK:
case MessageType::Chat::TEAM_KICK:
ChatPacketHandler::HandleTeamKick(packet);
break;
case eChatMessageType::TEAM_SET_LOOT:
case MessageType::Chat::TEAM_SET_LOOT:
ChatPacketHandler::HandleTeamLootOption(packet);
break;
case eChatMessageType::GMLEVEL_UPDATE:
case MessageType::Chat::GMLEVEL_UPDATE:
ChatPacketHandler::HandleGMLevelUpdate(packet);
break;
case eChatMessageType::LOGIN_SESSION_NOTIFY:
case MessageType::Chat::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatMessageType::GM_ANNOUNCE:{
case MessageType::Chat::GM_ANNOUNCE:{
// we just forward this packet to every connected server
inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
}
break;
case eChatMessageType::UNEXPECTED_DISCONNECT:
case MessageType::Chat::UNEXPECTED_DISCONNECT:
Game::playerContainer.RemovePlayer(packet);
break;
case eChatMessageType::WHO:
case MessageType::Chat::WHO:
ChatPacketHandler::HandleWho(packet);
break;
case eChatMessageType::SHOW_ALL:
case MessageType::Chat::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet);
break;
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
case eChatMessageType::WORLD_PARCEL_RESPONSE:
case eChatMessageType::TEAM_MISSED_INVITE_CHECK:
case eChatMessageType::GUILD_CREATE:
case eChatMessageType::GUILD_INVITE:
case eChatMessageType::GUILD_INVITE_RESPONSE:
case eChatMessageType::GUILD_LEAVE:
case eChatMessageType::GUILD_KICK:
case eChatMessageType::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_ALL:
case eChatMessageType::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODEL_READY:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
case eChatMessageType::PROPERTY_MODERATION_CHANGED:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT:
case eChatMessageType::MAIL:
case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST:
case eChatMessageType::REPUTATION_UPDATE:
case eChatMessageType::SEND_CANNED_TEXT:
case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST:
case eChatMessageType::CSR_REQUEST:
case eChatMessageType::CSR_REPLY:
case eChatMessageType::GM_KICK:
case eChatMessageType::WORLD_ROUTE_PACKET:
case eChatMessageType::GET_ZONE_POPULATIONS:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
case eChatMessageType::MATCH_REQUEST:
case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCC_REQUEST:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::ACHIEVEMENT_NOTIFY:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::PLAYER_READY:
case eChatMessageType::GET_DONATION_TOTAL:
case eChatMessageType::UPDATE_DONATION:
case eChatMessageType::PRG_CSR_COMMAND:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
case MessageType::Chat::WORLD_PARCEL_RESPONSE:
case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
case MessageType::Chat::GUILD_CREATE:
case MessageType::Chat::GUILD_INVITE:
case MessageType::Chat::GUILD_INVITE_RESPONSE:
case MessageType::Chat::GUILD_LEAVE:
case MessageType::Chat::GUILD_KICK:
case MessageType::Chat::GUILD_GET_STATUS:
case MessageType::Chat::GUILD_GET_ALL:
case MessageType::Chat::BLUEPRINT_MODERATED:
case MessageType::Chat::BLUEPRINT_MODEL_READY:
case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
case MessageType::Chat::MAIL:
case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
case MessageType::Chat::REPUTATION_UPDATE:
case MessageType::Chat::SEND_CANNED_TEXT:
case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
case MessageType::Chat::CSR_REQUEST:
case MessageType::Chat::CSR_REPLY:
case MessageType::Chat::GM_KICK:
case MessageType::Chat::WORLD_ROUTE_PACKET:
case MessageType::Chat::GET_ZONE_POPULATIONS:
case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
case MessageType::Chat::MATCH_REQUEST:
case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case MessageType::Chat::UGCC_REQUEST:
case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case MessageType::Chat::ACHIEVEMENT_NOTIFY:
case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case MessageType::Chat::PLAYER_READY:
case MessageType::Chat::GET_DONATION_TOTAL:
case MessageType::Chat::UPDATE_DONATION:
case MessageType::Chat::PRG_CSR_COMMAND:
case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break;
default:

View File

@@ -11,7 +11,7 @@
#include "eConnectionType.h"
#include "ChatPackets.h"
#include "dConfig.h"
#include "eChatMessageType.h"
#include "MessageType/Chat.h"
void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends =
@@ -36,16 +36,19 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.playerID = playerId;
uint32_t len;
inStream.Read<uint32_t>(len);
if (!inStream.Read<uint32_t>(len)) return;
for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character);
data.playerName += character;
if (len > 33) {
LOG("Received a really long player name, probably a fake packet %i.", len);
return;
}
inStream.Read(data.zoneID);
inStream.Read(data.muteExpire);
inStream.Read(data.gmLevel);
data.playerName.resize(len);
inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
if (!inStream.Read(data.zoneID)) return;
if (!inStream.Read(data.muteExpire)) return;
if (!inStream.Read(data.gmLevel)) return;
data.sysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
@@ -122,6 +125,11 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
size_t membersSize = 0;
inStream.Read(membersSize);
if (membersSize >= 4) {
LOG("Tried to create a team with more than 4 players");
return;
}
std::vector<LWOOBJID> members;
members.reserve(membersSize);
@@ -140,14 +148,13 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
if (team != nullptr) {
team->zoneId = zoneId;
UpdateTeamsOnWorld(team, false);
}
UpdateTeamsOnWorld(team, false);
}
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
bitStream.Write(player);
bitStream.Write(time);
@@ -354,7 +361,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::TEAM_GET_STATUS);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);

View File

@@ -9,73 +9,54 @@
* AMF3 Deserializer written by EmosewaMC
*/
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream& inStream) {
AMFBaseValue* returnValue = nullptr;
std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream) {
// Read in the value type from the bitStream
eAmf marker;
inStream.Read(marker);
// Based on the typing, create the value associated with that and return the base value class
switch (marker) {
case eAmf::Undefined: {
returnValue = new AMFBaseValue();
break;
}
case eAmf::Null: {
returnValue = new AMFNullValue();
break;
}
case eAmf::False: {
returnValue = new AMFBoolValue(false);
break;
}
case eAmf::True: {
returnValue = new AMFBoolValue(true);
break;
}
case eAmf::Integer: {
returnValue = ReadAmfInteger(inStream);
break;
}
case eAmf::Double: {
returnValue = ReadAmfDouble(inStream);
break;
}
case eAmf::String: {
returnValue = ReadAmfString(inStream);
break;
}
case eAmf::Array: {
returnValue = ReadAmfArray(inStream);
break;
}
case eAmf::Undefined:
return std::make_unique<AMFBaseValue>();
case eAmf::Null:
return std::make_unique<AMFNullValue>();
case eAmf::False:
return std::make_unique<AMFBoolValue>(false);
case eAmf::True:
return std::make_unique<AMFBoolValue>(true);
case eAmf::Integer:
return ReadAmfInteger(inStream);
case eAmf::Double:
return ReadAmfDouble(inStream);
case eAmf::String:
return ReadAmfString(inStream);
case eAmf::Array:
return ReadAmfArray(inStream);
// These values are unimplemented in the live client and will remain unimplemented
// unless someone modifies the client to allow serializing of these values.
case eAmf::XMLDoc:
[[fallthrough]];
case eAmf::Date:
[[fallthrough]];
case eAmf::Object:
[[fallthrough]];
case eAmf::XML:
[[fallthrough]];
case eAmf::ByteArray:
[[fallthrough]];
case eAmf::VectorInt:
[[fallthrough]];
case eAmf::VectorUInt:
[[fallthrough]];
case eAmf::VectorDouble:
[[fallthrough]];
case eAmf::VectorObject:
case eAmf::Dictionary: {
[[fallthrough]];
case eAmf::Dictionary:
throw marker;
break;
}
default:
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
break;
}
return returnValue;
}
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
@@ -118,14 +99,14 @@ const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
}
}
AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
std::unique_ptr<AMFDoubleValue> AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
double value;
inStream.Read<double>(value);
return new AMFDoubleValue(value);
return std::make_unique<AMFDoubleValue>(value);
}
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
auto arrayValue = new AMFArrayValue();
std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
auto arrayValue = std::make_unique<AMFArrayValue>();
// Read size of dense array
const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
@@ -143,10 +124,10 @@ AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
return arrayValue;
}
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
return new AMFStringValue(ReadString(inStream));
std::unique_ptr<AMFStringValue> AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
return std::make_unique<AMFStringValue>(ReadString(inStream));
}
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
return new AMFIntValue(ReadU29(inStream));
std::unique_ptr<AMFIntValue> AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
return std::make_unique<AMFIntValue>(ReadU29(inStream)); // NOTE: NARROWING CONVERSION FROM UINT TO INT. IS THIS INTENDED?
}

View File

@@ -1,12 +1,12 @@
#pragma once
#include "Amf3.h"
#include "BitStream.h"
#include <memory>
#include <vector>
#include <string>
class AMFBaseValue;
class AMFDeserialize {
public:
/**
@@ -15,7 +15,7 @@ public:
* @param inStream inStream to read value from.
* @return Returns an AMFValue with all the information from the bitStream in it.
*/
AMFBaseValue* Read(RakNet::BitStream& inStream);
std::unique_ptr<AMFBaseValue> Read(RakNet::BitStream& inStream);
private:
/**
* @brief Private method to read a U29 integer from a bitstream
@@ -39,7 +39,7 @@ private:
* @param inStream bitStream to read data from
* @return Double value represented as an AMFValue
*/
AMFBaseValue* ReadAmfDouble(RakNet::BitStream& inStream);
static std::unique_ptr<AMFDoubleValue> ReadAmfDouble(RakNet::BitStream& inStream);
/**
* @brief Read an AMFArray from a bitStream
@@ -47,7 +47,7 @@ private:
* @param inStream bitStream to read data from
* @return Array value represented as an AMFValue
*/
AMFBaseValue* ReadAmfArray(RakNet::BitStream& inStream);
std::unique_ptr<AMFArrayValue> ReadAmfArray(RakNet::BitStream& inStream);
/**
* @brief Read an AMFString from a bitStream
@@ -55,7 +55,7 @@ private:
* @param inStream bitStream to read data from
* @return String value represented as an AMFValue
*/
AMFBaseValue* ReadAmfString(RakNet::BitStream& inStream);
std::unique_ptr<AMFStringValue> ReadAmfString(RakNet::BitStream& inStream);
/**
* @brief Read an AMFInteger from a bitStream
@@ -63,7 +63,7 @@ private:
* @param inStream bitStream to read data from
* @return Integer value represented as an AMFValue
*/
AMFBaseValue* ReadAmfInteger(RakNet::BitStream& inStream);
static std::unique_ptr<AMFIntValue> ReadAmfInteger(RakNet::BitStream& inStream);
/**
* List of strings read so far saved to be read by reference.

View File

@@ -105,27 +105,14 @@ using AMFDoubleValue = AMFValue<double>;
* and are not to be deleted by a caller.
*/
class AMFArrayValue : public AMFBaseValue {
using AMFAssociative = std::unordered_map<std::string, AMFBaseValue*>;
using AMFDense = std::vector<AMFBaseValue*>;
using AMFAssociative =
std::unordered_map<std::string, std::unique_ptr<AMFBaseValue>, GeneralUtils::transparent_string_hash, std::equal_to<>>;
using AMFDense = std::vector<std::unique_ptr<AMFBaseValue>>;
public:
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
~AMFArrayValue() override {
for (const auto* valueToDelete : GetDense()) {
if (valueToDelete) {
delete valueToDelete;
valueToDelete = nullptr;
}
}
for (auto valueToDelete : GetAssociative()) {
if (valueToDelete.second) {
delete valueToDelete.second;
valueToDelete.second = nullptr;
}
}
}
/**
* Returns the Associative portion of the object
*/
@@ -151,30 +138,32 @@ public:
* or nullptr if a key existed and was not the same type
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, const ValueType value) {
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string_view key, const ValueType value) {
const auto element = m_Associative.find(key);
AMFValue<ValueType>* val = nullptr;
bool found = true;
if (element == m_Associative.cend()) {
val = new AMFValue<ValueType>(value);
m_Associative.emplace(key, val);
auto newVal = std::make_unique<AMFValue<ValueType>>(value);
val = newVal.get();
m_Associative.emplace(key, std::move(newVal));
} else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
val = dynamic_cast<AMFValue<ValueType>*>(element->second.get());
found = false;
}
return std::make_pair(val, found);
}
// Associates an array with a string key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string_view key) {
const auto element = m_Associative.find(key);
AMFArrayValue* val = nullptr;
bool found = true;
if (element == m_Associative.cend()) {
val = new AMFArrayValue();
m_Associative.emplace(key, val);
auto newVal = std::make_unique<AMFArrayValue>();
val = newVal.get();
m_Associative.emplace(key, std::move(newVal));
} else {
val = dynamic_cast<AMFArrayValue*>(element->second);
val = dynamic_cast<AMFArrayValue*>(element->second.get());
found = false;
}
return std::make_pair(val, found);
@@ -182,15 +171,13 @@ public:
// Associates an array with an integer key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
AMFArrayValue* val = nullptr;
bool inserted = false;
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
val = new AMFArrayValue();
m_Dense.at(index) = val;
m_Dense.at(index) = std::make_unique<AMFArrayValue>();
inserted = true;
}
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index)), inserted);
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()), inserted);
}
/**
@@ -205,15 +192,13 @@ public:
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
AMFValue<ValueType>* val = nullptr;
bool inserted = false;
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
val = new AMFValue<ValueType>(value);
m_Dense.at(index) = val;
m_Dense.at(index) = std::make_unique<AMFValue<ValueType>>(value);
inserted = true;
}
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index)), inserted);
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index).get()), inserted);
}
/**
@@ -225,13 +210,12 @@ public:
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const std::string& key, AMFBaseValue* const value) {
void Insert(const std::string_view key, std::unique_ptr<AMFBaseValue> value) {
const auto element = m_Associative.find(key);
if (element != m_Associative.cend() && element->second) {
delete element->second;
element->second = value;
element->second = std::move(value);
} else {
m_Associative.emplace(key, value);
m_Associative.emplace(key, std::move(value));
}
}
@@ -244,14 +228,11 @@ public:
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const size_t index, AMFBaseValue* const value) {
if (index < m_Dense.size()) {
const AMFDense::const_iterator itr = m_Dense.cbegin() + index;
if (*itr) delete m_Dense.at(index);
} else {
void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
}
m_Dense.at(index) = value;
m_Dense.at(index) = std::move(value);
}
/**
@@ -279,8 +260,7 @@ public:
void Remove(const std::string& key, const bool deleteValue = true) {
const AMFAssociative::const_iterator it = m_Associative.find(key);
if (it != m_Associative.cend()) {
if (deleteValue) delete it->second;
m_Associative.erase(it);
if (deleteValue) m_Associative.erase(it);
}
}
@@ -290,7 +270,6 @@ public:
void Remove(const size_t index) {
if (!m_Dense.empty() && index < m_Dense.size()) {
const auto itr = m_Dense.cbegin() + index;
if (*itr) delete (*itr);
m_Dense.erase(itr);
}
}
@@ -299,16 +278,16 @@ public:
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
}
[[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const {
[[nodiscard]] AMFArrayValue* GetArray(const std::string_view key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second) : nullptr;
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second.get()) : nullptr;
}
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index)) : nullptr;
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index).get()) : nullptr;
}
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) {
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string_view key) {
return static_cast<AMFArrayValue*>(Insert(key).first);
}
@@ -330,17 +309,17 @@ public:
* @return The AMFValue
*/
template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const {
[[nodiscard]] AMFValue<AmfType>* Get(const std::string_view key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ?
dynamic_cast<AMFValue<AmfType>*>(it->second) :
dynamic_cast<AMFValue<AmfType>*>(it->second.get()) :
nullptr;
}
// Get from the array but dont cast it
[[nodiscard]] AMFBaseValue* Get(const std::string& key) const {
[[nodiscard]] AMFBaseValue* Get(const std::string_view key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ? it->second : nullptr;
return it != m_Associative.cend() ? it->second.get() : nullptr;
}
/**
@@ -355,13 +334,13 @@ public:
template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
return index < m_Dense.size() ?
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index)) :
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index).get()) :
nullptr;
}
// Get from the dense but dont cast it
[[nodiscard]] AMFBaseValue* Get(const size_t index) const {
return index < m_Dense.size() ? m_Dense.at(index) : nullptr;
return index < m_Dense.size() ? m_Dense.at(index).get() : nullptr;
}
private:

View File

@@ -123,7 +123,7 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
LOG("Updated model %i to sd0", model.id);
updatedModels++;
} catch (sql::SQLException exception) {
} catch (std::exception& exception) {
LOG("Failed to update model %i. This model should be inspected manually to see why."
"The database error is %s", model.id, exception.what());
}

View File

@@ -201,7 +201,7 @@ void OnTerminate() {
}
void MakeBacktrace() {
struct sigaction sigact;
struct sigaction sigact{};
sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO;

View File

@@ -129,6 +129,29 @@ namespace GeneralUtils {
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
/**
* Transparent string hasher - used to allow string_view key lookups for maps storing std::string keys
* https://www.reddit.com/r/cpp_questions/comments/12xw3sn/find_stdstring_view_in_unordered_map_with/jhki225/
* https://godbolt.org/z/789xv8Eeq
*/
template <typename... Bases>
struct overload : Bases... {
using is_transparent = void;
using Bases::operator() ... ;
};
struct char_pointer_hash {
auto operator()(const char* const ptr) const noexcept {
return std::hash<std::string_view>{}(ptr);
}
};
using transparent_string_hash = overload<
std::hash<std::string>,
std::hash<std::string_view>,
char_pointer_hash
>;
// Concept constraining to enum types
template <typename T>
concept Enum = std::is_enum_v<T>;

92
dCommon/Implementation.h Normal file
View File

@@ -0,0 +1,92 @@
#ifndef __IMPLEMENTATION_H__
#define __IMPLEMENTATION_H__
#include <functional>
#include <optional>
/**
* @brief A way to defer the implementation of an action.
*
* @tparam R The result of the action.
* @tparam T The types of the arguments that the implementation requires.
*/
template <typename R, typename... T>
class Implementation {
public:
typedef std::function<std::optional<R>(T...)> ImplementationFunction;
/**
* @brief Sets the implementation of the action.
*
* @param implementation The implementation of the action.
*/
void SetImplementation(const ImplementationFunction& implementation) {
this->implementation = implementation;
}
/**
* @brief Clears the implementation of the action.
*/
void ClearImplementation() {
implementation.reset();
}
/**
* @brief Checks if the implementation is set.
*
* @return true If the implementation is set.
* @return false If the implementation is not set.
*/
bool IsSet() const {
return implementation.has_value();
}
/**
* @brief Executes the implementation if it is set.
*
* @param args The arguments to pass to the implementation.
* @return std::optional<R> The optional result of the implementation. If the result is not set, it indicates that the default action should be taken.
*/
std::optional<R> Execute(T... args) const {
return IsSet() ? implementation.value()(args...) : std::nullopt;
}
/**
* @brief Exectues the implementation if it is set, otherwise returns a default value.
*
* @param args The arguments to pass to the implementation.
* @param defaultValue The default value to return if the implementation is not set or should not be deferred.
*/
R ExecuteWithDefault(T... args, const R& defaultValue) const {
return Execute(args...).value_or(defaultValue);
}
/**
* = operator overload.
*/
Implementation& operator=(const Implementation& other) {
implementation = other.implementation;
return *this;
}
/**
* = operator overload.
*/
Implementation& operator=(const ImplementationFunction& implementation) {
this->implementation = implementation;
return *this;
}
/**
* () operator overload.
*/
std::optional<R> operator()(T... args) {
return !IsSet() ? std::nullopt : implementation(args...);
}
private:
std::optional<ImplementationFunction> implementation;
};
#endif //!__IMPLEMENTATION_H__

73
dCommon/Observable.h Normal file
View File

@@ -0,0 +1,73 @@
#ifndef __OBSERVABLE_H__
#define __OBSERVABLE_H__
#include <vector>
#include <functional>
/**
* @brief An event which can be observed by multiple observers.
*
* @tparam T The types of the arguments to be passed to the observers.
*/
template <typename... T>
class Observable {
public:
typedef std::function<void(T...)> Observer;
/**
* @brief Adds an observer to the event.
*
* @param observer The observer to add.
*/
void AddObserver(const Observer& observer) {
observers.push_back(observer);
}
/**
* @brief Removes an observer from the event.
*
* @param observer The observer to remove.
*/
void RemoveObserver(const Observer& observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
/**
* @brief Notifies all observers of the event.
*
* @param args The arguments to pass to the observers.
*/
void Notify(T... args) {
for (const auto& observer : observers) {
observer(args...);
}
}
/**
* += operator overload.
*/
Observable& operator+=(const Observer& observer) {
AddObserver(observer);
return *this;
}
/**
* -= operator overload.
*/
Observable& operator-=(const Observer& observer) {
RemoveObserver(observer);
return *this;
}
/**
* () operator overload.
*/
void operator()(T... args) {
Notify(args...);
}
private:
std::vector<Observer> observers;
};
#endif //!__OBSERVABLE_H__

View File

@@ -1,6 +1,7 @@
#include "dConfig.h"
#include <sstream>
#include <algorithm>
#include "BinaryPathFinder.h"
#include "GeneralUtils.h"

View File

@@ -0,0 +1,13 @@
#pragma once
#include <cstdint>
namespace MessageType {
enum class Auth : uint32_t {
LOGIN_REQUEST = 0,
LOGOUT_REQUEST,
CREATE_NEW_ACCOUNT_REQUEST,
LEGOINTERFACE_AUTH_RESPONSE,
SESSIONKEY_RECEIVED_CONFIRM,
RUNTIME_CONFIG
};
}

View File

@@ -0,0 +1,78 @@
#pragma once
#include <cstdint>
namespace MessageType {
//! The Internal Chat Packet Identifiers
enum class Chat : uint32_t {
LOGIN_SESSION_NOTIFY = 0,
GENERAL_CHAT_MESSAGE,
PRIVATE_CHAT_MESSAGE,
USER_CHANNEL_CHAT_MESSAGE,
WORLD_DISCONNECT_REQUEST,
WORLD_PROXIMITY_RESPONSE,
WORLD_PARCEL_RESPONSE,
ADD_FRIEND_REQUEST,
ADD_FRIEND_RESPONSE,
REMOVE_FRIEND,
GET_FRIENDS_LIST,
ADD_IGNORE,
REMOVE_IGNORE,
GET_IGNORE_LIST,
TEAM_MISSED_INVITE_CHECK,
TEAM_INVITE,
TEAM_INVITE_RESPONSE,
TEAM_KICK,
TEAM_LEAVE,
TEAM_SET_LOOT,
TEAM_SET_LEADER,
TEAM_GET_STATUS,
GUILD_CREATE,
GUILD_INVITE,
GUILD_INVITE_RESPONSE,
GUILD_LEAVE,
GUILD_KICK,
GUILD_GET_STATUS,
GUILD_GET_ALL,
SHOW_ALL,
BLUEPRINT_MODERATED,
BLUEPRINT_MODEL_READY,
PROPERTY_READY_FOR_APPROVAL,
PROPERTY_MODERATION_CHANGED,
PROPERTY_BUILDMODE_CHANGED,
PROPERTY_BUILDMODE_CHANGED_REPORT,
MAIL,
WORLD_INSTANCE_LOCATION_REQUEST,
REPUTATION_UPDATE,
SEND_CANNED_TEXT,
GMLEVEL_UPDATE,
CHARACTER_NAME_CHANGE_REQUEST,
CSR_REQUEST,
CSR_REPLY,
GM_KICK,
GM_ANNOUNCE,
GM_MUTE,
ACTIVITY_UPDATE,
WORLD_ROUTE_PACKET,
GET_ZONE_POPULATIONS,
REQUEST_MINIMUM_CHAT_MODE,
REQUEST_MINIMUM_CHAT_MODE_PRIVATE,
MATCH_REQUEST,
UGCMANIFEST_REPORT_MISSING_FILE,
UGCMANIFEST_REPORT_DONE_FILE,
UGCMANIFEST_REPORT_DONE_BLUEPRINT,
UGCC_REQUEST,
WHO,
WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE,
ACHIEVEMENT_NOTIFY,
GM_CLOSE_PRIVATE_CHAT_WINDOW,
UNEXPECTED_DISCONNECT,
PLAYER_READY,
GET_DONATION_TOTAL,
UPDATE_DONATION,
PRG_CSR_COMMAND,
HEARTBEAT_REQUEST_FROM_WORLD,
UPDATE_FREE_TRIAL_STATUS,
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
CREATE_TEAM,
};
}

View File

@@ -0,0 +1,74 @@
#pragma once
#include <cstdint>
namespace MessageType {
enum class Client : uint32_t {
LOGIN_RESPONSE = 0,
LOGOUT_RESPONSE,
LOAD_STATIC_ZONE,
CREATE_OBJECT,
CREATE_CHARACTER,
CREATE_CHARACTER_EXTENDED,
CHARACTER_LIST_RESPONSE,
CHARACTER_CREATE_RESPONSE,
CHARACTER_RENAME_RESPONSE,
CHAT_CONNECT_RESPONSE,
AUTH_ACCOUNT_CREATE_RESPONSE,
DELETE_CHARACTER_RESPONSE,
GAME_MSG,
CONNECT_CHAT,
TRANSFER_TO_WORLD,
IMPENDING_RELOAD_NOTIFY,
MAKE_GM_RESPONSE,
HTTP_MONITOR_INFO_RESPONSE,
SLASH_PUSH_MAP_RESPONSE,
SLASH_PULL_MAP_RESPONSE,
SLASH_LOCK_MAP_RESPONSE,
BLUEPRINT_SAVE_RESPONSE,
BLUEPRINT_LUP_SAVE_RESPONSE,
BLUEPRINT_LOAD_RESPONSE_ITEMID,
BLUEPRINT_GET_ALL_DATA_RESPONSE,
MODEL_INSTANTIATE_RESPONSE,
DEBUG_OUTPUT,
ADD_FRIEND_REQUEST,
ADD_FRIEND_RESPONSE,
REMOVE_FRIEND_RESPONSE,
GET_FRIENDS_LIST_RESPONSE,
UPDATE_FRIEND_NOTIFY,
ADD_IGNORE_RESPONSE,
REMOVE_IGNORE_RESPONSE,
GET_IGNORE_LIST_RESPONSE,
TEAM_INVITE,
TEAM_INVITE_INITIAL_RESPONSE,
GUILD_CREATE_RESPONSE,
GUILD_GET_STATUS_RESPONSE,
GUILD_INVITE,
GUILD_INVITE_INITIAL_RESPONSE,
GUILD_INVITE_FINAL_RESPONSE,
GUILD_INVITE_CONFIRM,
GUILD_ADD_PLAYER,
GUILD_REMOVE_PLAYER,
GUILD_LOGIN_LOGOUT,
GUILD_RANK_CHANGE,
GUILD_DATA,
GUILD_STATUS,
MAIL,
DB_PROXY_RESULT,
SHOW_ALL_RESPONSE,
WHO_RESPONSE,
SEND_CANNED_TEXT,
UPDATE_CHARACTER_NAME,
SET_NETWORK_SIMULATOR,
INVALID_CHAT_MESSAGE,
MINIMUM_CHAT_MODE_RESPONSE,
MINIMUM_CHAT_MODE_RESPONSE_PRIVATE,
CHAT_MODERATION_STRING,
UGC_MANIFEST_RESPONSE,
IN_LOGIN_QUEUE,
SERVER_STATES,
GM_CLOSE_TARGET_CHAT_WINDOW,
GENERAL_TEXT_FOR_LOCALIZATION,
UPDATE_FREE_TRIAL_STATUS,
UGC_DOWNLOAD_FAILED = 120
};
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
#pragma once
#include <cstdint>
namespace MessageType {
enum class Master : uint32_t {
REQUEST_PERSISTENT_ID = 1,
REQUEST_PERSISTENT_ID_RESPONSE,
REQUEST_ZONE_TRANSFER,
REQUEST_ZONE_TRANSFER_RESPONSE,
SERVER_INFO,
REQUEST_SESSION_KEY,
SET_SESSION_KEY,
SESSION_KEY_RESPONSE,
PLAYER_ADDED,
PLAYER_REMOVED,
CREATE_PRIVATE_ZONE,
REQUEST_PRIVATE_ZONE,
WORLD_READY,
PREP_ZONE,
SHUTDOWN,
SHUTDOWN_RESPONSE,
SHUTDOWN_IMMEDIATE,
SHUTDOWN_UNIVERSE,
AFFIRM_TRANSFER_REQUEST,
AFFIRM_TRANSFER_RESPONSE,
NEW_SESSION_ALERT
};
}

View File

@@ -0,0 +1,11 @@
#pragma once
#include <cstdint>
namespace MessageType {
//! The Internal Server Packet Identifiers
enum class Server : uint32_t {
VERSION_CONFIRM = 0,
DISCONNECT_NOTIFY,
GENERAL_NOTIFY
};
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <cstdint>
#include "magic_enum.hpp"
namespace MessageType {
enum class World : uint32_t {
VALIDATION = 1, // Session info
CHARACTER_LIST_REQUEST,
CHARACTER_CREATE_REQUEST,
LOGIN_REQUEST, // Character selected
GAME_MSG,
CHARACTER_DELETE_REQUEST,
CHARACTER_RENAME_REQUEST,
HAPPY_FLOWER_MODE_NOTIFY,
SLASH_RELOAD_MAP, // Reload map cmp
SLASH_PUSH_MAP_REQUEST, // Push map req cmd
SLASH_PUSH_MAP, // Push map cmd
SLASH_PULL_MAP, // Pull map cmd
LOCK_MAP_REQUEST,
GENERAL_CHAT_MESSAGE, // General chat message
HTTP_MONITOR_INFO_REQUEST,
SLASH_DEBUG_SCRIPTS, // Debug scripts cmd
MODELS_CLEAR,
EXHIBIT_INSERT_MODEL,
LEVEL_LOAD_COMPLETE, // Character data request
TMP_GUILD_CREATE,
ROUTE_PACKET, // Social?
POSITION_UPDATE,
MAIL,
WORD_CHECK, // AllowList word check
STRING_CHECK, // AllowList string check
GET_PLAYERS_IN_ZONE,
REQUEST_UGC_MANIFEST_INFO,
BLUEPRINT_GET_ALL_DATA_REQUEST,
CANCEL_MAP_QUEUE,
HANDLE_FUNNESS,
FAKE_PRG_CSR_MESSAGE,
REQUEST_FREE_TRIAL_REFRESH,
GM_SET_FREE_TRIAL_STATUS,
UI_HELP_TOP_5 = 91
};
}
template <>
struct magic_enum::customize::enum_range<MessageType::World> {
static constexpr int min = 0;
static constexpr int max = 91;
};

View File

@@ -8,7 +8,7 @@
#include <set>
#include "BitStream.h"
#include "eConnectionType.h"
#include "eClientMessageType.h"
#include "MessageType/Client.h"
#include "BitStreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings
@@ -33,7 +33,7 @@ constexpr uint32_t lowFrameDelta = FRAMES_TO_MS(lowFramerate);
#define CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false);
#define CINSTREAM_SKIP_HEADER CINSTREAM if (inStream.GetNumberOfUnreadBits() >= BYTES_TO_BITS(HEADER_SIZE)) inStream.IgnoreBytes(HEADER_SIZE); else inStream.IgnoreBits(inStream.GetNumberOfUnreadBits());
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
#define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
#define SEND_PACKET Game::server->Send(bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);

View File

@@ -1,15 +0,0 @@
#ifndef __EAUTHMESSAGETYPE__H__
#define __EAUTHMESSAGETYPE__H__
#include <cstdint>
enum class eAuthMessageType : uint32_t {
LOGIN_REQUEST = 0,
LOGOUT_REQUEST,
CREATE_NEW_ACCOUNT_REQUEST,
LEGOINTERFACE_AUTH_RESPONSE,
SESSIONKEY_RECEIVED_CONFIRM,
RUNTIME_CONFIG
};
#endif //!__EAUTHMESSAGETYPE__H__

View File

@@ -15,7 +15,8 @@ enum class eCharacterVersion : uint32_t {
// Fixes vault size value
VAULT_SIZE,
// Fixes speed base value in level component
UP_TO_DATE, // will become SPEED_BASE
SPEED_BASE,
UP_TO_DATE, // will become NJ_JAYMISSIONS
};
#endif //!__ECHARACTERVERSION__H__

View File

@@ -1,80 +0,0 @@
#ifndef __ECHATMESSAGETYPE__H__
#define __ECHATMESSAGETYPE__H__
#include <cstdint>
//! The Internal Chat Packet Identifiers
enum class eChatMessageType :uint32_t {
LOGIN_SESSION_NOTIFY = 0,
GENERAL_CHAT_MESSAGE,
PRIVATE_CHAT_MESSAGE,
USER_CHANNEL_CHAT_MESSAGE,
WORLD_DISCONNECT_REQUEST,
WORLD_PROXIMITY_RESPONSE,
WORLD_PARCEL_RESPONSE,
ADD_FRIEND_REQUEST,
ADD_FRIEND_RESPONSE,
REMOVE_FRIEND,
GET_FRIENDS_LIST,
ADD_IGNORE,
REMOVE_IGNORE,
GET_IGNORE_LIST,
TEAM_MISSED_INVITE_CHECK,
TEAM_INVITE,
TEAM_INVITE_RESPONSE,
TEAM_KICK,
TEAM_LEAVE,
TEAM_SET_LOOT,
TEAM_SET_LEADER,
TEAM_GET_STATUS,
GUILD_CREATE,
GUILD_INVITE,
GUILD_INVITE_RESPONSE,
GUILD_LEAVE,
GUILD_KICK,
GUILD_GET_STATUS,
GUILD_GET_ALL,
SHOW_ALL,
BLUEPRINT_MODERATED,
BLUEPRINT_MODEL_READY,
PROPERTY_READY_FOR_APPROVAL,
PROPERTY_MODERATION_CHANGED,
PROPERTY_BUILDMODE_CHANGED,
PROPERTY_BUILDMODE_CHANGED_REPORT,
MAIL,
WORLD_INSTANCE_LOCATION_REQUEST,
REPUTATION_UPDATE,
SEND_CANNED_TEXT,
GMLEVEL_UPDATE,
CHARACTER_NAME_CHANGE_REQUEST,
CSR_REQUEST,
CSR_REPLY,
GM_KICK,
GM_ANNOUNCE,
GM_MUTE,
ACTIVITY_UPDATE,
WORLD_ROUTE_PACKET,
GET_ZONE_POPULATIONS,
REQUEST_MINIMUM_CHAT_MODE,
REQUEST_MINIMUM_CHAT_MODE_PRIVATE,
MATCH_REQUEST,
UGCMANIFEST_REPORT_MISSING_FILE,
UGCMANIFEST_REPORT_DONE_FILE,
UGCMANIFEST_REPORT_DONE_BLUEPRINT,
UGCC_REQUEST,
WHO,
WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE,
ACHIEVEMENT_NOTIFY,
GM_CLOSE_PRIVATE_CHAT_WINDOW,
UNEXPECTED_DISCONNECT,
PLAYER_READY,
GET_DONATION_TOTAL,
UPDATE_DONATION,
PRG_CSR_COMMAND,
HEARTBEAT_REQUEST_FROM_WORLD,
UPDATE_FREE_TRIAL_STATUS,
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
CREATE_TEAM,
};
#endif //!__ECHATMESSAGETYPE__H__

View File

@@ -1,76 +0,0 @@
#ifndef __ECLIENTMESSAGETYPE__H__
#define __ECLIENTMESSAGETYPE__H__
#include <cstdint>
enum class eClientMessageType : uint32_t {
LOGIN_RESPONSE = 0,
LOGOUT_RESPONSE,
LOAD_STATIC_ZONE,
CREATE_OBJECT,
CREATE_CHARACTER,
CREATE_CHARACTER_EXTENDED,
CHARACTER_LIST_RESPONSE,
CHARACTER_CREATE_RESPONSE,
CHARACTER_RENAME_RESPONSE,
CHAT_CONNECT_RESPONSE,
AUTH_ACCOUNT_CREATE_RESPONSE,
DELETE_CHARACTER_RESPONSE,
GAME_MSG,
CONNECT_CHAT,
TRANSFER_TO_WORLD,
IMPENDING_RELOAD_NOTIFY,
MAKE_GM_RESPONSE,
HTTP_MONITOR_INFO_RESPONSE,
SLASH_PUSH_MAP_RESPONSE,
SLASH_PULL_MAP_RESPONSE,
SLASH_LOCK_MAP_RESPONSE,
BLUEPRINT_SAVE_RESPONSE,
BLUEPRINT_LUP_SAVE_RESPONSE,
BLUEPRINT_LOAD_RESPONSE_ITEMID,
BLUEPRINT_GET_ALL_DATA_RESPONSE,
MODEL_INSTANTIATE_RESPONSE,
DEBUG_OUTPUT,
ADD_FRIEND_REQUEST,
ADD_FRIEND_RESPONSE,
REMOVE_FRIEND_RESPONSE,
GET_FRIENDS_LIST_RESPONSE,
UPDATE_FRIEND_NOTIFY,
ADD_IGNORE_RESPONSE,
REMOVE_IGNORE_RESPONSE,
GET_IGNORE_LIST_RESPONSE,
TEAM_INVITE,
TEAM_INVITE_INITIAL_RESPONSE,
GUILD_CREATE_RESPONSE,
GUILD_GET_STATUS_RESPONSE,
GUILD_INVITE,
GUILD_INVITE_INITIAL_RESPONSE,
GUILD_INVITE_FINAL_RESPONSE,
GUILD_INVITE_CONFIRM,
GUILD_ADD_PLAYER,
GUILD_REMOVE_PLAYER,
GUILD_LOGIN_LOGOUT,
GUILD_RANK_CHANGE,
GUILD_DATA,
GUILD_STATUS,
MAIL,
DB_PROXY_RESULT,
SHOW_ALL_RESPONSE,
WHO_RESPONSE,
SEND_CANNED_TEXT,
UPDATE_CHARACTER_NAME,
SET_NETWORK_SIMULATOR,
INVALID_CHAT_MESSAGE,
MINIMUM_CHAT_MODE_RESPONSE,
MINIMUM_CHAT_MODE_RESPONSE_PRIVATE,
CHAT_MODERATION_STRING,
UGC_MANIFEST_RESPONSE,
IN_LOGIN_QUEUE,
SERVER_STATES,
GM_CLOSE_TARGET_CHAT_WINDOW,
GENERAL_TEXT_FOR_LOCALIZATION,
UPDATE_FREE_TRIAL_STATUS,
UGC_DOWNLOAD_FAILED = 120
};
#endif //!__ECLIENTMESSAGETYPE__H__

File diff suppressed because it is too large Load Diff

View File

@@ -4,6 +4,9 @@
#define __EINVENTORYTYPE__H__
#include <cstdint>
#include "magic_enum.hpp"
static const uint8_t NUMBER_OF_INVENTORIES = 17;
/**
* Represents the different types of inventories an entity may have
@@ -56,4 +59,10 @@ public:
};
};
template <>
struct magic_enum::customize::enum_range<eInventoryType> {
static constexpr int min = 0;
static constexpr int max = 16;
};
#endif //!__EINVENTORYTYPE__H__

View File

@@ -1,36 +0,0 @@
#ifndef __EMASTERMESSAGETYPE__H__
#define __EMASTERMESSAGETYPE__H__
#include <cstdint>
enum class eMasterMessageType : uint32_t {
REQUEST_PERSISTENT_ID = 1,
REQUEST_PERSISTENT_ID_RESPONSE,
REQUEST_ZONE_TRANSFER,
REQUEST_ZONE_TRANSFER_RESPONSE,
SERVER_INFO,
REQUEST_SESSION_KEY,
SET_SESSION_KEY,
SESSION_KEY_RESPONSE,
PLAYER_ADDED,
PLAYER_REMOVED,
CREATE_PRIVATE_ZONE,
REQUEST_PRIVATE_ZONE,
WORLD_READY,
PREP_ZONE,
SHUTDOWN,
SHUTDOWN_RESPONSE,
SHUTDOWN_IMMEDIATE,
SHUTDOWN_UNIVERSE,
AFFIRM_TRANSFER_REQUEST,
AFFIRM_TRANSFER_RESPONSE,
NEW_SESSION_ALERT
};
#endif //!__EMASTERMESSAGETYPE__H__

View File

@@ -0,0 +1,11 @@
#ifndef EPROPERTYSORTTYPE_H
#define EPROPERTYSORTTYPE_H
enum ePropertySortType : int32_t {
SORT_TYPE_FRIENDS = 0,
SORT_TYPE_REPUTATION = 1,
SORT_TYPE_RECENT = 3,
SORT_TYPE_FEATURED = 5
};
#endif //!EPROPERTYSORTTYPE_H

View File

@@ -0,0 +1,21 @@
#ifndef __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#define __EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__
#include <cstdint>
enum class eReponseMoveItemBetweenInventoryTypeCode : int32_t {
SUCCESS,
FAIL_GENERIC,
FAIL_INV_FULL,
FAIL_ITEM_NOT_FOUND,
FAIL_CANT_MOVE_TO_THAT_INV_TYPE,
FAIL_NOT_NEAR_BANK,
FAIL_CANT_SWAP_ITEMS,
FAIL_SOURCE_TYPE,
FAIL_WRONG_DEST_TYPE,
FAIL_SWAP_DEST_TYPE,
FAIL_CANT_MOVE_THINKING_HAT,
FAIL_DISMOUNT_BEFORE_MOVING
};
#endif //!__EREPONSEMOVEITEMBETWEENINVENTORYTYPECODE__H__

View File

@@ -1,12 +0,0 @@
#ifndef __ESERVERMESSAGETYPE__H__
#define __ESERVERMESSAGETYPE__H__
#include <cstdint>
//! The Internal Server Packet Identifiers
enum class eServerMessageType : uint32_t {
VERSION_CONFIRM = 0,
DISCONNECT_NOTIFY,
GENERAL_NOTIFY
};
#endif //!__ESERVERMESSAGETYPE__H__

View File

@@ -1,51 +0,0 @@
#ifndef __EWORLDMESSAGETYPE__H__
#define __EWORLDMESSAGETYPE__H__
#include <cstdint>
#include "magic_enum.hpp"
enum class eWorldMessageType : uint32_t {
VALIDATION = 1, // Session info
CHARACTER_LIST_REQUEST,
CHARACTER_CREATE_REQUEST,
LOGIN_REQUEST, // Character selected
GAME_MSG,
CHARACTER_DELETE_REQUEST,
CHARACTER_RENAME_REQUEST,
HAPPY_FLOWER_MODE_NOTIFY,
SLASH_RELOAD_MAP, // Reload map cmp
SLASH_PUSH_MAP_REQUEST, // Push map req cmd
SLASH_PUSH_MAP, // Push map cmd
SLASH_PULL_MAP, // Pull map cmd
LOCK_MAP_REQUEST,
GENERAL_CHAT_MESSAGE, // General chat message
HTTP_MONITOR_INFO_REQUEST,
SLASH_DEBUG_SCRIPTS, // Debug scripts cmd
MODELS_CLEAR,
EXHIBIT_INSERT_MODEL,
LEVEL_LOAD_COMPLETE, // Character data request
TMP_GUILD_CREATE,
ROUTE_PACKET, // Social?
POSITION_UPDATE,
MAIL,
WORD_CHECK, // AllowList word check
STRING_CHECK, // AllowList string check
GET_PLAYERS_IN_ZONE,
REQUEST_UGC_MANIFEST_INFO,
BLUEPRINT_GET_ALL_DATA_REQUEST,
CANCEL_MAP_QUEUE,
HANDLE_FUNNESS,
FAKE_PRG_CSR_MESSAGE,
REQUEST_FREE_TRIAL_REFRESH,
GM_SET_FREE_TRIAL_STATUS,
UI_HELP_TOP_5 = 91
};
template <>
struct magic_enum::customize::enum_range<eWorldMessageType> {
static constexpr int min = 0;
static constexpr int max = 91;
};
#endif //!__EWORLDMESSAGETYPE__H__

View File

@@ -8,9 +8,15 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}")
endforeach()
add_subdirectory(TestSQL)
foreach(file ${DDATABSE_DATABSES_TEST_SQL_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "TestSQL/${file}")
endforeach()
add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES})
target_include_directories(dDatabaseGame PUBLIC "."
"ITables" PRIVATE "MySQL"
"ITables" PRIVATE "MySQL" "TestSQL"
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
)

View File

@@ -38,3 +38,8 @@ void Database::Destroy(std::string source) {
LOG("Trying to destroy database when it's not connected!");
}
}
void Database::_setDatabase(GameDatabase* const db) {
if (database) delete database;
database = db;
}

View File

@@ -9,4 +9,8 @@ namespace Database {
void Connect();
GameDatabase* Get();
void Destroy(std::string source = "");
// Used for assigning a test database as the handler for database logic.
// Do not use in production code.
void _setDatabase(GameDatabase* const db);
};

View File

@@ -23,6 +23,7 @@
#include "IActivityLog.h"
#include "IIgnoreList.h"
#include "IAccountsRewardCodes.h"
#include "IBehaviors.h"
namespace sql {
class Statement;
@@ -40,7 +41,8 @@ class GameDatabase :
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo,
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList {
public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList,
public IBehaviors {
public:
virtual ~GameDatabase() = default;
// TODO: These should be made private.

View File

@@ -33,6 +33,9 @@ public:
// Add a new account to the database.
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0;
// Update the GameMaster level of an account.
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;
};
#endif //!__IACCOUNTS__H__

View File

@@ -0,0 +1,22 @@
#ifndef IBEHAVIORS_H
#define IBEHAVIORS_H
#include <cstdint>
#include "dCommonVars.h"
class IBehaviors {
public:
struct Info {
int32_t behaviorId{};
uint32_t characterId{};
std::string behaviorInfo;
};
// This Add also takes care of updating if it exists.
virtual void AddBehavior(const Info& info) = 0;
virtual std::string GetBehavior(const int32_t behaviorId) = 0;
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
};
#endif //!IBEHAVIORS_H

View File

@@ -4,6 +4,8 @@
#include <cstdint>
#include <optional>
enum ePropertySortType : int32_t;
class IProperty {
public:
struct Info {
@@ -18,11 +20,33 @@ public:
uint32_t lastUpdatedTime{};
uint32_t claimedTime{};
uint32_t reputation{};
float performanceCost{};
};
struct PropertyLookup {
uint32_t mapId{};
std::string searchString;
ePropertySortType sortChoice{};
uint32_t playerId{};
uint32_t numResults{};
uint32_t startIndex{};
uint32_t playerSort{};
};
struct PropertyEntranceResult {
int32_t totalEntriesMatchingQuery{};
// The entries that match the query. This should only contain up to 12 entries.
std::vector<IProperty::Info> entries;
};
// Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0;
// Get the properties for the given property lookup params.
// This is expected to return a result set of up to 12 properties
// so as not to transfer too much data at once.
virtual std::optional<IProperty::PropertyEntranceResult> GetProperties(const PropertyLookup& params) = 0;
// Update the property moderation info for the given property id.
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;

View File

@@ -1,6 +1,7 @@
#ifndef __IPROPERTIESCONTENTS__H__
#define __IPROPERTIESCONTENTS__H__
#include <array>
#include <cstdint>
#include <string_view>
@@ -16,6 +17,7 @@ public:
LWOOBJID id{};
LOT lot{};
uint32_t ugcId{};
std::array<int32_t, 5> behaviors{};
};
// Inserts a new UGC model into the database.
@@ -32,7 +34,7 @@ public:
virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0;
// Update the model position and rotation for the given property id.
virtual void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 0;
virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) = 0;
// Remove the model for the given property id.
virtual void RemoveModel(const LWOOBJID& modelId) = 0;

View File

@@ -4,6 +4,7 @@
#include "Game.h"
#include "dConfig.h"
#include "Logger.h"
#include "dPlatforms.h"
namespace {
std::string databaseName;
@@ -39,14 +40,13 @@ void MySQLDatabase::Connect() {
properties["autoReconnect"] = "true";
databaseName = Game::config->GetValue("mysql_database").c_str();
// `connect(const Properties& props)` segfaults in windows debug, but
// `connect(const SQLString& host, const SQLString& user, const SQLString& pwd)` doesn't handle pipes/unix sockets correctly
if (properties.find("localSocket") != properties.end() || properties.find("pipe") != properties.end()) {
con = driver->connect(properties);
} else {
#if defined(DARKFLAME_PLATFORM_WIN32) && defined(_DEBUG)
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str());
}
#else
con = driver->connect(properties);
#endif
con->setSchema(databaseName.c_str());
}

View File

@@ -74,7 +74,7 @@ public:
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override;
@@ -108,6 +108,11 @@ public:
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const int32_t behaviorId) override;
void RemoveBehavior(const int32_t characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
private:
// Generic query functions that can be used for any query.

View File

@@ -35,3 +35,7 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
ExecuteInsert("INSERT INTO accounts (name, password, gm_level) VALUES (?, ?, ?);", username, bcryptpassword, static_cast<int32_t>(eGameMasterLevel::OPERATOR));
}
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
}

View File

@@ -0,0 +1,19 @@
#include "IBehaviors.h"
#include "MySQLDatabase.h"
void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) {
ExecuteInsert(
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?",
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
);
}
void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) {
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
}
std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) {
auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
return result->next() ? result->getString("behavior_info").c_str() : "";
}

View File

@@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"Accounts.cpp"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp"
"CharInfo.cpp"
"CharXml.cpp"

View File

@@ -1,8 +1,140 @@
#include "MySQLDatabase.h"
#include "ePropertySortType.h"
std::optional<IProperty::PropertyEntranceResult> MySQLDatabase::GetProperties(const IProperty::PropertyLookup& params) {
std::optional<IProperty::PropertyEntranceResult> result;
std::string query;
std::unique_ptr<sql::ResultSet> properties;
if (params.sortChoice == SORT_TYPE_FEATURED || params.sortChoice == SORT_TYPE_FRIENDS) {
query = R"QUERY(
FROM properties as p
JOIN charinfo as ci
ON ci.prop_clone_id = p.clone_id
where p.zone_id = ?
AND (
p.description LIKE ?
OR p.name LIKE ?
OR ci.name LIKE ?
)
AND p.privacy_option >= ?
AND p.owner_id IN (
SELECT fr.requested_player AS player FROM (
SELECT CASE
WHEN player_id = ? THEN friend_id
WHEN friend_id = ? THEN player_id
END AS requested_player FROM friends
) AS fr
JOIN charinfo AS ci ON ci.id = fr.requested_player
WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?
) ORDER BY ci.name ASC
)QUERY";
const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;";
properties = ExecuteSelect(
completeQuery,
params.mapId,
"%" + params.searchString + "%",
"%" + params.searchString + "%",
"%" + params.searchString + "%",
params.playerSort,
params.playerId,
params.playerId,
params.playerId,
params.numResults,
params.startIndex
);
const auto countQuery = "SELECT COUNT(*) as count" + query + ";";
auto count = ExecuteSelect(
countQuery,
params.mapId,
"%" + params.searchString + "%",
"%" + params.searchString + "%",
"%" + params.searchString + "%",
params.playerSort,
params.playerId,
params.playerId,
params.playerId
);
if (count->next()) {
result->totalEntriesMatchingQuery = count->getUInt("count");
}
} else {
if (params.sortChoice == SORT_TYPE_REPUTATION) {
query = R"QUERY(
FROM properties as p
JOIN charinfo as ci
ON ci.prop_clone_id = p.clone_id
where p.zone_id = ?
AND (
p.description LIKE ?
OR p.name LIKE ?
OR ci.name LIKE ?
)
AND p.privacy_option >= ?
ORDER BY p.reputation DESC, p.last_updated DESC
)QUERY";
} else {
query = R"QUERY(
FROM properties as p
JOIN charinfo as ci
ON ci.prop_clone_id = p.clone_id
where p.zone_id = ?
AND (
p.description LIKE ?
OR p.name LIKE ?
OR ci.name LIKE ?
)
AND p.privacy_option >= ?
ORDER BY p.last_updated DESC
)QUERY";
}
const auto completeQuery = "SELECT p.* " + query + " LIMIT ? OFFSET ?;";
properties = ExecuteSelect(
completeQuery,
params.mapId,
"%" + params.searchString + "%",
"%" + params.searchString + "%",
"%" + params.searchString + "%",
params.playerSort,
params.numResults,
params.startIndex
);
const auto countQuery = "SELECT COUNT(*) as count" + query + ";";
auto count = ExecuteSelect(
countQuery,
params.mapId,
"%" + params.searchString + "%",
"%" + params.searchString + "%",
"%" + params.searchString + "%",
params.playerSort
);
if (count->next()) {
result->totalEntriesMatchingQuery = count->getUInt("count");
}
}
while (properties->next()) {
auto& entry = result->entries.emplace_back();
entry.id = properties->getUInt64("id");
entry.ownerId = properties->getUInt64("owner_id");
entry.cloneId = properties->getUInt64("clone_id");
entry.name = properties->getString("name").c_str();
entry.description = properties->getString("description").c_str();
entry.privacyOption = properties->getInt("privacy_option");
entry.rejectionReason = properties->getString("rejection_reason").c_str();
entry.lastUpdatedTime = properties->getUInt("last_updated");
entry.claimedTime = properties->getUInt("time_claimed");
entry.reputation = properties->getUInt("reputation");
entry.modApproved = properties->getUInt("mod_approved");
entry.performanceCost = properties->getFloat("performance_cost");
}
return result;
}
std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) {
auto propertyEntry = ExecuteSelect(
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved "
"SELECT id, owner_id, clone_id, name, description, privacy_option, rejection_reason, last_updated, time_claimed, reputation, mod_approved, performance_cost "
"FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId);
if (!propertyEntry->next()) {
@@ -21,6 +153,7 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
toReturn.claimedTime = propertyEntry->getUInt("time_claimed");
toReturn.reputation = propertyEntry->getUInt("reputation");
toReturn.modApproved = propertyEntry->getUInt("mod_approved");
toReturn.performanceCost = propertyEntry->getFloat("performance_cost");
return toReturn;
}

View File

@@ -1,7 +1,10 @@
#include "MySQLDatabase.h"
std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId);
auto result = ExecuteSelect(
"SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, "
"behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 "
"FROM properties_contents WHERE property_id = ?;", propertyId);
std::vector<IPropertyContents::Model> toReturn;
toReturn.reserve(result->rowsCount());
@@ -17,6 +20,12 @@ std::vector<IPropertyContents::Model> MySQLDatabase::GetPropertyModels(const LWO
model.rotation.y = result->getFloat("ry");
model.rotation.z = result->getFloat("rz");
model.ugcId = result->getUInt64("ugc_id");
model.behaviors[0] = result->getInt("behavior_1");
model.behaviors[1] = result->getInt("behavior_2");
model.behaviors[2] = result->getInt("behavior_3");
model.behaviors[3] = result->getInt("behavior_4");
model.behaviors[4] = result->getInt("behavior_5");
toReturn.push_back(std::move(model));
}
return toReturn;
@@ -32,21 +41,23 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w,
name, "", // Model description. TODO implement this.
0, // behavior 1. TODO implement this.
0, // behavior 2. TODO implement this.
0, // behavior 3. TODO implement this.
0, // behavior 4. TODO implement this.
0 // behavior 5. TODO implement this.
model.behaviors[0], // behavior 1
model.behaviors[1], // behavior 2
model.behaviors[2], // behavior 3
model.behaviors[3], // behavior 4
model.behaviors[4] // behavior 5
);
} catch (sql::SQLException& e) {
LOG("Error inserting new property model: %s", e.what());
}
}
void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) {
void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
ExecuteUpdate(
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId);
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
"behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;",
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w,
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
}
void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {

View File

@@ -0,0 +1,4 @@
SET(DDATABSE_DATABSES_TEST_SQL_SOURCES
"TestSQLDatabase.cpp"
PARENT_SCOPE
)

View File

@@ -0,0 +1,306 @@
#include "TestSQLDatabase.h"
void TestSQLDatabase::Connect() {
}
void TestSQLDatabase::Destroy(std::string source) {
}
sql::PreparedStatement* TestSQLDatabase::CreatePreppedStmt(const std::string& query) {
return nullptr;
}
void TestSQLDatabase::Commit() {
}
bool TestSQLDatabase::GetAutoCommit() {
return {};
}
void TestSQLDatabase::SetAutoCommit(bool value) {
}
void TestSQLDatabase::ExecuteCustomQuery(const std::string_view query) {
}
std::optional<IServers::MasterInfo> TestSQLDatabase::GetMasterInfo() {
return {};
}
std::vector<std::string> TestSQLDatabase::GetApprovedCharacterNames() {
return {};
}
std::vector<FriendData> TestSQLDatabase::GetFriendsList(uint32_t charID) {
return {};
}
std::optional<IFriends::BestFriendStatus> TestSQLDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
return {};
}
void TestSQLDatabase::SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) {
}
void TestSQLDatabase::AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) {
}
void TestSQLDatabase::RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) {
}
void TestSQLDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) {
}
void TestSQLDatabase::DeleteUgcModelData(const LWOOBJID& modelId) {
}
void TestSQLDatabase::UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) {
}
std::vector<IUgc::Model> TestSQLDatabase::GetAllUgcModels() {
return {};
}
void TestSQLDatabase::CreateMigrationHistoryTable() {
}
bool TestSQLDatabase::IsMigrationRun(const std::string_view str) {
return {};
}
void TestSQLDatabase::InsertMigration(const std::string_view str) {
}
std::optional<ICharInfo::Info> TestSQLDatabase::GetCharacterInfo(const uint32_t charId) {
return {};
}
std::optional<ICharInfo::Info> TestSQLDatabase::GetCharacterInfo(const std::string_view charId) {
return {};
}
std::string TestSQLDatabase::GetCharacterXml(const uint32_t accountId) {
return {};
}
void TestSQLDatabase::UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) {
}
std::optional<IAccounts::Info> TestSQLDatabase::GetAccountInfo(const std::string_view username) {
return {};
}
void TestSQLDatabase::InsertNewCharacter(const ICharInfo::Info info) {
}
void TestSQLDatabase::InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) {
}
std::vector<uint32_t> TestSQLDatabase::GetAccountCharacterIds(uint32_t accountId) {
return {};
}
void TestSQLDatabase::DeleteCharacter(const uint32_t characterId) {
}
void TestSQLDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) {
}
void TestSQLDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) {
}
void TestSQLDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
}
void TestSQLDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) {
}
std::optional<IPetNames::Info> TestSQLDatabase::GetPetNameInfo(const LWOOBJID& petId) {
return {};
}
std::optional<IProperty::Info> TestSQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) {
return {};
}
void TestSQLDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
}
void TestSQLDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
}
void TestSQLDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
}
std::vector<IPropertyContents::Model> TestSQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) {
return {};
}
void TestSQLDatabase::RemoveUnreferencedUgcModels() {
}
void TestSQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) {
}
void TestSQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) {
}
void TestSQLDatabase::RemoveModel(const LWOOBJID& modelId) {
}
void TestSQLDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
}
void TestSQLDatabase::InsertNewBugReport(const IBugReports::Info& info) {
}
void TestSQLDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) {
}
void TestSQLDatabase::InsertNewMail(const IMail::MailInfo& mail) {
}
void TestSQLDatabase::InsertNewUgcModel(std::istringstream& sd0Data, const uint32_t blueprintId, const uint32_t accountId, const uint32_t characterId) {
}
std::vector<IMail::MailInfo> TestSQLDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
return {};
}
std::optional<IMail::MailInfo> TestSQLDatabase::GetMail(const uint64_t mailId) {
return {};
}
uint32_t TestSQLDatabase::GetUnreadMailCount(const uint32_t characterId) {
return {};
}
void TestSQLDatabase::MarkMailRead(const uint64_t mailId) {
}
void TestSQLDatabase::DeleteMail(const uint64_t mailId) {
}
void TestSQLDatabase::ClaimMailItem(const uint64_t mailId) {
}
void TestSQLDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) {
}
void TestSQLDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) {
}
void TestSQLDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) {
}
void TestSQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) {
}
void TestSQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) {
}
void TestSQLDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
}
std::optional<uint32_t> TestSQLDatabase::GetCurrentPersistentId() {
return {};
}
void TestSQLDatabase::InsertDefaultPersistentId() {
}
void TestSQLDatabase::UpdatePersistentId(const uint32_t id) {
}
std::optional<uint32_t> TestSQLDatabase::GetDonationTotal(const uint32_t activityId) {
return {};
}
std::optional<bool> TestSQLDatabase::IsPlaykeyActive(const int32_t playkeyId) {
return {};
}
std::vector<IUgc::Model> TestSQLDatabase::GetUgcModels(const LWOOBJID& propertyId) {
return {};
}
void TestSQLDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
}
void TestSQLDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
}
std::vector<IIgnoreList::Info> TestSQLDatabase::GetIgnoreList(const uint32_t playerId) {
return {};
}
void TestSQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) {
}
std::vector<uint32_t> TestSQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) {
return {};
}
void TestSQLDatabase::AddBehavior(const IBehaviors::Info& info) {
}
std::string TestSQLDatabase::GetBehavior(const int32_t behaviorId) {
return {};
}
void TestSQLDatabase::RemoveBehavior(const int32_t behaviorId) {
}
void TestSQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
}

View File

@@ -0,0 +1,96 @@
#ifndef TESTSQLDATABASE_H
#define TESTSQLDATABASE_H
#include "GameDatabase.h"
class TestSQLDatabase : public GameDatabase {
void Connect() override;
void Destroy(std::string source = "") override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override;
void Commit() override;
bool GetAutoCommit() override;
void SetAutoCommit(bool value) override;
void ExecuteCustomQuery(const std::string_view query) override;
// Overloaded queries
std::optional<IServers::MasterInfo> GetMasterInfo() override;
std::vector<std::string> GetApprovedCharacterNames() override;
std::vector<FriendData> GetFriendsList(uint32_t charID) override;
std::optional<IFriends::BestFriendStatus> GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) override;
void SetBestFriendStatus(const uint32_t playerAccountId, const uint32_t friendAccountId, const uint32_t bestFriendStatus) override;
void AddFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void RemoveFriend(const uint32_t playerAccountId, const uint32_t friendAccountId) override;
void UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) override;
void DeleteUgcModelData(const LWOOBJID& modelId) override;
void UpdateUgcModelData(const LWOOBJID& modelId, std::istringstream& lxfml) override;
std::vector<IUgc::Model> GetAllUgcModels() override;
void CreateMigrationHistoryTable() override;
bool IsMigrationRun(const std::string_view str) override;
void InsertMigration(const std::string_view str) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const uint32_t charId) override;
std::optional<ICharInfo::Info> GetCharacterInfo(const std::string_view charId) override;
std::string GetCharacterXml(const uint32_t accountId) override;
void UpdateCharacterXml(const uint32_t characterId, const std::string_view lxfml) override;
std::optional<IAccounts::Info> GetAccountInfo(const std::string_view username) override;
void InsertNewCharacter(const ICharInfo::Info info) override;
void InsertCharacterXml(const uint32_t accountId, const std::string_view lxfml) override;
std::vector<uint32_t> GetAccountCharacterIds(uint32_t accountId) override;
void DeleteCharacter(const uint32_t characterId) override;
void SetCharacterName(const uint32_t characterId, const std::string_view name) override;
void SetPendingCharacterName(const uint32_t characterId, const std::string_view name) override;
void UpdateLastLoggedInCharacter(const uint32_t characterId) override;
void SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) override;
std::optional<IPetNames::Info> GetPetNameInfo(const LWOOBJID& petId) override;
std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) override;
void UpdatePropertyModerationInfo(const IProperty::Info& info) override;
void UpdatePropertyDetails(const IProperty::Info& info) override;
void InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) override;
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override;
void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array<std::pair<int32_t, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override;
void InsertCheatDetection(const IPlayerCheatDetections::Info& info) override;
void InsertNewMail(const IMail::MailInfo& mail) override;
void InsertNewUgcModel(
std::istringstream& sd0Data,
const uint32_t blueprintId,
const uint32_t accountId,
const uint32_t characterId) override;
std::vector<IMail::MailInfo> GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) override;
std::optional<IMail::MailInfo> GetMail(const uint64_t mailId) override;
uint32_t GetUnreadMailCount(const uint32_t characterId) override;
void MarkMailRead(const uint64_t mailId) override;
void DeleteMail(const uint64_t mailId) override;
void ClaimMailItem(const uint64_t mailId) override;
void InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) override;
void UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) override;
void UpdateAccountBan(const uint32_t accountId, const bool banned) override;
void UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) override;
void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) override;
void SetMasterIp(const std::string_view ip, const uint32_t port) override;
std::optional<uint32_t> GetCurrentPersistentId() override;
void InsertDefaultPersistentId() override;
void UpdatePersistentId(const uint32_t id) override;
std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) override;
std::optional<bool> IsPlaykeyActive(const int32_t playkeyId) override;
std::vector<IUgc::Model> GetUgcModels(const LWOOBJID& propertyId) override;
void AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
void RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) override;
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const int32_t behaviorId) override;
void RemoveBehavior(const int32_t behaviorId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override { return {}; };
};
#endif //!TESTSQLDATABASE_H

View File

@@ -34,7 +34,7 @@ Migration LoadMigration(std::string path) {
void MigrationRunner::RunMigrations() {
Database::Get()->CreateMigrationHistoryTable();
sql::SQLString finalSQL = "";
std::string finalSQL = "";
bool runSd0Migrations = false;
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/").string())) {
auto migration = LoadMigration("dlu/" + entry);
@@ -61,12 +61,12 @@ void MigrationRunner::RunMigrations() {
}
if (!finalSQL.empty()) {
auto migration = GeneralUtils::SplitString(static_cast<std::string>(finalSQL), ';');
auto migration = GeneralUtils::SplitString(finalSQL, ';');
for (auto& query : migration) {
try {
if (query.empty()) continue;
Database::Get()->ExecuteCustomQuery(query.c_str());
} catch (sql::SQLException& e) {
Database::Get()->ExecuteCustomQuery(query);
} catch (std::exception& e) {
LOG("Encountered error running migration: %s", e.what());
}
}

View File

@@ -27,6 +27,8 @@ Character::Character(uint32_t id, User* parentUser) {
m_ID = id;
m_ParentUser = parentUser;
m_OurEntity = nullptr;
m_GMLevel = eGameMasterLevel::CIVILIAN;
m_PermissionMap = static_cast<ePermissionMap>(0);
}
Character::~Character() {
@@ -38,8 +40,8 @@ void Character::UpdateInfoFromDatabase() {
auto charInfo = Database::Get()->GetCharacterInfo(m_ID);
if (charInfo) {
m_Name = charInfo->name;
m_UnapprovedName = charInfo->pendingName;
m_Name = charInfo->name;
m_UnapprovedName = charInfo->pendingName;
m_NameRejected = charInfo->needsRename;
m_PropertyCloneID = charInfo->cloneId;
m_PermissionMap = charInfo->permissionMap;
@@ -74,7 +76,7 @@ void Character::DoQuickXMLDataParse() {
if (m_Doc.Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
} else {
LOG("Failed to load xmlData!");
LOG("Failed to load xmlData (%i) (%s) (%s)!", m_Doc.ErrorID(), m_Doc.ErrorIDToName(m_Doc.ErrorID()), m_Doc.ErrorStr());
//Server::rakServer->CloseConnection(m_ParentUser->GetSystemAddress(), true);
return;
}

View File

@@ -38,6 +38,7 @@ public:
const std::string& GetXMLData() const { return m_XMLData; }
const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
void _setXmlDoc(tinyxml2::XMLDocument& doc) { doc.DeepCopy(&m_Doc); }
/**
* Out of abundance of safety and clarity of what this saves, this is its own function.
@@ -459,27 +460,31 @@ public:
User* GetParentUser() const { return m_ParentUser; }
void _doQuickXMLDataParse() { DoQuickXMLDataParse(); }
void _setXmlData(const std::string& xmlData) { m_XMLData = xmlData; }
private:
void UpdateInfoFromDatabase();
/**
* The ID of this character. First 32 bits of the ObjectID.
*/
uint32_t m_ID;
uint32_t m_ID{};
/**
* The 64-bit unique ID used in the game.
*/
LWOOBJID m_ObjectID;
LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
/**
* The user that owns this character.
*/
User* m_ParentUser;
User* m_ParentUser{};
/**
* If the character is in game, this is the entity that it represents, else nullptr.
*/
Entity* m_OurEntity;
Entity* m_OurEntity{};
/**
* 0-9, the Game Master level of this character.
@@ -506,17 +511,17 @@ private:
/**
* Whether the custom name of this character is rejected
*/
bool m_NameRejected;
bool m_NameRejected{};
/**
* The current amount of coins of this character
*/
int64_t m_Coins;
int64_t m_Coins{};
/**
* Whether the character is building
*/
bool m_BuildMode;
bool m_BuildMode{};
/**
* The items equipped by the character on world load
@@ -583,7 +588,7 @@ private:
/**
* The ID of the properties of this character
*/
uint32_t m_PropertyCloneID;
uint32_t m_PropertyCloneID{};
/**
* The XML data for this character, stored as string
@@ -613,7 +618,7 @@ private:
/**
* The last time this character logged in
*/
uint64_t m_LastLogin;
uint64_t m_LastLogin{};
/**
* The gameplay flags this character has (not just true values)

View File

@@ -24,7 +24,7 @@
#include "eTriggerEventType.h"
#include "eObjectBits.h"
#include "PositionUpdate.h"
#include "eChatMessageType.h"
#include "MessageType/Chat.h"
#include "PlayerManager.h"
//Component includes:
@@ -96,6 +96,8 @@
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
Observable<Entity*, const PositionUpdate&> Entity::OnPlayerPositionUpdate;
Entity::Entity(const LWOOBJID& objectID, EntityInfo info, User* parentUser, Entity* parentEntity) {
m_ObjectID = objectID;
m_TemplateID = info.lot;
@@ -225,7 +227,7 @@ void Entity::Initialize() {
AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
AddComponent<ModelComponent>();
AddComponent<ModelComponent>()->LoadBehaviors();
AddComponent<RenderComponent>();
@@ -649,7 +651,7 @@ void Entity::Initialize() {
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
AddComponent<ModelComponent>();
AddComponent<ModelComponent>()->LoadBehaviors();
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
auto* destroyableComponent = AddComponent<DestroyableComponent>();
destroyableComponent->SetHealth(1);
@@ -881,7 +883,7 @@ void Entity::SetGMLevel(eGameMasterLevel value) {
// Update the chat server of our GM Level
{
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GMLEVEL_UPDATE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GMLEVEL_UPDATE);
bitStream.Write(m_ObjectID);
bitStream.Write(m_GMLevel);
@@ -1349,11 +1351,6 @@ void Entity::OnCollisionPhantom(const LWOOBJID otherEntity) {
callback(other);
}
SwitchComponent* switchComp = GetComponent<SwitchComponent>();
if (switchComp) {
switchComp->EntityEnter(other);
}
TriggerEvent(eTriggerEventType::ENTER, other);
// POI system
@@ -2133,6 +2130,8 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
Game::entityManager->QueueGhostUpdate(GetObjectID());
if (updateChar) Game::entityManager->SerializeEntity(this);
OnPlayerPositionUpdate.Notify(this, update);
}
const SystemAddress& Entity::GetSystemAddress() const {

View File

@@ -11,6 +11,7 @@
#include "NiQuaternion.h"
#include "LDFFormat.h"
#include "eKillType.h"
#include "Observable.h"
namespace Loot {
class Info;
@@ -299,6 +300,11 @@ public:
// Scale will only be communicated to the client when the construction packet is sent
void SetScale(const float scale) { m_Scale = scale; };
/**
* @brief The observable for player entity position updates.
*/
static Observable<Entity*, const PositionUpdate&> OnPlayerPositionUpdate;
protected:
LWOOBJID m_ObjectID;

View File

@@ -12,6 +12,7 @@ User::User(const SystemAddress& sysAddr, const std::string& username, const std:
m_AccountID = 0;
m_Username = "";
m_SessionKey = "";
m_MuteExpire = 0;
m_MaxGMLevel = eGameMasterLevel::CIVILIAN; //The max GM level this account can assign to it's characters
m_LastCharID = 0;

View File

@@ -25,7 +25,7 @@ public:
User& operator=(const User& other);
bool operator==(const User& other) const;
uint32_t GetAccountID() { return m_AccountID; }
uint32_t GetAccountID() const noexcept { return m_AccountID; }
std::string& GetUsername() { return m_Username; }
std::string& GetSessionKey() { return m_SessionKey; }
SystemAddress& GetSystemAddress() { return m_SystemAddress; }

View File

@@ -26,7 +26,7 @@
#include "eCharacterCreationResponse.h"
#include "eRenameResponse.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "MessageType/Chat.h"
#include "BitStreamUtils.h"
#include "CheatDetection.h"
@@ -216,7 +216,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
}
RakNet::BitStream bitStream;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::CHARACTER_LIST_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHARACTER_LIST_RESPONSE);
std::vector<Character*> characters = u->GetCharacters();
bitStream.Write<uint8_t>(characters.size());
@@ -266,7 +266,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet) {
User* u = GetUser(sysAddr);
if (!u) return;
LUWString LUWStringName;
uint32_t firstNameIndex;
uint32_t middleNameIndex;
@@ -422,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
Database::Get()->DeleteCharacter(charID);
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::UNEXPECTED_DISCONNECT);
bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
@@ -439,7 +439,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
CINSTREAM_SKIP_HEADER;
LWOOBJID objectID;
inStream.Read(objectID);
inStream.Read(objectID);
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);

View File

@@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
this->scheduledUpdates.clear();
}
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
BehaviorSyncEntry entry;
auto found = false;
@@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (!found) {
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
return;
return false;
}
auto* behavior = entry.behavior;
@@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (behavior == nullptr) {
LOG("Invalid behavior for sync id (%i)!", syncId);
return;
return false;
}
behavior->Sync(this, bitStream, branch);
return true;
}
@@ -198,6 +199,26 @@ void BehaviorContext::UpdatePlayerSyncs(float deltaTime) {
i++;
continue;
}
if (this->skillUId != 0 && !clientInitalized) {
EchoSyncSkill echo;
echo.bDone = true;
echo.uiSkillHandle = this->skillUId;
echo.uiBehaviorHandle = entry.handle;
RakNet::BitStream bitStream{};
entry.behavior->SyncCalculation(this, bitStream, entry.branchContext);
echo.sBitStream.assign(reinterpret_cast<char*>(bitStream.GetData()), bitStream.GetNumberOfBytesUsed());
RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
message.Write(this->originator);
echo.Serialize(message);
Game::server->Send(message, UNASSIGNED_SYSTEM_ADDRESS, true);
}
this->syncEntries.erase(this->syncEntries.begin() + i);
}
}
@@ -264,7 +285,7 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
// Write message
RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
message.Write(this->originator);
echo.Serialize(message);

View File

@@ -93,7 +93,7 @@ struct BehaviorContext
void ExecuteUpdates();
void SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
bool SyncBehavior(uint32_t syncId, RakNet::BitStream& bitStream);
void Update(float deltaTime);

View File

@@ -1,7 +1,7 @@
#pragma once
#include "Behavior.h"
#include "eAninmationFlags.h"
#include "eAnimationFlags.h"
class ChangeIdleFlagsBehavior final : public Behavior {
public:

View File

@@ -27,6 +27,8 @@ void DamageAbsorptionBehavior::Handle(BehaviorContext* context, RakNet::BitStrea
destroyable->SetIsShielded(true);
context->RegisterTimerBehavior(this, branch, target->GetObjectID());
Game::entityManager->SerializeEntity(target);
}
void DamageAbsorptionBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
@@ -52,7 +54,13 @@ void DamageAbsorptionBehavior::Timer(BehaviorContext* context, BehaviorBranchCon
const auto toRemove = std::min(present, this->m_absorbAmount);
destroyable->SetDamageToAbsorb(present - toRemove);
const auto remaining = present - toRemove;
destroyable->SetDamageToAbsorb(remaining);
destroyable->SetIsShielded(remaining > 0);
Game::entityManager->SerializeEntity(target);
}
void DamageAbsorptionBehavior::Load() {

View File

@@ -21,7 +21,7 @@
#include "eMissionTaskType.h"
#include "eMatchUpdate.h"
#include "eConnectionType.h"
#include "eChatMessageType.h"
#include "MessageType/Chat.h"
#include "CDCurrencyTableTable.h"
#include "CDActivityRewardsTable.h"
@@ -501,7 +501,7 @@ void ActivityInstance::StartZone() {
// only make a team if we have more than one participant
if (participants.size() > 1) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::CREATE_TEAM);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::CREATE_TEAM);
bitStream.Write(leader->GetObjectID());
bitStream.Write(m_Participants.size());

View File

@@ -29,7 +29,8 @@
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
m_Target = LWOOBJID_EMPTY;
SetAiState(AiState::spawn);
m_DirtyStateOrTarget = true;
m_State = AiState::spawn;
m_Timer = 1.0f;
m_StartPosition = parent->GetPosition();
m_MovementAI = nullptr;

View File

@@ -25,6 +25,8 @@ struct ZoneStatistics {
uint64_t m_CoinsCollected;
uint64_t m_EnemiesSmashed;
uint64_t m_QuickBuildsCompleted;
bool operator==(const ZoneStatistics& rhs) const = default;
};
/**
@@ -279,9 +281,9 @@ public:
*/
void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const;
void SetCurrentInteracting(LWOOBJID objectID) {m_CurrentInteracting = objectID;};
void SetCurrentInteracting(LWOOBJID objectID) { m_CurrentInteracting = objectID; };
LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;};
LWOOBJID GetCurrentInteracting() { return m_CurrentInteracting; };
/**
* Sends a player to another zone with an optional clone ID
@@ -307,6 +309,14 @@ public:
void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; };
const std::array<uint64_t, 4>& GetClaimCodes() const { return m_ClaimCodes; };
const std::map<LWOMAPID, ZoneStatistics>& GetZoneStatistics() const { return m_ZoneStatistics; };
const std::u16string& GetLastRocketConfig() const { return m_LastRocketConfig; };
uint64_t GetTotalTimePlayed() const { return m_TotalTimePlayed; };
/**
* Character info regarding this character, including clothing styles, etc.
*/

View File

@@ -38,6 +38,9 @@
#include "CDComponentsRegistryTable.h"
Implementation<bool, const Entity*> DestroyableComponent::IsEnemyImplentation;
Implementation<bool, const Entity*> DestroyableComponent::IsFriendImplentation;
DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_iArmor = 0;
m_fMaxArmor = 0.0f;
@@ -418,6 +421,7 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
}
bool DestroyableComponent::IsEnemy(const Entity* other) const {
if (IsEnemyImplentation.ExecuteWithDefault(other, false)) return true;
if (m_Parent->IsPlayer() && other->IsPlayer()) {
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!thisCharacterComponent) return false;
@@ -440,6 +444,7 @@ bool DestroyableComponent::IsEnemy(const Entity* other) const {
}
bool DestroyableComponent::IsFriend(const Entity* other) const {
if (IsFriendImplentation.ExecuteWithDefault(other, false)) return true;
const auto* otherDestroyableComponent = other->GetComponent<DestroyableComponent>();
if (otherDestroyableComponent != nullptr) {
for (const auto enemyFaction : m_EnemyFactionIDs) {

View File

@@ -7,6 +7,7 @@
#include "Entity.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include "Implementation.h"
namespace CppScripts {
class Script;
@@ -463,6 +464,9 @@ public:
// handle hardcode mode drops
void DoHardcoreModeDrops(const LWOOBJID source);
static Implementation<bool, const Entity*> IsEnemyImplentation;
static Implementation<bool, const Entity*> IsFriendImplentation;
private:
/**
* Whether or not the health should be serialized

View File

@@ -37,6 +37,9 @@
#include "CDScriptComponentTable.h"
#include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h"
#include "StringifiedEnum.h"
#include <ranges>
InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true;
@@ -492,6 +495,11 @@ void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
return;
}
auto* const groups = inventoryElement->FirstChildElement("grps");
if (groups) {
LoadGroupXml(*groups);
}
m_Consumable = inventoryElement->IntAttribute("csl", LOT_NULL);
auto* bag = bags->FirstChildElement();
@@ -630,6 +638,15 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
bags->LinkEndChild(bag);
}
auto* groups = inventoryElement->FirstChildElement("grps");
if (groups) {
groups->DeleteChildren();
} else {
groups = inventoryElement->InsertNewChildElement("grps");
}
UpdateGroupXml(*groups);
auto* items = inventoryElement->FirstChildElement("items");
if (items == nullptr) {
@@ -875,8 +892,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
RemoveSlot(item->GetInfo().equipLocation);
PurgeProxies(item);
UnequipScripts(item);
Game::entityManager->SerializeEntity(m_Parent);
@@ -886,6 +901,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
}
PurgeProxies(item);
}
@@ -1505,10 +1522,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
const auto root = item->GetParent();
if (root != LWOOBJID_EMPTY) {
item = FindItemById(root);
Item* itemRoot = FindItemById(root);
if (item != nullptr) {
UnEquipItem(item);
if (itemRoot != nullptr) {
UnEquipItem(itemRoot);
}
return;
@@ -1603,3 +1620,110 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
m_Skills.insert_or_assign(slot, skillId);
return true;
}
void InventoryComponent::UpdateGroup(const GroupUpdate& groupUpdate) {
if (groupUpdate.groupId.empty()) return;
if (groupUpdate.inventory != eInventoryType::BRICKS && groupUpdate.inventory != eInventoryType::MODELS) {
LOG("Invalid inventory type for grouping %s", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
auto& groups = m_Groups[groupUpdate.inventory];
auto groupItr = std::ranges::find_if(groups, [&groupUpdate](const Group& group) {
return group.groupId == groupUpdate.groupId;
});
if (groupUpdate.command != GroupUpdateCommand::ADD && groupItr == groups.end()) {
LOG("Group %s not found in inventory %s. Cannot process command.", groupUpdate.groupId.c_str(), StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
if (groupUpdate.command == GroupUpdateCommand::ADD && groups.size() >= MaximumGroupCount) {
LOG("Cannot add group to inventory %s. Maximum group count reached.", StringifiedEnum::ToString(groupUpdate.inventory).data());
return;
}
switch (groupUpdate.command) {
case GroupUpdateCommand::ADD: {
auto& group = groups.emplace_back();
group.groupId = groupUpdate.groupId;
group.groupName = groupUpdate.groupName;
break;
}
case GroupUpdateCommand::ADD_LOT: {
groupItr->lots.insert(groupUpdate.lot);
break;
}
case GroupUpdateCommand::REMOVE: {
groups.erase(groupItr);
break;
}
case GroupUpdateCommand::REMOVE_LOT: {
groupItr->lots.erase(groupUpdate.lot);
break;
}
case GroupUpdateCommand::MODIFY: {
groupItr->groupName = groupUpdate.groupName;
break;
}
default: {
LOG("Invalid group update command %i", groupUpdate.command);
break;
}
}
}
void InventoryComponent::UpdateGroupXml(tinyxml2::XMLElement& groups) const {
for (const auto& [inventory, groupsData] : m_Groups) {
for (const auto& group : groupsData) {
auto* const groupElement = groups.InsertNewChildElement("grp");
groupElement->SetAttribute("id", group.groupId.c_str());
groupElement->SetAttribute("n", group.groupName.c_str());
groupElement->SetAttribute("t", static_cast<uint32_t>(inventory));
groupElement->SetAttribute("u", 0);
std::ostringstream lots;
bool first = true;
for (const auto lot : group.lots) {
if (!first) lots << ' ';
first = false;
lots << lot;
}
groupElement->SetAttribute("l", lots.str().c_str());
}
}
}
void InventoryComponent::LoadGroupXml(const tinyxml2::XMLElement& groups) {
auto* groupElement = groups.FirstChildElement("grp");
while (groupElement) {
const char* groupId = nullptr;
const char* groupName = nullptr;
const char* lots = nullptr;
uint32_t inventory = eInventoryType::INVALID;
groupElement->QueryStringAttribute("id", &groupId);
groupElement->QueryStringAttribute("n", &groupName);
groupElement->QueryStringAttribute("l", &lots);
groupElement->QueryAttribute("t", &inventory);
if (!groupId || !groupName || !lots) {
LOG("Failed to load group from xml id %i name %i lots %i",
groupId == nullptr, groupName == nullptr, lots == nullptr);
} else {
auto& group = m_Groups[static_cast<eInventoryType>(inventory)].emplace_back();
group.groupId = groupId;
group.groupName = groupName;
for (const auto& lotStr : GeneralUtils::SplitString(lots, ' ')) {
auto lot = GeneralUtils::TryParse<LOT>(lotStr);
if (lot) group.lots.insert(*lot);
}
}
groupElement = groupElement->NextSiblingElement("grp");
}
}

View File

@@ -37,6 +37,35 @@ enum class eItemType : int32_t;
*/
class InventoryComponent final : public Component {
public:
struct Group {
// Generated ID for the group. The ID is sent by the client and has the format user_group + Math.random() * UINT_MAX.
std::string groupId;
// Custom name assigned by the user.
std::string groupName;
// All the lots the user has in the group.
std::set<LOT> lots;
};
enum class GroupUpdateCommand {
ADD,
ADD_LOT,
MODIFY,
REMOVE,
REMOVE_LOT,
};
// Based on the command, certain fields will be used or not used.
// for example, ADD_LOT wont use groupName, MODIFY wont use lots, etc.
struct GroupUpdate {
std::string groupId;
std::string groupName;
LOT lot;
eInventoryType inventory;
GroupUpdateCommand command;
};
static constexpr uint32_t MaximumGroupCount = 50;
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
InventoryComponent(Entity* parent);
@@ -367,14 +396,23 @@ public:
*/
void UnequipScripts(Item* unequippedItem);
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
std::map<BehaviorSlot, uint32_t> GetSkills() { return m_Skills; };
bool SetSkill(int slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
void UpdateGroup(const GroupUpdate& groupUpdate);
void RemoveGroup(const std::string& groupId);
~InventoryComponent() override;
private:
/**
* The key is the inventory the group belongs to, the value maps' key is the id for the group.
* This is only used for bricks and model inventories.
*/
std::map<eInventoryType, std::vector<Group>> m_Groups{ { eInventoryType::BRICKS, {} }, { eInventoryType::MODELS, {} } };
/**
* All the inventory this entity possesses
*/
@@ -477,6 +515,9 @@ private:
* @param document the xml doc to load from
*/
void UpdatePetXml(tinyxml2::XMLDocument& document);
void LoadGroupXml(const tinyxml2::XMLElement& groups);
void UpdateGroupXml(tinyxml2::XMLElement& groups) const;
};
#endif

View File

@@ -6,6 +6,10 @@
#include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
#include "SimplePhysicsComponent.h"
#include "Database.h"
ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
@@ -14,6 +18,33 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}
void ModelComponent::LoadBehaviors() {
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
for (const auto& behavior : behaviors) {
if (behavior.empty()) continue;
const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;
LOG_DEBUG("Loading behavior %d", behaviorId.value());
auto& inserted = m_Behaviors.emplace_back();
inserted.SetBehaviorId(*behaviorId);
const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value());
tinyxml2::XMLDocument behaviorXml;
auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size());
LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str());
const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior");
if (!behaviorRoot) {
LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value());
continue;
}
inserted.Deserialize(*behaviorRoot);
}
}
void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
@@ -65,10 +96,42 @@ void ModelComponent::AddBehavior(AddMessage& msg) {
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);
auto* const simplePhysComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysComponent) {
simplePhysComponent->SetPhysicsMotionState(1);
Game::entityManager->SerializeEntity(m_Parent);
}
}
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
if (m_Behaviors.empty()) {
auto* const simplePhysComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysComponent) {
simplePhysComponent->SetPhysicsMotionState(4);
Game::entityManager->SerializeEntity(m_Parent);
}
}
}
std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
std::array<std::pair<int32_t, std::string>, 5> toReturn{};
for (auto i = 0; i < m_Behaviors.size(); i++) {
const auto& behavior = m_Behaviors.at(i);
if (behavior.GetBehaviorId() == -1) continue;
auto& [id, behaviorData] = toReturn[i];
id = behavior.GetBehaviorId();
tinyxml2::XMLDocument doc;
auto* root = doc.NewElement("Behavior");
behavior.Serialize(*root);
doc.InsertFirstChild(root);
tinyxml2::XMLPrinter printer(0, true, 0);
doc.Print(&printer);
behaviorData = printer.CStr();
}
return toReturn;
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include <array>
#include <map>
#include "dCommonVars.h"
@@ -28,6 +29,8 @@ public:
ModelComponent(Entity* parent);
void LoadBehaviors();
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
/**
@@ -63,8 +66,8 @@ public:
template<typename Msg>
void HandleControlBehaviorsMsg(const AMFArrayValue& args) {
static_assert(std::is_base_of_v<BehaviorMessageBase, Msg>, "Msg must be a BehaviorMessageBase");
Msg msg(args);
for (auto& behavior : m_Behaviors) {
Msg msg{ args };
for (auto&& behavior : m_Behaviors) {
if (behavior.GetBehaviorId() == msg.GetBehaviorId()) {
behavior.HandleMsg(msg);
return;
@@ -109,6 +112,8 @@ public:
void VerifyBehaviors();
std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;
private:
/**
* The behaviors of the model

View File

@@ -88,7 +88,7 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone
m_Ability = ePetAbilityType::Invalid;
m_StartPosition = NiPoint3Constant::ZERO;
m_MovementAI = nullptr;
m_TresureTime = 0;
m_TreasureTime = 0;
std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar<std::u16string>(u"CheckPrecondition"));
@@ -319,27 +319,27 @@ void PetComponent::Update(float deltaTime) {
return;
}
if (m_TresureTime > 0) {
auto* tresure = Game::entityManager->GetEntity(m_Interaction);
if (m_TreasureTime > 0) {
auto* treasure = Game::entityManager->GetEntity(m_Interaction);
if (tresure == nullptr) {
m_TresureTime = 0;
if (treasure == nullptr) {
m_TreasureTime = 0;
return;
}
m_TresureTime -= deltaTime;
m_TreasureTime -= deltaTime;
m_MovementAI->Stop();
if (m_TresureTime <= 0) {
if (m_TreasureTime <= 0) {
m_Parent->SetOwnerOverride(m_Owner);
tresure->Smash(m_Parent->GetObjectID());
treasure->Smash(m_Parent->GetObjectID());
m_Interaction = LWOOBJID_EMPTY;
m_TresureTime = 0;
m_TreasureTime = 0;
}
return;
@@ -381,7 +381,7 @@ void PetComponent::Update(float deltaTime) {
float distance = Vector3::DistanceSquared(position, switchPosition);
if (distance < 3 * 3) {
m_Interaction = closestSwitch->GetParentEntity()->GetObjectID();
closestSwitch->EntityEnter(m_Parent);
closestSwitch->OnUse(m_Parent);
} else if (distance < 20 * 20) {
haltDistance = 1;
@@ -396,30 +396,30 @@ void PetComponent::Update(float deltaTime) {
// Determine if the "Lost Tags" mission has been completed and digging has been unlocked
const bool digUnlocked = missionComponent->GetMissionState(842) == eMissionState::COMPLETE;
Entity* closestTresure = PetDigServer::GetClosestTresure(position);
Entity* closestTreasure = PetDigServer::GetClosestTreasure(position);
if (closestTresure != nullptr && digUnlocked) {
if (closestTreasure != nullptr && digUnlocked) {
// Skeleton Dragon Pat special case for bone digging
if (closestTresure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) {
goto skipTresure;
if (closestTreasure->GetLOT() == 12192 && m_Parent->GetLOT() != 13067) {
goto skipTreasure;
}
NiPoint3 tresurePosition = closestTresure->GetPosition();
float distance = Vector3::DistanceSquared(position, tresurePosition);
NiPoint3 treasurePosition = closestTreasure->GetPosition();
float distance = Vector3::DistanceSquared(position, treasurePosition);
if (distance < 5 * 5) {
m_Interaction = closestTresure->GetObjectID();
m_Interaction = closestTreasure->GetObjectID();
Command(NiPoint3Constant::ZERO, LWOOBJID_EMPTY, 1, 202, true);
m_TresureTime = 2;
m_TreasureTime = 2;
} else if (distance < 10 * 10) {
haltDistance = 1;
destination = tresurePosition;
destination = treasurePosition;
}
}
skipTresure:
skipTreasure:
m_MovementAI->SetHaltDistance(haltDistance);
@@ -795,8 +795,6 @@ void PetComponent::Wander() {
}
void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
AddDrainImaginationTimer(item, fromTaming);
m_ItemId = item->GetId();
m_DatabaseId = item->GetSubKey();
@@ -807,6 +805,7 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
inventoryComponent->DespawnPet();
m_Owner = inventoryComponent->GetParent()->GetObjectID();
AddDrainImaginationTimer(fromTaming);
auto* owner = GetOwner();
@@ -859,17 +858,14 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) {
}
}
void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
void PetComponent::AddDrainImaginationTimer(bool fromTaming) {
if (Game::config->GetValue("pets_take_imagination") != "1") return;
auto playerInventory = item->GetInventory();
if (!playerInventory) return;
auto playerInventoryComponent = playerInventory->GetComponent();
if (!playerInventoryComponent) return;
auto playerEntity = playerInventoryComponent->GetParent();
if (!playerEntity) return;
auto* playerEntity = Game::entityManager->GetEntity(m_Owner);
if (!playerEntity) {
LOG("owner was null or didnt exist!");
return;
}
auto playerDestroyableComponent = playerEntity->GetComponent<DestroyableComponent>();
if (!playerDestroyableComponent) return;
@@ -878,12 +874,16 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
if (!fromTaming) playerDestroyableComponent->Imagine(-1);
// Set this to a variable so when this is called back from the player the timer doesn't fire off.
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [playerDestroyableComponent, this, item]() {
if (!playerDestroyableComponent) {
LOG("No petComponent and/or no playerDestroyableComponent");
m_Parent->AddCallbackTimer(m_PetInfo.imaginationDrainRate, [this]() {
const auto* owner = Game::entityManager->GetEntity(m_Owner);
if (!owner) {
LOG("owner was null or didnt exist!");
return;
}
const auto* playerDestroyableComponent = owner->GetComponent<DestroyableComponent>();
if (!playerDestroyableComponent) return;
// If we are out of imagination despawn the pet.
if (playerDestroyableComponent->GetImagination() == 0) {
this->Deactivate();
@@ -893,15 +893,13 @@ void PetComponent::AddDrainImaginationTimer(Item* item, bool fromTaming) {
GameMessages::SendUseItemRequirementsResponse(playerEntity->GetObjectID(), playerEntity->GetSystemAddress(), eUseItemResponse::NoImaginationForPet);
}
this->AddDrainImaginationTimer(item);
this->AddDrainImaginationTimer();
});
}
void PetComponent::Deactivate() {
GameMessages::SendPlayFXEffect(m_Parent->GetObjectID(), -1, u"despawn", "", LWOOBJID_EMPTY, 1, 1, true);
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, GetOwner()->GetSystemAddress());
activePets.erase(m_Owner);
m_Parent->Kill();
@@ -910,6 +908,8 @@ void PetComponent::Deactivate() {
if (owner == nullptr) return;
GameMessages::SendMarkInventoryItemAsActive(m_Owner, false, eUnequippableActiveType::PET, m_ItemId, owner->GetSystemAddress());
GameMessages::SendAddPetToPlayer(m_Owner, 0, u"", LWOOBJID_EMPTY, LOT_NULL, owner->GetSystemAddress());
GameMessages::SendRegisterPetID(m_Owner, LWOOBJID_EMPTY, owner->GetSystemAddress());
@@ -1034,6 +1034,7 @@ Entity* PetComponent::GetParentEntity() const {
}
PetComponent::~PetComponent() {
m_Owner = LWOOBJID_EMPTY;
}
void PetComponent::SetPetNameForModeration(const std::string& petName) {

View File

@@ -205,7 +205,7 @@ public:
*
* @param item The item that represents this pet in the inventory.
*/
void AddDrainImaginationTimer(Item* item, bool fromTaming = false);
void AddDrainImaginationTimer(bool fromTaming = false);
private:
@@ -329,7 +329,7 @@ private:
* Timer that tracks how long a pet has been digging up some treasure, required to spawn the treasure contents
* on time
*/
float m_TresureTime;
float m_TreasureTime;
/**
* The position that this pet was spawned at

View File

@@ -5,7 +5,7 @@
#include "Component.h"
#include "Item.h"
#include "PossessorComponent.h"
#include "eAninmationFlags.h"
#include "eAnimationFlags.h"
#include "eReplicaComponentType.h"
/**

View File

@@ -14,6 +14,8 @@
#include "Amf3.h"
#include "eObjectBits.h"
#include "eGameMasterLevel.h"
#include "ePropertySortType.h"
#include "User.h"
PropertyEntranceComponent::PropertyEntranceComponent(Entity* parent, uint32_t componentID) : Component(parent) {
this->propertyQueries = {};
@@ -74,261 +76,103 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
launcher->Launch(entity, launcher->GetTargetZone(), cloneId);
}
PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) {
property.CloneId = cloneId;
property.OwnerName = ownerName;
property.Name = propertyName;
property.Description = propertyDescription;
property.Reputation = reputation;
property.IsBestFriend = isBFF;
property.IsFriend = isFriend;
property.IsModeratorApproved = isModeratorApproved;
property.IsAlt = isAlt;
property.IsOwned = isOwned;
property.AccessType = privacyOption;
property.DateLastPublished = timeLastUpdated;
property.PerformanceCost = performanceCost;
return property;
}
std::string PropertyEntranceComponent::BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery, bool wantLimits) {
std::string base;
if (customQuery == "") {
base = baseQueryForProperties;
} else {
base = customQuery;
}
std::string orderBy = "";
if (sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS) {
std::string friendsList = " AND p.owner_id IN (";
auto friendsListQuery = Database::Get()->CreatePreppedStmt("SELECT * FROM (SELECT CASE WHEN player_id = ? THEN friend_id WHEN friend_id = ? THEN player_id END AS requested_player FROM friends ) AS fr WHERE requested_player IS NOT NULL ORDER BY requested_player DESC;");
friendsListQuery->setUInt(1, character->GetID());
friendsListQuery->setUInt(2, character->GetID());
auto friendsListQueryResult = friendsListQuery->executeQuery();
while (friendsListQueryResult->next()) {
auto playerIDToConvert = friendsListQueryResult->getInt(1);
friendsList = friendsList + std::to_string(playerIDToConvert) + ",";
}
// Replace trailing comma with the closing parenthesis.
if (friendsList.at(friendsList.size() - 1) == ',') friendsList.erase(friendsList.size() - 1, 1);
friendsList += ") ";
// If we have no friends then use a -1 for the query.
if (friendsList.find("()") != std::string::npos) friendsList = " AND p.owner_id IN (-1) ";
orderBy += friendsList + "ORDER BY ci.name ASC ";
delete friendsListQueryResult;
friendsListQueryResult = nullptr;
delete friendsListQuery;
friendsListQuery = nullptr;
} else if (sortMethod == SORT_TYPE_RECENT) {
orderBy = "ORDER BY p.last_updated DESC ";
} else if (sortMethod == SORT_TYPE_REPUTATION) {
orderBy = "ORDER BY p.reputation DESC, p.last_updated DESC ";
} else {
orderBy = "ORDER BY p.last_updated DESC ";
}
return base + orderBy + (wantLimits ? "LIMIT ? OFFSET ?;" : ";");
}
void PropertyEntranceComponent::OnPropertyEntranceSync(Entity* entity, bool includeNullAddress, bool includeNullDescription, bool playerOwn, bool updateUi, int32_t numResults, int32_t lReputationTime, int32_t sortMethod, int32_t startIndex, std::string filterText, const SystemAddress& sysAddr) {
std::vector<PropertySelectQueryProperty> entries{};
PropertySelectQueryProperty playerEntry{};
auto character = entity->GetCharacter();
const auto* const character = entity->GetCharacter();
if (!character) return;
const auto* const user = character->GetParentUser();
if (!user) return;
auto& entries = propertyQueries[entity->GetObjectID()];
entries.clear();
// Player property goes in index 1 of the vector. This is how the client expects it.
auto playerPropertyLookup = Database::Get()->CreatePreppedStmt("SELECT * FROM properties WHERE owner_id = ? AND zone_id = ?");
playerPropertyLookup->setInt(1, character->GetID());
playerPropertyLookup->setInt(2, this->m_MapID);
auto playerPropertyLookupResults = playerPropertyLookup->executeQuery();
const auto playerProperty = Database::Get()->GetPropertyInfo(m_MapID, character->GetPropertyCloneID());
// If the player has a property this query will have a single result.
if (playerPropertyLookupResults->next()) {
const auto cloneId = playerPropertyLookupResults->getUInt64(4);
const auto propertyName = std::string(playerPropertyLookupResults->getString(5).c_str());
const auto propertyDescription = std::string(playerPropertyLookupResults->getString(6).c_str());
const auto privacyOption = playerPropertyLookupResults->getInt(9);
const auto modApproved = playerPropertyLookupResults->getBoolean(10);
const auto dateLastUpdated = playerPropertyLookupResults->getInt64(11);
const auto reputation = playerPropertyLookupResults->getUInt(14);
const auto performanceCost = playerPropertyLookupResults->getFloat(16);
playerEntry = SetPropertyValues(playerEntry, cloneId, character->GetName(), propertyName, propertyDescription, reputation, true, true, modApproved, true, true, privacyOption, dateLastUpdated, performanceCost);
auto& playerEntry = entries.emplace_back();
if (playerProperty.has_value()) {
playerEntry.OwnerName = character->GetName();
playerEntry.IsBestFriend = true;
playerEntry.IsFriend = true;
playerEntry.IsAlt = true;
playerEntry.IsOwned = true;
playerEntry.CloneId = playerProperty->cloneId;
playerEntry.Name = playerProperty->name;
playerEntry.Description = playerProperty->description;
playerEntry.AccessType = playerProperty->privacyOption;
playerEntry.IsModeratorApproved = playerProperty->modApproved;
playerEntry.DateLastPublished = playerProperty->lastUpdatedTime;
playerEntry.Reputation = playerProperty->reputation;
playerEntry.PerformanceCost = playerProperty->performanceCost;
auto& entry = playerEntry;
} else {
playerEntry = SetPropertyValues(playerEntry, character->GetPropertyCloneID(), character->GetName(), "", "", 0, true, true);
playerEntry.OwnerName = character->GetName();
playerEntry.IsBestFriend = true;
playerEntry.IsFriend = true;
playerEntry.IsAlt = false;
playerEntry.IsOwned = false;
playerEntry.CloneId = character->GetPropertyCloneID();
playerEntry.Name = "";
playerEntry.Description = "";
playerEntry.AccessType = 0;
playerEntry.IsModeratorApproved = false;
playerEntry.DateLastPublished = 0;
playerEntry.Reputation = 0;
playerEntry.PerformanceCost = 0.0f;
}
delete playerPropertyLookupResults;
playerPropertyLookupResults = nullptr;
IProperty::PropertyLookup propertyLookup;
propertyLookup.mapId = m_MapID;
propertyLookup.searchString = filterText;
propertyLookup.sortChoice = static_cast<ePropertySortType>(sortMethod);
propertyLookup.playerSort = static_cast<uint32_t>(sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? PropertyPrivacyOption::Friends : PropertyPrivacyOption::Public);
propertyLookup.playerId = character->GetID();
propertyLookup.numResults = numResults;
propertyLookup.startIndex = startIndex;
delete playerPropertyLookup;
playerPropertyLookup = nullptr;
entries.push_back(playerEntry);
const auto query = BuildQuery(entity, sortMethod, character);
auto propertyLookup = Database::Get()->CreatePreppedStmt(query);
const auto searchString = "%" + filterText + "%";
propertyLookup->setUInt(1, this->m_MapID);
propertyLookup->setString(2, searchString.c_str());
propertyLookup->setString(3, searchString.c_str());
propertyLookup->setString(4, searchString.c_str());
propertyLookup->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? static_cast<uint32_t>(PropertyPrivacyOption::Friends) : static_cast<uint32_t>(PropertyPrivacyOption::Public));
propertyLookup->setInt(6, numResults);
propertyLookup->setInt(7, startIndex);
auto propertyEntry = propertyLookup->executeQuery();
while (propertyEntry->next()) {
const auto propertyId = propertyEntry->getUInt64(1);
const auto owner = propertyEntry->getInt(2);
const auto cloneId = propertyEntry->getUInt64(4);
const auto propertyNameFromDb = std::string(propertyEntry->getString(5).c_str());
const auto propertyDescriptionFromDb = std::string(propertyEntry->getString(6).c_str());
const auto privacyOption = propertyEntry->getInt(9);
const auto modApproved = propertyEntry->getBoolean(10);
const auto dateLastUpdated = propertyEntry->getInt(11);
const float reputation = propertyEntry->getInt(14);
const auto performanceCost = propertyEntry->getFloat(16);
PropertySelectQueryProperty entry{};
std::string ownerName = "";
bool isOwned = true;
auto nameLookup = Database::Get()->CreatePreppedStmt("SELECT name FROM charinfo WHERE prop_clone_id = ?;");
nameLookup->setUInt64(1, cloneId);
auto nameResult = nameLookup->executeQuery();
if (!nameResult->next()) {
delete nameLookup;
nameLookup = nullptr;
LOG("Failed to find property owner name for %llu!", cloneId);
const auto lookupResult = Database::Get()->GetProperties(propertyLookup);
for (const auto& propertyEntry : lookupResult->entries) {
const auto owner = propertyEntry.ownerId;
const auto otherCharacter = Database::Get()->GetCharacterInfo(owner);
if (!otherCharacter.has_value()) {
LOG("Failed to find property owner name for %u!", owner);
continue;
} else {
isOwned = cloneId == character->GetPropertyCloneID();
ownerName = std::string(nameResult->getString(1).c_str());
}
auto& entry = entries.emplace_back();
delete nameResult;
nameResult = nullptr;
delete nameLookup;
nameLookup = nullptr;
std::string propertyName = propertyNameFromDb;
std::string propertyDescription = propertyDescriptionFromDb;
bool isBestFriend = false;
bool isFriend = false;
// Convert owner char id to LWOOBJID
LWOOBJID ownerObjId = owner;
GeneralUtils::SetBit(ownerObjId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(ownerObjId, eObjectBits::PERSISTENT);
entry.IsOwned = entry.CloneId == otherCharacter->cloneId;
entry.OwnerName = otherCharacter->name;
entry.CloneId = propertyEntry.cloneId;
entry.Name = propertyEntry.name;
entry.Description = propertyEntry.description;
entry.AccessType = propertyEntry.privacyOption;
entry.IsModeratorApproved = propertyEntry.modApproved;
entry.DateLastPublished = propertyEntry.lastUpdatedTime;
entry.Reputation = propertyEntry.reputation;
entry.PerformanceCost = propertyEntry.performanceCost;
entry.IsBestFriend = false;
entry.IsFriend = false;
// Query to get friend and best friend fields
auto friendCheck = Database::Get()->CreatePreppedStmt("SELECT best_friend FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?)");
friendCheck->setUInt(1, character->GetID());
friendCheck->setUInt(2, ownerObjId);
friendCheck->setUInt(3, ownerObjId);
friendCheck->setUInt(4, character->GetID());
auto friendResult = friendCheck->executeQuery();
const auto friendCheck = Database::Get()->GetBestFriendStatus(character->GetID(), owner);
// If we got a result than the two players are friends.
if (friendResult->next()) {
isFriend = true;
if (friendResult->getInt(1) == 3) {
isBestFriend = true;
}
if (friendCheck.has_value()) {
entry.IsFriend = true;
entry.IsBestFriend = friendCheck->bestFriendStatus == 3;
}
delete friendCheck;
friendCheck = nullptr;
delete friendResult;
friendResult = nullptr;
bool isModeratorApproved = propertyEntry->getBoolean(10);
if (!isModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) {
propertyName = "[AWAITING APPROVAL]";
propertyDescription = "[AWAITING APPROVAL]";
isModeratorApproved = true;
if (!entry.IsModeratorApproved && entity->GetGMLevel() >= eGameMasterLevel::LEAD_MODERATOR) {
entry.Name = "[AWAITING APPROVAL]";
entry.Description = "[AWAITING APPROVAL]";
entry.IsModeratorApproved = true;
}
bool isAlt = false;
// Query to determine whether this property is an alt character of the entity.
auto isAltQuery = Database::Get()->CreatePreppedStmt("SELECT id FROM charinfo where account_id in (SELECT account_id from charinfo WHERE id = ?) AND id = ?;");
isAltQuery->setInt(1, character->GetID());
isAltQuery->setInt(2, owner);
auto isAltQueryResults = isAltQuery->executeQuery();
if (isAltQueryResults->next()) {
isAlt = true;
for (const auto charid : Database::Get()->GetAccountCharacterIds(user->GetAccountID())) {
entry.IsAlt = charid == owner;
if (entry.IsAlt) break;
}
delete isAltQueryResults;
isAltQueryResults = nullptr;
delete isAltQuery;
isAltQuery = nullptr;
entry = SetPropertyValues(entry, cloneId, ownerName, propertyName, propertyDescription, reputation, isBestFriend, isFriend, isModeratorApproved, isAlt, isOwned, privacyOption, dateLastUpdated, performanceCost);
entries.push_back(entry);
}
delete propertyEntry;
propertyEntry = nullptr;
delete propertyLookup;
propertyLookup = nullptr;
propertyQueries[entity->GetObjectID()] = entries;
// Query here is to figure out whether or not to display the button to go to the next page or not.
int32_t numberOfProperties = 0;
auto buttonQuery = BuildQuery(entity, sortMethod, character, "SELECT COUNT(*) FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ", false);
auto propertiesLeft = Database::Get()->CreatePreppedStmt(buttonQuery);
propertiesLeft->setUInt(1, this->m_MapID);
propertiesLeft->setString(2, searchString.c_str());
propertiesLeft->setString(3, searchString.c_str());
propertiesLeft->setString(4, searchString.c_str());
propertiesLeft->setInt(5, sortMethod == SORT_TYPE_FEATURED || sortMethod == SORT_TYPE_FRIENDS ? 1 : 2);
auto result = propertiesLeft->executeQuery();
result->next();
numberOfProperties = result->getInt(1);
delete result;
result = nullptr;
delete propertiesLeft;
propertiesLeft = nullptr;
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, numberOfProperties - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
GameMessages::SendPropertySelectQuery(m_Parent->GetObjectID(), startIndex, lookupResult->totalEntriesMatchingQuery - (startIndex + numResults) > 0, character->GetPropertyCloneID(), false, true, entries, sysAddr);
}

View File

@@ -57,11 +57,7 @@ public:
* Returns the map ID for this property
* @return the map ID for this property
*/
[[nodiscard]] LWOMAPID GetMapID() const { return m_MapID; };
PropertySelectQueryProperty SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId = LWOCLONEID_INVALID, std::string ownerName = "", std::string propertyName = "", std::string propertyDescription = "", float reputation = 0, bool isBFF = false, bool isFriend = false, bool isModeratorApproved = false, bool isAlt = false, bool isOwned = false, uint32_t privacyOption = 0, uint32_t timeLastUpdated = 0, float performanceCost = 0.0f);
std::string BuildQuery(Entity* entity, int32_t sortMethod, Character* character, std::string customQuery = "", bool wantLimits = true);
[[nodiscard]] LWOMAPID GetMapID() const noexcept { return m_MapID; };
private:
/**
@@ -78,13 +74,4 @@ private:
* The base map ID for this property (Avant Grove, etc).
*/
LWOMAPID m_MapID;
enum ePropertySortType : int32_t {
SORT_TYPE_FRIENDS = 0,
SORT_TYPE_REPUTATION = 1,
SORT_TYPE_RECENT = 3,
SORT_TYPE_FEATURED = 5
};
std::string baseQueryForProperties = "SELECT p.* FROM properties as p JOIN charinfo as ci ON ci.prop_clone_id = p.clone_id where p.zone_id = ? AND (p.description LIKE ? OR p.name LIKE ? OR ci.name LIKE ?) AND p.privacy_option >= ? ";
};

View File

@@ -21,9 +21,11 @@
#include "eObjectBits.h"
#include "CharacterComponent.h"
#include "PlayerManager.h"
#include "ModelComponent.h"
#include <vector>
#include "CppScripts.h"
#include <ranges>
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
@@ -593,6 +595,20 @@ void PropertyManagementComponent::Load() {
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
}
std::ostringstream userModelBehavior;
bool firstAdded = false;
for (auto behavior : databaseModel.behaviors) {
if (behavior < 0) {
LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior);
behavior = 0;
}
if (firstAdded) userModelBehavior << ",";
userModelBehavior << behavior;
firstAdded = true;
}
settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));
node->config = settings;
const auto spawnerId = Game::zoneManager->MakeSpawner(info);
@@ -610,6 +626,12 @@ void PropertyManagementComponent::Save() {
return;
}
const auto* const owner = GetOwner();
if (!owner) return;
const auto* const character = owner->GetCharacter();
if (!character) return;
auto present = Database::Get()->GetPropertyModels(propertyId);
std::vector<LWOOBJID> modelIds;
@@ -624,6 +646,20 @@ void PropertyManagementComponent::Save() {
if (entity == nullptr) {
continue;
}
auto* modelComponent = entity->GetComponent<ModelComponent>();
if (!modelComponent) continue;
const auto modelBehaviors = modelComponent->GetBehaviorsForSave();
// save the behaviors of the model
for (const auto& [behaviorId, behaviorStr] : modelBehaviors) {
if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue;
IBehaviors::Info info {
.behaviorId = behaviorId,
.characterId = character->GetID(),
.behaviorInfo = behaviorStr
};
Database::Get()->AddBehavior(info);
}
const auto position = entity->GetPosition();
const auto rotation = entity->GetRotation();
@@ -635,10 +671,13 @@ void PropertyManagementComponent::Save() {
model.position = position;
model.rotation = rotation;
model.ugcId = 0;
for (auto i = 0; i < model.behaviors.size(); i++) {
model.behaviors[i] = modelBehaviors[i].first;
}
Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
} else {
Database::Get()->UpdateModelPositionRotation(id, position, rotation);
Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
}
}

View File

@@ -25,6 +25,7 @@
#include "LeaderboardManager.h"
#include "dZoneManager.h"
#include "CDActivitiesTable.h"
#include "eStateChangeType.h"
#include <ctime>
#ifndef M_PI
@@ -44,7 +45,6 @@ RacingControlComponent::RacingControlComponent(Entity* parent)
m_LoadedPlayers = 0;
m_LoadTimer = 0;
m_Finished = 0;
m_StartTime = 0;
m_EmptyTimer = 0;
m_SoloRacing = Game::config->GetValue("solo_racing") == "1";
@@ -77,6 +77,9 @@ void RacingControlComponent::OnPlayerLoaded(Entity* player) {
m_LoadedPlayers++;
// not live accurate to stun the player but prevents them from using skills during the race that are not meant to be used.
GameMessages::SendSetStunned(player->GetObjectID(), eStateChangeType::PUSH, player->GetSystemAddress(), LWOOBJID_EMPTY, true, true, true, true, true, true, true, true, true);
LOG("Loading player %i",
m_LoadedPlayers);
m_LobbyPlayers.push_back(player->GetObjectID());
@@ -394,25 +397,6 @@ void RacingControlComponent::HandleMessageBoxResponse(Entity* player, int32_t bu
GameMessages::SendNotifyRacingClient(
m_Parent->GetObjectID(), 2, 0, LWOOBJID_EMPTY, u"",
player->GetObjectID(), UNASSIGNED_SYSTEM_ADDRESS);
auto* missionComponent = player->GetComponent<MissionComponent>();
if (missionComponent == nullptr) return;
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, data->smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_LoadedPlayers > 2) {
missionComponent->Progress(eMissionTaskType::RACING, data->finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (data->finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (data->finished == m_LoadedPlayers) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}
} else if ((id == "ACT_RACE_EXIT_THE_RACE?" || id == "Exit") && button == m_ActivityExitConfirm) {
auto* vehicle = Game::entityManager->GetEntity(data->vehicleID);
@@ -442,9 +426,9 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
outBitStream.Write(player.playerID);
outBitStream.Write(player.data[0]);
if (player.finished != 0) outBitStream.Write<float>(player.raceTime);
if (player.finished != 0) outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
else outBitStream.Write(player.data[1]);
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime);
if (player.finished != 0) outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
else outBitStream.Write(player.data[2]);
if (player.finished == 1) outBitStream.Write<float>(1.0f);
else outBitStream.Write(player.data[3]);
@@ -505,8 +489,8 @@ void RacingControlComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
if (player.finished == 0) continue;
outBitStream.Write1(); // Has more data
outBitStream.Write(player.playerID);
outBitStream.Write<float>(player.bestLapTime);
outBitStream.Write<float>(player.raceTime);
outBitStream.Write<float>(player.bestLapTime.count() / 1000.0f);
outBitStream.Write<float>(player.raceTime.count() / 1000.0f);
}
outBitStream.Write0(); // No more data
@@ -736,7 +720,7 @@ void RacingControlComponent::Update(float deltaTime) {
Game::entityManager->SerializeEntity(m_Parent);
m_StartTime = std::time(nullptr);
m_StartTime = std::chrono::high_resolution_clock::now();
}
m_StartTimer += deltaTime;
@@ -825,46 +809,60 @@ void RacingControlComponent::Update(float deltaTime) {
// Reached the start point, lapped
if (respawnIndex == 0) {
time_t lapTime = std::time(nullptr) - (player.lap == 0 ? m_StartTime : player.lapTime);
const auto now = std::chrono::high_resolution_clock::now();
const auto lapTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - (player.lap == 0 ? m_StartTime : player.lapTime));
// Cheating check
if (lapTime < 40) {
if (lapTime.count() < 40000) {
continue;
}
player.lap++;
player.lapTime = now;
player.lapTime = std::time(nullptr);
if (player.bestLapTime == 0 || player.bestLapTime > lapTime) {
if (player.bestLapTime > lapTime || player.lap == 0) {
player.bestLapTime = lapTime;
LOG("Best lap time (%llu)", lapTime);
}
player.lap++;
auto* missionComponent =
playerEntity->GetComponent<MissionComponent>();
if (missionComponent != nullptr) {
// Progress lap time tasks
missionComponent->Progress(eMissionTaskType::RACING, (lapTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
missionComponent->Progress(eMissionTaskType::RACING, lapTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::LAP_TIME));
if (player.lap == 3) {
m_Finished++;
player.finished = m_Finished;
const auto raceTime =
(std::time(nullptr) - m_StartTime);
const auto raceTime = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_StartTime);
player.raceTime = raceTime;
LOG("Completed time %llu, %llu",
raceTime, raceTime * 1000);
LOG("Completed time %llums %fs", raceTime.count(), raceTime.count() / 1000.0f);
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime), static_cast<float>(player.bestLapTime), static_cast<float>(player.finished == 1));
LeaderboardManager::SaveScore(playerEntity->GetObjectID(), m_ActivityID, static_cast<float>(player.raceTime.count()) / 1000, static_cast<float>(player.bestLapTime.count()) / 1000, static_cast<float>(player.finished == 1));
// Entire race time
missionComponent->Progress(eMissionTaskType::RACING, (raceTime) * 1000, static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
missionComponent->Progress(eMissionTaskType::RACING, player.raceTime.count(), static_cast<LWOOBJID>(eRacingTaskParam::TOTAL_TRACK_TIME));
missionComponent->Progress(eMissionTaskType::RACING, 0, static_cast<LWOOBJID>(eRacingTaskParam::COMPETED_IN_RACE)); // Progress task for competing in a race
missionComponent->Progress(eMissionTaskType::RACING, player.smashedTimes, static_cast<LWOOBJID>(eRacingTaskParam::SAFE_DRIVER)); // Finish a race without being smashed.
// If solo racing is enabled OR if there are 3 players in the race, progress placement tasks.
if (m_SoloRacing || m_RacingPlayers.size() > 2) {
missionComponent->Progress(eMissionTaskType::RACING, player.finished, static_cast<LWOOBJID>(eRacingTaskParam::FINISH_WITH_PLACEMENT)); // Finish in 1st place on a race
if (player.finished == 1) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::FIRST_PLACE_MULTIPLE_TRACKS)); // Finish in 1st place on multiple tracks.
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::WIN_RACE_IN_WORLD)); // Finished first place in specific world.
}
if (player.finished == m_RacingPlayers.size()) {
missionComponent->Progress(eMissionTaskType::RACING, Game::zoneManager->GetZone()->GetWorldID(), static_cast<LWOOBJID>(eRacingTaskParam::LAST_PLACE_FINISH)); // Finished first place in specific world.
}
}
auto* characterComponent = playerEntity->GetComponent<CharacterComponent>();
if (characterComponent != nullptr) {
@@ -873,8 +871,8 @@ void RacingControlComponent::Update(float deltaTime) {
}
}
LOG("Lapped (%i) in (%llu)", player.lap,
lapTime);
LOG("Lapped (%i) in (%llums %fs)", player.lap,
lapTime.count(), lapTime.count() / 1000.0f);
}
LOG("Reached point (%i)/(%i)", player.respawnIndex,

View File

@@ -8,6 +8,7 @@
#include "Entity.h"
#include "Component.h"
#include "eReplicaComponentType.h"
#include <chrono>
/**
* Information for each player in the race
@@ -72,12 +73,12 @@ struct RacingPlayerInfo {
/**
* The fastest lap time of the player
*/
time_t bestLapTime = 0;
std::chrono::milliseconds bestLapTime;
/**
* The current lap time of the player
*/
time_t lapTime = 0;
std::chrono::high_resolution_clock::time_point lapTime;
/**
* The number of times this player smashed their car
@@ -97,7 +98,7 @@ struct RacingPlayerInfo {
/**
* Unused
*/
time_t raceTime = 0;
std::chrono::milliseconds raceTime;
};
/**
@@ -231,7 +232,7 @@ private:
/**
* The time the race was started
*/
time_t m_StartTime;
std::chrono::high_resolution_clock::time_point m_StartTime;
/**
* Timer for tracking how long a player was alone in this race

View File

@@ -18,7 +18,7 @@
#include "BitStreamUtils.h"
#include "eObjectWorldState.h"
#include "eConnectionType.h"
#include "eMasterMessageType.h"
#include "MessageType/Master.h"
RocketLaunchpadControlComponent::RocketLaunchpadControlComponent(Entity* parent, int rocketId) : Component(parent) {
auto query = CDClientDatabase::CreatePreppedStmt(
@@ -137,7 +137,7 @@ LWOCLONEID RocketLaunchpadControlComponent::GetSelectedCloneId(LWOOBJID player)
void RocketLaunchpadControlComponent::TellMasterToPrepZone(int zoneID) {
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, eMasterMessageType::PREP_ZONE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::MASTER, MessageType::Master::PREP_ZONE);
bitStream.Write(zoneID);
Game::server->SendToMaster(bitStream);
}

View File

@@ -27,6 +27,7 @@ SimplePhysicsComponent::SimplePhysicsComponent(Entity* parent, uint32_t componen
} else {
SetClimbableType(eClimbableType::CLIMBABLE_TYPE_NOT);
}
m_PhysicsMotionState = m_Parent->GetVarAs<uint32_t>(u"motionType");
}
SimplePhysicsComponent::~SimplePhysicsComponent() {
@@ -47,11 +48,10 @@ void SimplePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bool bIs
}
// Physics motion state
if (m_PhysicsMotionState != 0) {
outBitStream.Write1();
outBitStream.Write(m_DirtyPhysicsMotionState || bIsInitialUpdate);
if (m_DirtyPhysicsMotionState || bIsInitialUpdate) {
outBitStream.Write<uint32_t>(m_PhysicsMotionState);
} else {
outBitStream.Write0();
m_DirtyPhysicsMotionState = false;
}
PhysicsComponent::Serialize(outBitStream, bIsInitialUpdate);
}
@@ -61,5 +61,6 @@ uint32_t SimplePhysicsComponent::GetPhysicsMotionState() const {
}
void SimplePhysicsComponent::SetPhysicsMotionState(uint32_t value) {
m_DirtyPhysicsMotionState = m_PhysicsMotionState != value;
m_PhysicsMotionState = value;
}

View File

@@ -102,7 +102,9 @@ private:
/**
* The current physics motion state
*/
uint32_t m_PhysicsMotionState = 0;
uint32_t m_PhysicsMotionState = 5;
bool m_DirtyPhysicsMotionState = true;
/**
* Whether or not the entity is climbable

View File

@@ -24,7 +24,7 @@
#include "CDClientManager.h"
#include "CDSkillBehaviorTable.h"
#include "eConnectionType.h"
#include "eClientMessageType.h"
#include "MessageType/Client.h"
ProjectileSyncEntry::ProjectileSyncEntry() {
}
@@ -38,7 +38,7 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
context->skillID = skillID;
this->m_managedBehaviors.insert_or_assign(skillUid, context);
this->m_managedBehaviors.insert({ skillUid, context });
auto* behavior = Behavior::CreateBehavior(behaviorId);
@@ -52,17 +52,24 @@ bool SkillComponent::CastPlayerSkill(const uint32_t behaviorId, const uint32_t s
}
void SkillComponent::SyncPlayerSkill(const uint32_t skillUid, const uint32_t syncId, RakNet::BitStream& bitStream) {
const auto index = this->m_managedBehaviors.find(skillUid);
const auto index = this->m_managedBehaviors.equal_range(skillUid);
if (index == this->m_managedBehaviors.end()) {
if (index.first == this->m_managedBehaviors.end()) {
LOG("Failed to find skill with uid (%i)!", skillUid, syncId);
return;
}
auto* context = index->second;
bool foundSyncId = false;
for (auto it = index.first; it != index.second && !foundSyncId; ++it) {
const auto& context = it->second;
context->SyncBehavior(syncId, bitStream);
foundSyncId = context->SyncBehavior(syncId, bitStream);
}
if (!foundSyncId) {
LOG("Failed to find sync id (%i) for skill with uid (%i)!", syncId, skillUid);
}
}
@@ -138,7 +145,7 @@ void SkillComponent::Update(const float deltaTime) {
for (const auto& pair : this->m_managedBehaviors) pair.second->UpdatePlayerSyncs(deltaTime);
}
std::map<uint32_t, BehaviorContext*> keep{};
std::multimap<uint32_t, BehaviorContext*> keep{};
for (const auto& pair : this->m_managedBehaviors) {
auto* context = pair.second;
@@ -176,7 +183,7 @@ void SkillComponent::Update(const float deltaTime) {
}
}
keep.insert_or_assign(pair.first, context);
keep.insert({ pair.first, context });
}
this->m_managedBehaviors = keep;
@@ -285,7 +292,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
return { false, 0 };
}
this->m_managedBehaviors.insert_or_assign(context->skillUId, context);
this->m_managedBehaviors.insert({ context->skillUId, context });
if (!clientInitalized) {
// Echo start skill
@@ -313,7 +320,7 @@ SkillExecutionResult SkillComponent::CalculateBehavior(
// Write message
RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
message.Write(this->m_Parent->GetObjectID());
start.Serialize(message);
@@ -444,7 +451,7 @@ void SkillComponent::SyncProjectileCalculation(const ProjectileSyncEntry& entry)
RakNet::BitStream message;
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, MessageType::Client::GAME_MSG);
message.Write(this->m_Parent->GetObjectID());
projectileImpact.Serialize(message);

View File

@@ -188,7 +188,7 @@ private:
/**
* All of the active skills mapped by their unique ID.
*/
std::map<uint32_t, BehaviorContext*> m_managedBehaviors;
std::multimap<uint32_t, BehaviorContext*> m_managedBehaviors;
/**
* All active projectiles.

View File

@@ -2,6 +2,7 @@
#include "EntityManager.h"
#include "eTriggerEventType.h"
#include "RenderComponent.h"
#include "DestroyableComponent.h"
std::vector<SwitchComponent*> SwitchComponent::petSwitches;
@@ -11,6 +12,13 @@ SwitchComponent::SwitchComponent(Entity* parent) : Component(parent) {
m_ResetTime = m_Parent->GetVarAs<int32_t>(u"switch_reset_time");
m_QuickBuild = m_Parent->GetComponent<QuickBuildComponent>();
const auto factions = GeneralUtils::SplitString(m_Parent->GetVar<std::u16string>(u"respond_to_faction"), u':');
for (const auto& faction : factions) {
auto factionID = GeneralUtils::TryParse<int32_t>(GeneralUtils::UTF16ToWTF8(faction));
if (!factionID) continue;
m_FactionsToRespondTo.push_back(factionID.value());
}
}
SwitchComponent::~SwitchComponent() {
@@ -25,6 +33,17 @@ void SwitchComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitial
outBitStream.Write(m_Active);
}
void SwitchComponent::OnUse(Entity* originator) {
const auto* const destroyableComponent = originator->GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
for (const auto faction : m_FactionsToRespondTo) {
if (destroyableComponent->HasFaction(faction)) {
EntityEnter(originator);
break;
}
}
}
void SwitchComponent::SetActive(bool active) {
m_Active = active;
@@ -63,6 +82,7 @@ void SwitchComponent::EntityEnter(Entity* entity) {
RenderComponent::PlayAnimation(m_Parent, u"engaged");
m_PetBouncer->SetPetBouncerEnabled(true);
} else {
GameMessages::SendKnockback(entity->GetObjectID(), m_Parent->GetObjectID(), m_Parent->GetObjectID(), 0.0f, NiPoint3(0.0f, 17.0f, 0.0f));
Game::entityManager->SerializeEntity(m_Parent);
}

View File

@@ -22,6 +22,7 @@ public:
~SwitchComponent() override;
void Update(float deltaTime) override;
void OnUse(Entity* originator) override;
Entity* GetParentEntity() const;
@@ -101,6 +102,8 @@ private:
* Attached pet bouncer
*/
BouncerComponent* m_PetBouncer = nullptr;
std::vector<int32_t> m_FactionsToRespondTo{};
};
#endif // SWITCHCOMPONENT_H

View File

@@ -76,8 +76,8 @@ void VendorComponent::RefreshInventory(bool isCreation) {
if (vendorItems.empty()) break;
auto randomItemIndex = GeneralUtils::GenerateRandomNumber<int32_t>(0, vendorItems.size() - 1);
const auto& randomItem = vendorItems.at(randomItemIndex);
vendorItems.erase(vendorItems.begin() + randomItemIndex);
if (SetupItem(randomItem.itemid)) m_Inventory.push_back(SoldItem(randomItem.itemid, randomItem.sortPriority));
vendorItems.erase(vendorItems.begin() + randomItemIndex);
}
}
}

View File

@@ -27,7 +27,7 @@ public:
}
void Serialize(RakNet::BitStream& stream) {
stream.Write(eGameMessageType::DO_CLIENT_PROJECTILE_IMPACT);
stream.Write(MessageType::Game::DO_CLIENT_PROJECTILE_IMPACT);
stream.Write(i64OrgID != LWOOBJID_EMPTY);
if (i64OrgID != LWOOBJID_EMPTY) stream.Write(i64OrgID);

View File

@@ -4,7 +4,7 @@
#include "dCommonVars.h"
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "eGameMessageType.h"
#include "MessageType/Game.h"
/* Same as start skill but with different network options. An echo down to other clients that need to play the skill. */
class EchoStartSkill {
@@ -40,7 +40,7 @@ public:
}
void Serialize(RakNet::BitStream& stream) {
stream.Write(eGameMessageType::ECHO_START_SKILL);
stream.Write(MessageType::Game::ECHO_START_SKILL);
stream.Write(bUsedMouse);

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