Compare commits

...

133 Commits

Author SHA1 Message Date
d77a1a92bf WIP 2025-01-07 09:13:24 -06:00
72ae55981b fix here too 2025-01-06 13:55:43 -06:00
David Markowitz
c31bf3fad4 Merge branch 'webapiv2' of https://github.com/DarkflameUniverse/DarkflameServer into webapiv2 2025-01-06 09:23:25 -08:00
0aa1be01d2 fix windows? 2025-01-06 08:47:07 -06:00
d8172e2126 Update versions.txt
Co-authored-by: Daniel Seiler <me@xiphoseer.de>
2025-01-06 01:40:33 -06:00
88376c233c cleanup and make the web routes cleaner 2025-01-06 01:23:29 -06:00
f3b4143698 fix typo 2025-01-05 18:31:53 -06:00
55a1209c75 Merge branch 'main' into webapiv2 2025-01-05 16:49:24 -06:00
a6c6d892cf chore: remove httplib since it's unused (#1723) 2025-01-05 00:13:07 -06:00
f7228a6495 update version.txt 2025-01-03 16:45:36 -06:00
David Markowitz
fb32534ae3 implement rest of equipment scripts (#1714) 2025-01-03 16:44:20 -06:00
Gie "Max" Vanommeslaeghe
c8fcb3788d Merge pull request #1718 from DarkflameUniverse/no-weapon-thing
fix: weaponless world load
2025-01-03 23:41:21 +01:00
7a99e8ee6b don't unecessairly convert to string 2025-01-03 09:27:48 -06:00
a5e8fd86ac update readme 2025-01-02 23:18:29 -06:00
210bc48149 return 204 when no data 2025-01-02 23:11:06 -06:00
David Markowitz
e757086465 include optimization 2025-01-02 20:28:03 -08:00
David Markowitz
915e9f75d1 Merge branch 'webapiv2' of https://github.com/DarkflameUniverse/DarkflameServer into webapiv2 2025-01-02 20:25:02 -08:00
David Markowitz
e9ee3e21cf Update PlayerContainer.h 2025-01-02 20:24:53 -08:00
deddf0f256 move to top 2025-01-02 22:23:40 -06:00
David Markowitz
ca38139ff5 Update dNet/CMakeLists.txt 2025-01-02 20:21:41 -08:00
41a001e242 Update dNet/CMakeLists.txt
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2025-01-02 22:20:31 -06:00
4e57b4aa7e feedback 2025-01-02 22:19:04 -06:00
David Markowitz
86b419735b fix weaponless world load 2025-01-02 20:17:06 -08:00
David Markowitz
631980af3a add json stuff 2025-01-02 20:14:07 -08:00
David Markowitz
9e16e01b8d Merge branch 'webapiv2' of https://github.com/DarkflameUniverse/DarkflameServer into webapiv2 2025-01-02 19:12:14 -08:00
David Markowitz
6e66c5c362 works 2025-01-02 18:42:50 -08:00
ee590c49c1 disable by default 2025-01-02 20:25:46 -06:00
7aaa69e42d lol, fix it 2025-01-02 20:19:23 -06:00
181bb0ce14 Address some fo the feedback 2025-01-02 20:09:44 -06:00
2b325165aa Update dChatServer/ChatWebAPI.h
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2025-01-02 20:03:59 -06:00
94d53fa77c Update dChatServer/ChatWebAPI.cpp
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2025-01-02 20:03:44 -06:00
846ba894a4 Update dChatServer/PlayerContainer.cpp
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2025-01-02 20:03:16 -06:00
2ce2f4e363 remove uneeded make_optional 2025-01-02 18:01:45 -06:00
fd1ce75380 whitespace 2025-01-02 17:06:08 -06:00
3578076eca even more cleanup, and make the tryparse work properly 2025-01-02 17:04:07 -06:00
7d06d012b5 formatting and more cleanup 2025-01-02 16:48:31 -06:00
126701b5fe Make startup cleaner and don't listen by default 2025-01-02 16:35:27 -06:00
2d08ec641c cleanup and fixes 2025-01-02 16:28:34 -06:00
9387a8e3d1 Merge branch 'main' into webapiv2 2025-01-02 16:13:17 -06:00
e86f4e011b redo it with mongoose
add all previous POC api endpoints
2025-01-02 16:11:45 -06:00
070bec697c Alllll the groundwork 2025-01-02 00:45:53 -06:00
David Markowitz
9936bb0d00 add ignored scripts (#1713) 2025-01-02 00:11:18 -06:00
David Markowitz
beffad42ea fix online notification (#1703) 2025-01-01 21:33:06 -06:00
David Markowitz
3ecbd1013b fix: update player container on shutdown (#1704)
* update activity log on shutdown

* fix online notification

* update container on shutdown
2025-01-01 21:31:12 -06:00
David Markowitz
ff4546c027 readme updates (#1712) 2025-01-01 21:29:45 -06:00
jadebenn
71baa5ce90 feat: Replace calls to system function in server startups (#1691)
* replace linux calls

* windows api

* log child PIDs in parent process

* fix typo for windows

* functions now return the process ID

* use wchar_t for windows APIs

* Update Start.cpp

Try to fix MacOS issues

* Conditionally include unistd.h

* remove sudo config option and add error message for linux

* fix windows .exe extension

* REALLY fix windows

* try replacing c_str() with data()

* really REALLY fix Windows

* Update dNet/dServer.cpp

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

* Update dServer.cpp
2025-01-01 15:41:21 -08:00
David Markowitz
5ccd15a7d8 fix first attack being weaponless (#1709) 2025-01-01 13:33:20 -06:00
David Markowitz
35bcaf6e95 Note required g++ compiler version (#1711) 2025-01-01 11:11:37 -08:00
David Markowitz
900c9b6abe fix: add missing racing scripts (#1708) 2025-01-01 10:54:21 -08:00
David Markowitz
94e7cfc211 fix optional (#1707) 2025-01-01 04:07:44 -06:00
David Markowitz
021db0ecd1 add child loading (#1706)
Tested that the NT combat challenge, am skullkin towers and qa wall in avant gardens all function as before.
2025-01-01 00:46:00 -06:00
David Markowitz
0b261e934f fix shooting gallery bugs (#1702) 2024-12-29 18:21:22 -06:00
David Markowitz
1b9f7e44c7 remove dead loop (#1700) 2024-12-28 17:11:44 -06:00
David Markowitz
08a168de88 update cdclient.fdb file check (#1699) 2024-12-27 22:15:32 -06:00
David Markowitz
0c948a8df6 use simpler converter (#1695) 2024-12-24 22:23:14 -08:00
Gie "Max" Vanommeslaeghe
6ed6efa921 Merge pull request #1694 from DarkflameUniverse/latin1
fix: use encoding on latin1 strings from cdclient
2024-12-25 00:27:00 +01:00
Gie "Max" Vanommeslaeghe
dcc9e023a6 Merge pull request #1693 from DarkflameUniverse/bandwidth
fix: remove bandwidth limit
2024-12-25 00:26:51 +01:00
Gie "Max" Vanommeslaeghe
8509ec8856 Merge pull request #1692 from DarkflameUniverse/really
fix: folder and file checks
2024-12-25 00:25:48 +01:00
David Markowitz
18295017c1 use encoding
use template function

Update GeneralUtils.cpp

consolidate duplicate code

Update GeneralUtils.cpp

Update BinaryIO.cpp

compilers
2024-12-24 14:32:08 -08:00
David Markowitz
e8f011b830 Update sharedconfig.ini 2024-12-24 13:07:55 -08:00
David Markowitz
a787673baf show error box for windows 2024-12-24 12:57:20 -08:00
David Markowitz
e869c0ad03 Add parenthesis around path 2024-12-24 12:39:18 -08:00
David Markowitz
b2af3fa9d4 use binary dir paths, create ones that dont exist, do not run if critical ones do not exist. 2024-12-24 12:36:54 -08:00
David Markowitz
2560bb00da feat: add ns race server script and ignore 3 scripts from pet cove (#1682)
* brother

* use some better logic

* Implement spider boss msg script

tested that the message now shows up when hitting the survival spider entrance area

* add drag to start race feature

* ignore 3 more scripts

* add Ns race server script

* remove logs

* unique

* Update RaceImaginationServer.cpp

* Update CppScripts.cpp
2024-12-20 01:59:22 -06:00
David Markowitz
1ae21c423f skip non-files (#1690) 2024-12-19 12:19:41 -06:00
jadebenn
0ae9eb4a96 remove unneeded Component.cpp, forward declare dependencies, and make Component definition header-only (#1688) 2024-12-18 00:45:56 -08:00
David Markowitz
fced6d753a fix: Create resServer and logs if it doesnt exist and update readme (#1686)
* create resServer if not exist

* Update README.md

* Update README.md
2024-12-17 21:06:07 -06:00
David Markowitz
15dc5feeb5 feat: start car races if you "equip" the car near the car pad; add more old ns scripts to ignore list (#1681)
* brother

* use some better logic

* Implement spider boss msg script

tested that the message now shows up when hitting the survival spider entrance area

* add drag to start race feature
2024-12-17 21:04:35 -06:00
David Markowitz
a60865cd19 feat: allow SQLite database backend (#1663)
* simplify leaderboard code, fully abstract database

* update exception catching

* update exception catching and sql references, remove ugc from gamemessages

fix deleting model

remove unrelated changes

Update GameMessages.cpp

* remove ugc from gamemessages

* Update GameMessages.cpp

* Update Leaderboard.cpp

* bug fixes

* fix racing leaderboard

* remove extra stuff

* update

* add sqlite

* use a default for optimizations

* update sqlite

* Fix limits on update and delete

* fix bugs

* use definition to switch between databases

* add switch for different backends

* fix include guard and includes

* always build both

* add mysql if block

* Update Database.cpp

* add new options and add check to prevent overriding mysql

* correct config names

* Update README.md

* Update README.md

* merge to 1 sql file for sqlite database

* move to sqlite folder

* add back mysql migrations

* Update README.md

* add migration to correct the folder name or mysql

* yes aron

* updates

* Update CMakeLists.txt

* dont use paths at all, add where check to only update if folder name still exist

check also doesnt check for slashes and assumes one will be there since it will be.

* default dont auto create account

for releases we can change this flag

* default 0

* add times played query

* fix leaderboard not incrementing on a not better score

* add env vars with defaults for docker

* use an "enum"

* default to mariadb

* Update .env.example
2024-12-17 16:07:07 -08:00
jadebenn
77b42daca1 feat: Remove reinterpret_casts from AG race timer script and add method and chat command to get current server uptime (#1673)
* use steady_clock race timer

* formatting and const

* improve time interface

* added uptime chat command

* fix bug and update documentation

* inrease /uptime GM level requirement

* update GM level for /uptime (again)

* made changes according to feedback
2024-12-17 14:06:16 -06:00
David Markowitz
ba364800fe feat: allow for teleporting to player or relative position (#1683)
* allow for teleporting to player or relative position

* Update Commands.md

* Update Commands.md

* Update SlashCommandHandler.cpp
2024-12-17 13:39:28 -06:00
David Markowitz
e1c20192f7 fix: Implement missing survival tooltip script (#1679)
* brother

* use some better logic

* Implement spider boss msg script

tested that the message now shows up when hitting the survival spider entrance area
2024-12-16 13:35:36 -06:00
David Markowitz
0f8c5b436d fix: implement enemy clear threat script (#1678)
* brother

* use some better logic
2024-12-15 23:44:57 -06:00
53242ad5d5 Merge pull request #1680 from DarkflameUniverse/warn
fix: warnings
2024-12-15 22:45:52 -06:00
David Markowitz
a8919c8c14 Update dGame/dUtilities/Preconditions.cpp
Co-authored-by: jadebenn <jadebenn@users.noreply.github.com>
2024-12-14 21:14:07 -08:00
David Markowitz
5ff121612e use a cast
fix warning

remove pragma

we dont need this tbh
2024-12-14 17:55:41 -08:00
34618607c3 Merge pull request #1675 from DarkflameUniverse/invisible-items
the client code for this is a mess and should load everything at once or use non race condition code
2024-12-11 11:32:20 -06:00
Jett
02b76adb7a Replace keygen with CyberChef (#1677) 2024-12-11 16:58:37 +00:00
David Markowitz
3beb414b55 good enough
the client code for this is a mess and should load everything at once or use non race condition code
2024-12-10 19:10:54 -08:00
David Markowitz
1dadeeb36f fix leaderboard not incrementing on a not better score (#1674) 2024-12-10 05:37:49 -06:00
David Markowitz
aa7c3b9061 better vanity checks (#1666)
tested that vanity npcs now chat when close, and then on a cooldown
2024-12-08 16:27:04 -06:00
David Markowitz
1644d9448d Update .gitignore (#1672) 2024-12-08 16:23:55 -06:00
David Markowitz
8b56b0b7ba fix: use current binary directory mariadb shared object and dont override env variable (#1669)
* mac stuff

grab correct mariadb file and dont override env variable

fix for unix

Update CMakeLists.txt

unix only

* get that dylib
2024-12-08 00:36:49 -06:00
David Markowitz
4a1c289fb1 fix: avant gardens switches (#1667)
* yep

* remove dumb knockback

idk how the 1 switch in pet cove does it
2024-12-07 19:11:13 -06:00
David Markowitz
32a1e5ece5 update sqlite (#1665) 2024-12-06 15:04:23 -06:00
David Markowitz
7fcbb9507b feat: re-write leaderboards again and fully remove mysql dependency outside of database (#1662)
* simplify leaderboard code, fully abstract database

* update exception catching

* update exception catching and sql references, remove ugc from gamemessages

fix deleting model

remove unrelated changes

Update GameMessages.cpp

* remove ugc from gamemessages

* Update GameMessages.cpp

* Update Leaderboard.cpp

* bug fixes

* fix racing leaderboard

* remove extra stuff

* update
2024-12-06 05:03:47 -06:00
David Markowitz
730533c690 fix: abstract ugc rockets and cars from GameMessages (#1660)
* update exception catching and sql references, remove ugc from gamemessages

fix deleting model

remove unrelated changes

Update GameMessages.cpp

* remove ugc from gamemessages
2024-12-05 20:00:54 -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
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
320 changed files with 175131 additions and 46204 deletions

View File

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

View File

@@ -3,12 +3,20 @@ CLIENT_PATH=./client
# Updates NET_VERSION in CMakeVariables.txt # Updates NET_VERSION in CMakeVariables.txt
NET_VERSION=171022 NET_VERSION=171022
# make sure this is a long random string # make sure this is a long random string
# grab a "SHA 256-bit Key" from here: https://keygen.io/ # generate a "SHA 256-bit Key" from here: https://gchq.github.io/CyberChef/#recipe=Pseudo-Random_Number_Generator(256,'Hex')
ACCOUNT_MANAGER_SECRET= ACCOUNT_MANAGER_SECRET=
# Should be the externally facing IP of your server host # Should be the externally facing IP of your server host
EXTERNAL_IP=localhost EXTERNAL_IP=localhost
# The database type that will be used.
# Acceptable values are `sqlite`, `mysql`, `mariadb`, `maria`.
# Case insensitive.
DATABASE_TYPE=mariadb
SQLITE_DATABASE_PATH=resServer/dlu.sqlite
# Database values # Database values
# Be careful with special characters here. It is more safe to use normal characters and/or numbers. # Be careful with special characters here. It is more safe to use normal characters and/or numbers.
MARIADB_USER=darkflame MARIADB_USER=darkflame
MARIADB_PASSWORD= MARIADB_PASSWORD=
MARIADB_DATABASE=darkflame MARIADB_DATABASE=darkflame
SKIP_ACCOUNT_CREATION=1

View File

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

5
.gitignore vendored
View File

@@ -6,7 +6,6 @@ docker/configs
# Third party libraries # Third party libraries
thirdparty/mysql/ thirdparty/mysql/
thirdparty/mysql_linux/ thirdparty/mysql_linux/
CMakeVariables.txt
# Build folders # Build folders
build/ build/
@@ -95,6 +94,7 @@ ipch/
# Exceptions: # Exceptions:
CMakeSettings.json CMakeSettings.json
CMakeUserPresets.json
*.vcxproj *.vcxproj
*.filters *.filters
*.cmake *.cmake
@@ -122,4 +122,7 @@ docker/__pycache__
docker-compose.override.yml docker-compose.override.yml
!*Test.bin !*Test.bin
# CMake scripts
!cmake/* !cmake/*
!cmake/toolchains/*

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "thirdparty/cpp-httplib"]
path = thirdparty/cpp-httplib
url = https://github.com/yhirose/cpp-httplib
[submodule "thirdparty/tinyxml2"] [submodule "thirdparty/tinyxml2"]
path = thirdparty/tinyxml2 path = thirdparty/tinyxml2
url = https://github.com/leethomason/tinyxml2 url = https://github.com/leethomason/tinyxml2

View File

@@ -1,5 +1,8 @@
cmake_minimum_required(VERSION 3.25) 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 # check if the path to the source directory contains a space
if("${CMAKE_SOURCE_DIR}" MATCHES " ") if("${CMAKE_SOURCE_DIR}" MATCHES " ")
@@ -8,8 +11,10 @@ endif()
include(CTest) include(CTest)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 20) 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_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_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 set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
@@ -61,35 +66,37 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled no-register # Disabled no-register
# Disabled unknown pragmas because Linux doesn't understand Windows pragmas. # Disabled unknown pragmas because Linux doesn't understand Windows pragmas.
if(UNIX) if(UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC") add_link_options("-Wl,-rpath,$ORIGIN/")
add_compile_options("-fPIC")
add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0) add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
if(NOT APPLE) # For all except Clang and Apple Clang
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static-libgcc -lstdc++fs") if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options("-static-libgcc" "-lstdc++fs")
endif() endif()
if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") if(${DYNAMIC} AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic") add_link_options("-export-dynamic")
endif() endif()
if(${GGDB}) if(${GGDB})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") add_compile_options("-ggdb")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC")
elseif(MSVC) elseif(MSVC)
# Skip warning for invalid conversion from size_t to uint32_t for all targets below for now # Skip warning for invalid conversion from size_t to uint32_t for all targets below for now
# Also disable non-portable MSVC volatile behavior # 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) elseif(WIN32)
add_compile_definitions(_CRT_SECURE_NO_WARNINGS) add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif() endif()
# Our output dir # 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 #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 # 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_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
@@ -109,31 +116,39 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
# Create a /logs directory # Create a /logs directory
make_directory(${CMAKE_BINARY_DIR}/logs) 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 # Copy resource files on first build
set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf") set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
message(STATUS "Checking resource file integrity") message(STATUS "Checking resource file integrity")
include(Utils) include(Utils)
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port") UpdateConfigOption(${DLU_CONFIG_DIR}/authconfig.ini "port" "auth_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port") UpdateConfigOption(${DLU_CONFIG_DIR}/chatconfig.ini "port" "chat_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port") UpdateConfigOption(${DLU_CONFIG_DIR}/masterconfig.ini "port" "master_server_port")
foreach(resource_file ${RESOURCE_FILES}) foreach(resource_file ${RESOURCE_FILES})
set(file_size 0) set(file_size 0)
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file}) if(EXISTS ${DLU_CONFIG_DIR}/${resource_file})
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size) file(SIZE ${DLU_CONFIG_DIR}/${resource_file} file_size)
endif() endif()
if(${file_size} EQUAL 0) if(${file_size} EQUAL 0)
configure_file( 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 COPYONLY
) )
message(STATUS "Moved " ${resource_file} " to project binary directory") message(STATUS "Moved " ${resource_file} " to DLU config directory")
elseif(resource_file MATCHES ".ini") elseif(resource_file MATCHES ".ini")
message(STATUS "Checking " ${resource_file} " for missing config options") 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})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents}) string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "") set(parsed_current_file_contents "")
@@ -160,16 +175,18 @@ foreach(resource_file ${RESOURCE_FILES})
list(GET line_split 0 variable_name) list(GET line_split 0 variable_name)
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name}) if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file}) # For backwards compatibility with older setup versions, dont add this option.
set(line_to_add ${line_to_add} ${line}) if(NOT ${variable_name} MATCHES "database_type")
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
set(line_to_add ${line_to_add} ${line})
foreach(line_to_append ${line_to_add}) foreach(line_to_append ${line_to_add})
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append}) file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
endforeach() endforeach()
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n") file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
endif()
endif() endif()
set(line_to_add "") set(line_to_add "")
else() else()
set(line_to_add ${line_to_add} ${line}) set(line_to_add ${line_to_add} ${line})
@@ -199,21 +216,8 @@ foreach(file ${VANITY_FILES})
endforeach() endforeach()
# Move our migrations for MasterServer to run # Move our migrations for MasterServer to run
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/) file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/migrations)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql) file(COPY ${CMAKE_SOURCE_DIR}/migrations DESTINATION ${CMAKE_BINARY_DIR})
foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
endforeach()
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
endforeach()
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
if (APPLE) if (APPLE)
@@ -236,14 +240,16 @@ include_directories(
"tests/dGameTests" "tests/dGameTests"
"tests/dGameTests/dComponentsTests" "tests/dGameTests/dComponentsTests"
SYSTEM "thirdparty/magic_enum/include/magic_enum" SYSTEM
SYSTEM "thirdparty/raknet/Source" "thirdparty/magic_enum/include/magic_enum"
SYSTEM "thirdparty/tinyxml2" "thirdparty/raknet/Source"
SYSTEM "thirdparty/recastnavigation" "thirdparty/tinyxml2"
SYSTEM "thirdparty/SQLite" "thirdparty/recastnavigation"
SYSTEM "thirdparty/cpplinq" "thirdparty/SQLite"
SYSTEM "thirdparty/cpp-httplib" "thirdparty/cpplinq"
SYSTEM "thirdparty/MD5" "thirdparty/MD5"
"thirdparty/nlohmann"
"thirdparty/mongoose"
) )
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
@@ -252,10 +258,17 @@ if(APPLE)
include_directories("/usr/local/include/") include_directories("/usr/local/include/")
endif() endif()
# Add linking directories: # Set warning flags
if (UNIX) if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags # 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() endif()
# Add linking directories:
file( file(
GLOB HEADERS_DZONEMANAGER GLOB HEADERS_DZONEMANAGER
LIST_DIRECTORIES false LIST_DIRECTORIES false
@@ -290,7 +303,7 @@ add_subdirectory(dPhysics)
add_subdirectory(dServer) add_subdirectory(dServer)
# Create a list of common libraries shared between all binaries # Create a list of common libraries shared between all binaries
set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum") set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "magic_enum")
# Add platform specific common libraries # Add platform specific common libraries
if(UNIX) if(UNIX)

View File

@@ -1,128 +1,638 @@
{ {
"version": 3, "version": 6,
"cmakeMinimumRequired": { "cmakeMinimumRequired": {
"major": 3, "major": 3,
"minor": 14, "minor": 25,
"patch": 0 "patch": 0
}, },
"configurePresets": [ "configurePresets": [
{ {
"name": "default", "name": "default",
"displayName": "Default configure step", "displayName": "Default configure step",
"description": "Use 'build' dir and Unix makefiles", "description": "Use 'build' dir and Unix makefiles",
"binaryDir": "${sourceDir}/build", "binaryDir": "${sourceDir}/build",
"generator": "Unix Makefiles" "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
}, },
"output": { {
"outputOnFailure": true "name": "debug-config",
} "hidden": true,
}, "cacheVariables": {
{ "CMAKE_BUILD_TYPE": "Debug"
"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": "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

@@ -1,5 +1,5 @@
PROJECT_VERSION_MAJOR=2 PROJECT_VERSION_MAJOR=3
PROJECT_VERSION_MINOR=3 PROJECT_VERSION_MINOR=0
PROJECT_VERSION_PATCH=0 PROJECT_VERSION_PATCH=0
# Debugging # Debugging

View File

@@ -13,21 +13,35 @@ Darkflame Universe is licensed under AGPLv3, please read [LICENSE](LICENSE). Som
* You must disclose any changes you make to the code when you distribute it * You must disclose any changes you make to the code when you distribute it
* Hosting a server for others counts as distribution * Hosting a server for others counts as distribution
## Disclaimers
### Setup difficulty
Throughout the entire build and setup process a level of familiarity with the command line and preferably a Unix-like development environment is greatly advantageous.
### Hosting a server ### Hosting a server
We do not recommend hosting public servers. Darkflame Universe is intended for small scale deployment, for example within a group of friends. It has not been tested for large scale deployment which comes with additional security risks. We do not recommend hosting public servers. Darkflame Universe is intended for small scale deployment, for example within a group of friends. It has not been tested for large scale deployment which comes with additional security risks.
### Supply of resource files ### Supply of resource files
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work. Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
## Step by step walkthrough for a single-player server ## Setting up a single player server
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README. * If you don't know what WSL is, skip this warning.
Warning: WSL version 1 does NOT support using sqlite as a database due to how it handles filesystem synchronization.
You must use Version 2 if you must run the server under WSL. Not doing so will result in save data loss.
* Single player installs now no longer require building the server from source or installing development tools.
* Download the [latest windows release](https://github.com/DarkflameUniverse/DarkflameServer/releases) (or whichever release you need) and extract the files into a folder inside your client. Note that this setup is expecting that when double clicking the folder that you put in the same folder as `legouniverse.exe`, the file `MasterServer.exe` is in there.
* You should be able to see the folder with the server files in the same folder as `legouniverse.exe`.
* Go into the server files folder and open `sharedconfig.ini`. Find the line that says `client_location` and put `..` after it so the line reads `client_location=..`.
* To run the server, double-click `MasterServer.exe`.
* You will be asked to create an account the first time you run the server. After you have created the account, the server will shutdown and need to be restarted.
* To connect to the server, either delete the file `boot.cfg` which is found in your LEGO Universe client, rename the file `boot.cfg` to something else or follow the steps [here](#allowing-a-user-to-connect-to-your-server) if you wish to keep the file.
* When shutting down the server, it is highly recommended to click the `MasterServer.exe` window and hold `ctrl` while pressing `c` to stop the server.
* We are working on a way to make it so when you close the game, the server stops automatically alongside when you open the game, the server starts automatically.
* If you are not setting a server up on mac, you can ignore this note
* Note: you'll need to allow through System Preferences `AuthServer`, `ChatServer`, `MasterServer`, `WorldServer` and `libmariadbcpp.dylib` to run. The initial pop-up will block it due to the binaries being unsigned, after allowing them to run the servers will run as normal.
## Steps to setup server <font size="32">**If you are not planning on hosting a server for others, working in the codebase or wanting to use MariaDB for a database, you can stop reading here.**</font>
If you would like to use a MariaDB as a database instead of the default of sqlite, follow the steps [here](#database-setup).
# Steps to setup a development environment
* [Clone this repository](#clone-the-repository) * [Clone this repository](#clone-the-repository)
* [Setting up a development environment](#setting-up-a-development-environment)
* [Install dependencies](#install-dependencies) * [Install dependencies](#install-dependencies)
* [Database setup](#database-setup) * [Database setup](#database-setup)
* [Build the server](#build-the-server) * [Build the server](#build-the-server)
@@ -39,6 +53,13 @@ If you would like a setup for a single player server only on a Windows machine,
* [User Guide](#user-guide) * [User Guide](#user-guide)
* [Docker](#docker) * [Docker](#docker)
## Disclaimers
### Setup difficulty
Throughout the entire build and setup process a level of familiarity with the command line and preferably a Unix-like development environment is greatly advantageous.
## Step by step walkthrough for building a single-player Windows server from source
If you would like a setup for a single player server only on a Windows machine built from source, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
## Clone the repository ## Clone the repository
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win) If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)
@@ -49,6 +70,12 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
## Install dependencies ## Install dependencies
### Required compiler versions
- g++11 or greater
- MSVC unchecked
- clang unchecked
- appleclang unchecked
### Windows packages ### Windows packages
Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed. Ensure that you have either the [MSVC C++ compiler](https://visualstudio.microsoft.com/vs/features/cplusplus/) (recommended) or the [Clang compiler](https://github.com/llvm/llvm-project/releases/) installed.
You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!). You'll also need to download and install [CMake](https://cmake.org/download/) (version <font size="4">**CMake version 3.25**</font> or later!).
@@ -183,6 +210,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes ### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building: Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory. * If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* By default it should be set to the correct directory.
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023. * If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
## Configuring your server ## Configuring your server
@@ -205,28 +233,41 @@ Navigate to `build/sharedconfig.ini` and fill in the following fields:
* `chatconfig.ini` contains a port option. * `chatconfig.ini` contains a port option.
* `masterconfig.ini` contains options related to permissions you want to run your servers with. * `masterconfig.ini` contains options related to permissions you want to run your servers with.
* `sharedconfig.ini` contains several options that are shared across all servers * `sharedconfig.ini` contains several options that are shared across all servers
* `worldconfig.ini` contains several options to turn on QOL improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off. * `worldconfig.ini` contains several options to turn on Quality of Life improvements should you want them. If you would like the most vanilla experience possible, you will need to turn some of these settings off.
## Verify your setup ## Verify your setup
Your build directory should now look like this: Your build directory should contain at a minimum all of the following files.
* AuthServer All listed files are required for a server to start.
* ChatServer `ini` files can be located at the environment variable `DLU_CONFIG_DIR` and do not need to be located in this directory.
* MasterServer (windows will have .exe at the end of the executables):
* WorldServer
* authconfig.ini
* chatconfig.ini
* masterconfig.ini
* sharedconfig.ini * sharedconfig.ini
* AuthServer(.exe)
* authconfig.ini
* ChatServer(.exe)
* chatconfig.ini
* MasterServer(.exe)
* masterconfig.ini
* WorldServer(.exe)
* worldconfig.ini * worldconfig.ini
* ... * blocklist.dcf
* migrations
* vanity
* navmeshes
* 1 of the following lists based on platform
* windows
* libmariadb.dll
* mariadbcpp.dll
* zlib.dll
* MacOS
* libmariadbcpp.dylib
* *nix
* libmariadbcpp.so
## Running the server ## Running the server
If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you either have to give the `AuthServer` binary network permissions or run it under sudo. If everything has been configured correctly you should now be able to run the `MasterServer` binary which is located in the `build` directory. Darkflame Universe utilizes port numbers under 1024, so under Linux you have to give the `AuthServer` binary network permissions by running the following command:
To give `AuthServer` network permissions and not require sudo, run the following command
```bash ```bash
sudo setcap 'cap_net_bind_service=+ep' AuthServer sudo setcap 'cap_net_bind_service=+ep' AuthServer
``` ```
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
### Linux Service ### Linux Service
If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server. If you are running this on a linux based system, it will use your terminal to run the program interactively, preventing you using it for other tasks and requiring it to be open to run the server.
@@ -266,8 +307,8 @@ systemctl stop darkflame.service
journalctl -xeu darkflame.service journalctl -xeu darkflame.service
``` ```
### First admin user ### First user or adding more users.
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users! The first time you run `MasterServer`, you will be prompted to create an account. To create more accounts from the command line, `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
### Account management tool (Nexus Dashboard) ### Account management tool (Nexus Dashboard)
**If you are just using this server for yourself, you can skip setting up Nexus Dashboard** **If you are just using this server for yourself, you can skip setting up Nexus Dashboard**
@@ -289,8 +330,14 @@ To connect to a server follow these steps:
* Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers * Replace the contents after to `:` and the following `,` with what you configured as the server's public facing IP. For example `AUTHSERVERIP=0:localhost` for locally hosted servers
* Next locate the line `UGCUSE3DSERVICES=7:` * Next locate the line `UGCUSE3DSERVICES=7:`
* Ensure the number after the 7 is a `0` * Ensure the number after the 7 is a `0`
* Alternatively, remove the line with `UGCUSE3DSERVICES` altogether
* Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system * Launch `legouniverse.exe`, through `wine` if on a Unix-like operating system
* Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users. * Note that if you are on WSL2, you will need to configure the public IP in the server and client to be the IP of the WSL2 instance and not localhost, which can be found by running `ifconfig` in the terminal. Windows defaults to WSL1, so this will not apply to most users.
As an example, here is what the boot.cfg is required to contain for a server with the ip 12.34.56.78
```cfg
AUTHSERVERIP=0:12.34.56.78,
UGCUSE3DSERVICES=7:0
```
## Updating your server ## Updating your server
To update your server to the latest version navigate to your cloned directory To update your server to the latest version navigate to your cloned directory
@@ -307,6 +354,10 @@ Now follow the [build](#build-the-server) section for your system and your serve
## In-game commands ## In-game commands
* A list of all in-game commands can be found [here](./docs/Commands.md). * A list of all in-game commands can be found [here](./docs/Commands.md).
## Chat Web API
* The Chat server has an API that can be enabled via `chatconfig.ini`.
* You can view the OpenAPI doc for the API here [here](./docs/ChatWebAPI.yaml).
## Verifying your client files ## Verifying your client files
### LEGO® Universe 1.10.64 ### LEGO® Universe 1.10.64
@@ -371,7 +422,7 @@ at once. For that:
- Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env` - Download the [.env.example](.env.example) file and place it next to `client` with the file name `.env`
- You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows). - You may get warnings that this name starts with a dot, acknowledge those, this is intentional. Depending on your operating system, you may need to activate showing hidden files (e.g. Ctrl-H in Gnome on Linux) and/or file extensions ("File name extensions" in the "View" tab on Windows).
- Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords. - Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords.
- Use a password generator like <https://keygen.io> - Use a password generator <https://gchq.github.io/CyberChef/#recipe=Pseudo-Random_Number_Generator(256,'Hex')>
- Avoid `:` and `@` characters - Avoid `:` and `@` characters
- Once the database user is created, changing the password will not update it, so the server will just fail to connect. - Once the database user is created, changing the password will not update it, so the server will just fail to connect.
- Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family - Set `EXTERNAL_IP` to your LAN IP or public IP if you want to host the game for friends & family

View File

@@ -6,8 +6,7 @@ mkdir -p build
cd build cd build
# Run cmake to generate make files # 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' # 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 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,13 +21,12 @@
//Auth includes: //Auth includes:
#include "AuthPackets.h" #include "AuthPackets.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eServerMessageType.h" #include "MessageType/Server.h"
#include "eAuthMessageType.h" #include "MessageType/Auth.h"
#include "Game.h" #include "Game.h"
#include "Server.h" #include "Server.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
@@ -60,7 +59,7 @@ int main(int argc, char** argv) {
try { try {
Database::Connect(); Database::Connect();
} catch (sql::SQLException& ex) { } catch (std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
@@ -102,7 +101,7 @@ int main(int argc, char** argv) {
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
AuthPackets::LoadClaimCodes(); AuthPackets::LoadClaimCodes();
Game::logger->Flush(); // once immediately before main loop Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) { while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
@@ -166,11 +165,11 @@ void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_USER_PACKET_ENUM) { if (packet->data[0] == ID_USER_PACKET_ENUM) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::SERVER) { 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); AuthPackets::HandleHandshake(Game::server, packet);
} }
} else if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::AUTH) { } 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); AuthPackets::HandleLoginRequest(Game::server, packet);
} }
} }

View File

@@ -1,7 +1,4 @@
add_executable(AuthServer "AuthServer.cpp") add_executable(AuthServer "AuthServer.cpp")
target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer) target_link_libraries(AuthServer ${COMMON_LIBRARIES} dServer)
target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer) target_include_directories(AuthServer PRIVATE ${PROJECT_SOURCE_DIR}/dServer)
add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") add_compile_definitions(AuthServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")

View File

@@ -2,6 +2,9 @@ set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp" "ChatIgnoreList.cpp"
"ChatPacketHandler.cpp" "ChatPacketHandler.cpp"
"PlayerContainer.cpp" "PlayerContainer.cpp"
"ChatWebAPI.cpp"
"JSONUtils.cpp"
"CSRCommandHandler.cpp"
) )
add_executable(ChatServer "ChatServer.cpp") add_executable(ChatServer "ChatServer.cpp")
@@ -12,5 +15,5 @@ add_library(dChatServer ${DCHATSERVER_SOURCES})
target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer") target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer")
target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter)
target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer mongoose)

View File

@@ -0,0 +1,353 @@
#include "CSRCommandHandler.h"
#include "eCSRCommand.h"
#include "eHTTPStatusCode.h"
#include "JSONUtils.h"
#include "magic_enum.hpp"
#include "json.hpp"
void CSRCommandHandler::HandleCSRCommand(HTTPReply& reply, const json& data) {
auto check = JSONUtils::CheckRequiredData(data, { "command" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
// get command from json
std::string command_string = data["command"];
// upper
std::transform(command_string.begin(), command_string.end(), command_string.begin(), ::toupper);
eCSRCommand command = magic_enum::enum_cast<eCSRCommand>(command_string).value_or(eCSRCommand::INVALID);
switch (command) {
case eCSRCommand::QUERY_SERVER_STATUS:
QueryServerStatus(reply, data);
break;
case eCSRCommand::QUERY_CHARACTER_LOCATION:
QueryCharacterLocation(reply, data);
break;
case eCSRCommand::QUERY_CHARACTER_ONLINE_STATUS:
QueryCharacterOnlineStatus(reply, data);
break;
case eCSRCommand::INVENTORY_ADD_ITEM:
InventoryAddItem(reply, data);
break;
case eCSRCommand::INVENTORY_DELETE_ITEM:
InventoryDeleteItem(reply, data);
break;
case eCSRCommand::MODERATE_MUTE_ACCOUNT:
ModerateMuteAccount(reply, data);
break;
case eCSRCommand::MODERATE_BAN_ACCOUNT:
ModerateBanAccount(reply, data);
break;
case eCSRCommand::MODERATE_EDUCATE_CHARACTER:
ModerateEducateCharacter(reply, data);
break;
case eCSRCommand::MODERATE_KICK_CHARACTER:
ModerateKickCharacter(reply, data);
break;
case eCSRCommand::MODERATE_WARN_CHARACTER:
ModerateWarnCharacter(reply, data);
break;
case eCSRCommand::MODERATE_RENAME_CHARACTER:
ModerateRenameCharacter(reply, data);
break;
case eCSRCommand::MODERATE_DELETE_CHARACTER_FRIEND:
ModerateDeleteCharacterFriend(reply, data);
break;
case eCSRCommand::MODERATE_KILL_CHARACTER:
ModerateKillCharacter(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_HEALTH:
UpdateCharacterHealth(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_ARMOR:
UpdateCharacterArmor(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_IMAGINATION:
UpdateCharacterImagination(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_HEALTH:
UpdateCharacterMaxHealth(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_ARMOR:
UpdateCharacterMaxArmor(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_MAX_IMAGINATION:
UpdateCharacterMaxImagination(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_CURRENCY:
UpdateCharacterCurrency(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_REPUTATION:
UpdateCharacterReputation(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_LEGO_SCORE:
UpdateCharacterLegoScore(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_EMOTES:
UpdateCharacterEmotes(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_ADD_ACHIEVEMENT:
UpdateCharacterAddAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT:
UpdateCharacterCompleteAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_REMOVE_ACHIEVEMENT:
UpdateCharacterRemoveAchievement(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_POSITION_OFFLINE:
UpdateCharacterPositionOffline(reply, data);
break;
case eCSRCommand::UPDATE_CHARACTER_INV_SLOT_AMOUNT:
UpdateCharacterInvSlotAmount(reply, data);
break;
case eCSRCommand::UTILITY_SAVE_CHARACTER:
UtilitySaveCharacter(reply, data);
break;
case eCSRCommand::UTILITY_SEND_MAIL:
UtilitySendMail(reply, data);
break;
case eCSRCommand::UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE:
UtilityGiveItemToAllPlayersOnline(reply, data);
break;
case eCSRCommand::METRICS_CONFIGURE:
MetricsConfigure(reply, data);
break;
case eCSRCommand::DISABLE_ZONE:
DisableZone(reply, data);
break;
case eCSRCommand::INIT_DONATION_AMOUNT:
InitDonationAmount(reply, data);
break;
case eCSRCommand::KILL_SERVERS_COUNTDOWN:
KillServersCountdown(reply, data);
break;
case eCSRCommand::DISABLE_FAQ:
DisableFAQ(reply, data);
break;
case eCSRCommand::THROTTLEQUEUE:
ThrottleQueue(reply, data);
break;
case eCSRCommand::GATEGM_ACCESS:
GateGMAccess(reply, data);
break;
case eCSRCommand::RECONNECT_CRISP:
ReconnectCrisp(reply, data);
break;
case eCSRCommand::MODERATE_KICK_ACCOUNT:
ModerateKickAccount(reply, data);
break;
case eCSRCommand::TOGGLE_CRISP_SERVER:
ToggleCrispServer(reply, data);
break;
case eCSRCommand::QUICK_DRAIN_SERVER:
QuickDrainServer(reply, data);
break;
case eCSRCommand::QUICK_DRAIN_SERVER_RENEW:
QuickDrainServerRenew(reply, data);
break;
case eCSRCommand::REPLICATE_CHARACTER:
ReplicateCharacter(reply, data);
break;
case eCSRCommand::GET_SERVER_STATUS:
GetServerStatus(reply);
break;
case eCSRCommand::RELOAD_SERVER_INIS:
ReloadServerINIs(reply);
break;
case eCSRCommand::INVALID:
default:
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid Command\"}";
break;
}
}
}
void CSRCommandHandler::QueryServerStatus(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QueryCharacterLocation(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QueryCharacterOnlineStatus(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InventoryAddItem(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InventoryDeleteItem(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateMuteAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateBanAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateEducateCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKickCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateWarnCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateRenameCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKillCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterHealth(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterArmor(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterImagination(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxHealth(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxArmor(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterMaxImagination(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterCurrency(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterReputation(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterLegoScore(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterEmotes(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterAddAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterPositionOffline(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilitySaveCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilitySendMail(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::MetricsConfigure(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::DisableZone(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::InitDonationAmount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::KillServersCountdown(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::DisableFAQ(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ThrottleQueue(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::GateGMAccess(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReconnectCrisp(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ModerateKickAccount(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ToggleCrispServer(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QuickDrainServer(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::QuickDrainServerRenew(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReplicateCharacter(HTTPReply& reply, const json& data) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::GetServerStatus(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}
void CSRCommandHandler::ReloadServerINIs(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_IMPLEMENTED;
reply.message = "{\"error\":\"Not Implemented\"}";
}

View File

@@ -0,0 +1,62 @@
#ifndef __CSR_COMMAND_HANDLER__H__
#define __CSR_COMMAND_HANDLER__H__
#include <optional>
#include "json_fwd.hpp"
using json = nlohmann::json;
struct HTTPReply;
namespace CSRCommandHandler {
void HandleCSRCommand(HTTPReply& reply, const json& data);
void QueryServerStatus(HTTPReply& reply, const json& data);
void QueryCharacterLocation(HTTPReply& reply, const json& data);
void QueryCharacterOnlineStatus(HTTPReply& reply, const json& data);
void InventoryAddItem(HTTPReply& reply, const json& data);
void InventoryDeleteItem(HTTPReply& reply, const json& data);
void ModerateMuteAccount(HTTPReply& reply, const json& data);
void ModerateBanAccount(HTTPReply& reply, const json& data);
void ModerateEducateCharacter(HTTPReply& reply, const json& data);
void ModerateKickCharacter(HTTPReply& reply, const json& data);
void ModerateWarnCharacter(HTTPReply& reply, const json& data);
void ModerateRenameCharacter(HTTPReply& reply, const json& data);
void ModerateDeleteCharacterFriend(HTTPReply& reply, const json& data);
void ModerateKillCharacter(HTTPReply& reply, const json& data);
void UpdateCharacterHealth(HTTPReply& reply, const json& data);
void UpdateCharacterArmor(HTTPReply& reply, const json& data);
void UpdateCharacterImagination(HTTPReply& reply, const json& data);
void UpdateCharacterMaxHealth(HTTPReply& reply, const json& data);
void UpdateCharacterMaxArmor(HTTPReply& reply, const json& data);
void UpdateCharacterMaxImagination(HTTPReply& reply, const json& data);
void UpdateCharacterCurrency(HTTPReply& reply, const json& data);
void UpdateCharacterReputation(HTTPReply& reply, const json& data);
void UpdateCharacterLegoScore(HTTPReply& reply, const json& data);
void UpdateCharacterEmotes(HTTPReply& reply, const json& data);
void UpdateCharacterAddAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterCompleteAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterRemoveAchievement(HTTPReply& reply, const json& data);
void UpdateCharacterPositionOffline(HTTPReply& reply, const json& data);
void UpdateCharacterInvSlotAmount(HTTPReply& reply, const json& data);
void UtilitySaveCharacter(HTTPReply& reply, const json& data);
void UtilitySendMail(HTTPReply& reply, const json& data);
void UtilityGiveItemToAllPlayersOnline(HTTPReply& reply, const json& data);
void MetricsConfigure(HTTPReply& reply, const json& data);
void DisableZone(HTTPReply& reply, const json& data);
void InitDonationAmount(HTTPReply& reply, const json& data);
void KillServersCountdown(HTTPReply& reply, const json& data);
void DisableFAQ(HTTPReply& reply, const json& data);
void ThrottleQueue(HTTPReply& reply, const json& data);
void GateGMAccess(HTTPReply& reply, const json& data);
void ReconnectCrisp(HTTPReply& reply, const json& data);
void ModerateKickAccount(HTTPReply& reply, const json& data);
void ToggleCrispServer(HTTPReply& reply, const json& data);
void QuickDrainServer(HTTPReply& reply, const json& data);
void QuickDrainServerRenew(HTTPReply& reply, const json& data);
void ReplicateCharacter(HTTPReply& reply, const json& data);
void GetServerStatus(HTTPReply& reply);
void ReloadServerINIs(HTTPReply& reply);
};
#endif // !__CSR_COMMAND_HANDLER__H__

View File

@@ -1,6 +1,6 @@
#include "ChatIgnoreList.h" #include "ChatIgnoreList.h"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "eChatMessageType.h" #include "MessageType/Chat.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
@@ -13,7 +13,7 @@
// The only thing not auto-handled is instance activities force joining the team on the server. // 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) { 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); bitStream.Write(receivingPlayer);
//portion that will get routed: //portion that will get routed:

View File

@@ -13,9 +13,9 @@
#include "dConfig.h" #include "dConfig.h"
#include "eObjectBits.h" #include "eObjectBits.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatMessageType.h" #include "MessageType/Chat.h"
#include "eClientMessageType.h" #include "MessageType/Client.h"
#include "eGameMessageType.h" #include "MessageType/Game.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "ChatPackets.h" #include "ChatPackets.h"
@@ -29,42 +29,44 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
auto& player = Game::playerContainer.GetPlayerDataMutable(playerID); auto& player = Game::playerContainer.GetPlayerDataMutable(playerID);
if (!player) return; if (!player) return;
auto friendsList = Database::Get()->GetFriendsList(playerID); if (player.friends.empty()) {
for (const auto& friendData : friendsList) { auto friendsList = Database::Get()->GetFriendsList(playerID);
FriendData fd; for (const auto& friendData : friendsList) {
fd.isFTP = false; // not a thing in DLU FriendData fd;
fd.friendID = friendData.friendID; fd.isFTP = false; // not a thing in DLU
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT); fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER); GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player.countOfBestFriends += 1; if (fd.isBestFriend) player.countOfBestFriends += 1;
fd.friendName = friendData.friendName; fd.friendName = friendData.friendName;
//Now check if they're online: //Now check if they're online:
const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID); const auto& fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) { if (fr) {
fd.isOnline = true; fd.isOnline = true;
fd.zoneID = fr.zoneID; fd.zoneID = fr.zoneID;
//Since this friend is online, we need to update them on the fact that we've just logged in: //Since this friend is online, we need to update them on the fact that we've just logged in:
SendFriendUpdate(fr, player, 1, fd.isBestFriend); SendFriendUpdate(fr, player, 1, fd.isBestFriend);
} else { } else {
fd.isOnline = false; fd.isOnline = false;
fd.zoneID = LWOZONEID(); fd.zoneID = LWOZONEID();
}
player.friends.push_back(fd);
} }
player.friends.push_back(fd);
} }
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(playerID); bitStream.Write(playerID);
//portion that will get routed: //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<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>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write<uint16_t>(player.friends.size()); bitStream.Write<uint16_t>(player.friends.size());
@@ -140,7 +142,7 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Prevent GM friend spam // Prevent GM friend spam
// If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process // If the player we are trying to be friends with is not a civilian and we are a civilian, abort the process
if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN ) { if (requestee.gmLevel > eGameMasterLevel::CIVILIAN && requestor.gmLevel == eGameMasterLevel::CIVILIAN) {
SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN); SendFriendResponse(requestor, requestee, eAddFriendResponseType::MYTHRAN);
return; return;
} }
@@ -368,10 +370,10 @@ void ChatPacketHandler::HandleWho(Packet* packet) {
bool online = player; bool online = player;
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor); 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<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID()); bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID()); bitStream.Write(player.zoneID.GetInstanceID());
@@ -391,17 +393,17 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
if (!sender) return; if (!sender) return;
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor); 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<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount()); bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount()); bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers); bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData); bitStream.Write<uint8_t>(request.displayZoneData);
if (request.displayZoneData || request.displayIndividualPlayers){ if (request.displayZoneData || request.displayIndividualPlayers) {
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){ for (auto& [playerID, playerData] : Game::playerContainer.GetAllPlayers()) {
if (!playerData) continue; if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName)); if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
@@ -416,7 +418,7 @@ void ChatPacketHandler::HandleShowAll(Packet* packet) {
SEND_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 // that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandleChatMessage(Packet* packet) { void ChatPacketHandler::HandleChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
@@ -428,7 +430,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
eChatChannel channel; eChatChannel channel;
uint32_t size; uint32_t size;
inStream.IgnoreBytes(4); inStream.IgnoreBytes(4);
inStream.Read(channel); inStream.Read(channel);
inStream.Read(size); inStream.Read(size);
@@ -436,7 +438,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
LUWString message(size); LUWString message(size);
inStream.Read(message); inStream.Read(message);
LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str()); LOG("Got a message from (%s) via [%s]: %s", sender.playerName.c_str(), StringifiedEnum::ToString(channel).data(), message.GetAsString().c_str());
switch (channel) { switch (channel) {
@@ -457,7 +459,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 // that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
@@ -484,7 +486,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
LUWString message(size); LUWString message(size);
inStream.Read(message); 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()); 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); const auto& receiver = Game::playerContainer.GetPlayerData(receiverName);
@@ -515,10 +517,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) { void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(routeTo.playerID); 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(sender.playerID);
bitStream.Write(channel); bitStream.Write(channel);
bitStream.Write<uint32_t>(0); // not used bitStream.Write<uint32_t>(0); // not used
@@ -757,11 +759,11 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) { void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //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(LUWString(sender.playerName.c_str()));
bitStream.Write(sender.playerID); bitStream.Write(sender.playerID);
@@ -772,14 +774,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) { 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; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_INVITE_CONFIRM); bitStream.Write(MessageType::Game::TEAM_INVITE_CONFIRM);
bitStream.Write(bLeaderIsFreeTrial); bitStream.Write(bLeaderIsFreeTrial);
bitStream.Write(i64LeaderID); bitStream.Write(i64LeaderID);
@@ -799,14 +801,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) { void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_GET_STATUS_RESPONSE); bitStream.Write(MessageType::Game::TEAM_GET_STATUS_RESPONSE);
bitStream.Write(i64LeaderID); bitStream.Write(i64LeaderID);
bitStream.Write(i64LeaderZoneID); bitStream.Write(i64LeaderZoneID);
@@ -824,14 +826,14 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) { void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_SET_LEADER); bitStream.Write(MessageType::Game::TEAM_SET_LEADER);
bitStream.Write(i64PlayerID); bitStream.Write(i64PlayerID);
@@ -841,14 +843,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) { void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_ADD_PLAYER); bitStream.Write(MessageType::Game::TEAM_ADD_PLAYER);
bitStream.Write(bIsFreeTrial); bitStream.Write(bIsFreeTrial);
bitStream.Write(bLocal); bitStream.Write(bLocal);
@@ -870,14 +872,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) { void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_REMOVE_PLAYER); bitStream.Write(MessageType::Game::TEAM_REMOVE_PLAYER);
bitStream.Write(bDisband); bitStream.Write(bDisband);
bitStream.Write(bIsKicked); bitStream.Write(bIsKicked);
@@ -896,14 +898,14 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
CMSGHEADER; CMSGHEADER;
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
bitStream.Write(eGameMessageType::TEAM_SET_OFF_WORLD_FLAG); bitStream.Write(MessageType::Game::TEAM_SET_OFF_WORLD_FLAG);
bitStream.Write(i64PlayerID); bitStream.Write(i64PlayerID);
if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) { if (receiver.zoneID.GetCloneID() == zoneID.GetCloneID()) {
@@ -930,11 +932,11 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is FTP*/ [bool] - is FTP*/
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(friendData.playerID); bitStream.Write(friendData.playerID);
//portion that will get routed: //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); bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData.playerName.c_str(); std::string playerName = playerData.playerName.c_str();
@@ -967,11 +969,11 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
} }
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //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(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. 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 +983,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) { void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
// Portion that will get routed: // 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); bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver. // 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); bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender.sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
@@ -1004,11 +1006,11 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) { void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //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<uint8_t>(isSuccessful); //isOnline
bitStream.Write(LUWString(personToRemove)); bitStream.Write(LUWString(personToRemove));

View File

@@ -16,8 +16,8 @@
#include "eConnectionType.h" #include "eConnectionType.h"
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "eChatMessageType.h" #include "MessageType/Chat.h"
#include "eWorldMessageType.h" #include "MessageType/World.h"
#include "ChatIgnoreList.h" #include "ChatIgnoreList.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
@@ -28,6 +28,8 @@
#include "RakNetDefines.h" #include "RakNetDefines.h"
#include "MessageIdentifiers.h" #include "MessageIdentifiers.h"
#include "ChatWebAPI.h"
namespace Game { namespace Game {
Logger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
@@ -81,7 +83,7 @@ int main(int argc, char** argv) {
//Connect to the MySQL Database //Connect to the MySQL Database
try { try {
Database::Connect(); Database::Connect();
} catch (sql::SQLException& ex) { } catch (std::exception& ex) {
LOG("Got an error while connecting to the database: %s", ex.what()); LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
@@ -108,7 +110,7 @@ int main(int argc, char** argv) {
const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false); 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::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::randomEngine = std::mt19937(time(0)); Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize(); Game::playerContainer.Initialize();
@@ -122,6 +124,13 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
bool web_server_enabled = Game::config->GetValue("web_server_enabled") == "1";
ChatWebAPI chatwebapi;
if (web_server_enabled) chatwebapi.Listen();
auto lastTime = std::chrono::high_resolution_clock::now();
Game::logger->Flush(); // once immediately before main loop Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) { while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
@@ -132,7 +141,11 @@ int main(int argc, char** argv) {
break; //Exit our loop, shut down. break; //Exit our loop, shut down.
} else framesSinceMasterDisconnect = 0; } else framesSinceMasterDisconnect = 0;
//In world we'd update our other systems here. const auto currentTime = std::chrono::high_resolution_clock::now();
const float deltaTime = std::chrono::duration<float>(currentTime - lastTime).count();
lastTime = currentTime;
Game::playerContainer.Update(deltaTime);
//Check for packets here: //Check for packets here:
Game::server->ReceiveFromMaster(); //ReceiveFromMaster also handles the master packets if needed. Game::server->ReceiveFromMaster(); //ReceiveFromMaster also handles the master packets if needed.
@@ -143,6 +156,11 @@ int main(int argc, char** argv) {
packet = nullptr; packet = nullptr;
} }
//Check and handle web requests:
if (web_server_enabled) {
chatwebapi.ReceiveRequests();
}
//Push our log every 30s: //Push our log every 30s:
if (framesSinceLastFlush >= logFlushTime) { if (framesSinceLastFlush >= logFlushTime) {
Game::logger->Flush(); Game::logger->Flush();
@@ -168,7 +186,7 @@ int main(int argc, char** argv) {
t += std::chrono::milliseconds(chatFrameDelta); //Chat can run at a lower "fps" t += std::chrono::milliseconds(chatFrameDelta); //Chat can run at a lower "fps"
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
Game::playerContainer.Shutdown();
//Delete our objects here: //Delete our objects here:
Database::Destroy("ChatServer"); Database::Destroy("ChatServer");
delete Game::server; delete Game::server;
@@ -190,157 +208,156 @@ void HandlePacket(Packet* packet) {
inStream.SetReadOffset(BYTES_TO_BITS(1)); inStream.SetReadOffset(BYTES_TO_BITS(1));
eConnectionType connection; eConnectionType connection;
eChatMessageType chatMessageID; MessageType::Chat chatMessageID;
inStream.Read(connection); inStream.Read(connection);
if (connection != eConnectionType::CHAT) return; if (connection != eConnectionType::CHAT) return;
inStream.Read(chatMessageID); inStream.Read(chatMessageID);
switch (chatMessageID) { switch (chatMessageID) {
case eChatMessageType::GM_MUTE: case MessageType::Chat::GM_MUTE:
Game::playerContainer.MuteUpdate(packet); Game::playerContainer.MuteUpdate(packet);
break; break;
case eChatMessageType::CREATE_TEAM: case MessageType::Chat::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet); Game::playerContainer.CreateTeamServer(packet);
break; break;
case eChatMessageType::GET_FRIENDS_LIST: case MessageType::Chat::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet); ChatPacketHandler::HandleFriendlistRequest(packet);
break; break;
case eChatMessageType::GET_IGNORE_LIST: case MessageType::Chat::GET_IGNORE_LIST:
ChatIgnoreList::GetIgnoreList(packet); ChatIgnoreList::GetIgnoreList(packet);
break; break;
case eChatMessageType::ADD_IGNORE: case MessageType::Chat::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet); ChatIgnoreList::AddIgnore(packet);
break; break;
case eChatMessageType::REMOVE_IGNORE: case MessageType::Chat::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet); ChatIgnoreList::RemoveIgnore(packet);
break; break;
case eChatMessageType::TEAM_GET_STATUS: case MessageType::Chat::TEAM_GET_STATUS:
ChatPacketHandler::HandleTeamStatusRequest(packet); ChatPacketHandler::HandleTeamStatusRequest(packet);
break; 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. //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. //We basically just check to see if this player is online or not and route the packet.
ChatPacketHandler::HandleFriendRequest(packet); ChatPacketHandler::HandleFriendRequest(packet);
break; 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. //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. //Here, we'll actually have to add them to eachother's friend lists depending on the response code.
ChatPacketHandler::HandleFriendResponse(packet); ChatPacketHandler::HandleFriendResponse(packet);
break; break;
case eChatMessageType::REMOVE_FRIEND: case MessageType::Chat::REMOVE_FRIEND:
ChatPacketHandler::HandleRemoveFriend(packet); ChatPacketHandler::HandleRemoveFriend(packet);
break; break;
case eChatMessageType::GENERAL_CHAT_MESSAGE: case MessageType::Chat::GENERAL_CHAT_MESSAGE:
ChatPacketHandler::HandleChatMessage(packet); ChatPacketHandler::HandleChatMessage(packet);
break; 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 //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. //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); ChatPacketHandler::HandlePrivateChatMessage(packet);
break; break;
case eChatMessageType::TEAM_INVITE: case MessageType::Chat::TEAM_INVITE:
ChatPacketHandler::HandleTeamInvite(packet); ChatPacketHandler::HandleTeamInvite(packet);
break; break;
case eChatMessageType::TEAM_INVITE_RESPONSE: case MessageType::Chat::TEAM_INVITE_RESPONSE:
ChatPacketHandler::HandleTeamInviteResponse(packet); ChatPacketHandler::HandleTeamInviteResponse(packet);
break; break;
case eChatMessageType::TEAM_LEAVE: case MessageType::Chat::TEAM_LEAVE:
ChatPacketHandler::HandleTeamLeave(packet); ChatPacketHandler::HandleTeamLeave(packet);
break; break;
case eChatMessageType::TEAM_SET_LEADER: case MessageType::Chat::TEAM_SET_LEADER:
ChatPacketHandler::HandleTeamPromote(packet); ChatPacketHandler::HandleTeamPromote(packet);
break; break;
case eChatMessageType::TEAM_KICK: case MessageType::Chat::TEAM_KICK:
ChatPacketHandler::HandleTeamKick(packet); ChatPacketHandler::HandleTeamKick(packet);
break; break;
case eChatMessageType::TEAM_SET_LOOT: case MessageType::Chat::TEAM_SET_LOOT:
ChatPacketHandler::HandleTeamLootOption(packet); ChatPacketHandler::HandleTeamLootOption(packet);
break; break;
case eChatMessageType::GMLEVEL_UPDATE: case MessageType::Chat::GMLEVEL_UPDATE:
ChatPacketHandler::HandleGMLevelUpdate(packet); ChatPacketHandler::HandleGMLevelUpdate(packet);
break; break;
case eChatMessageType::LOGIN_SESSION_NOTIFY: case MessageType::Chat::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet); Game::playerContainer.InsertPlayer(packet);
break; break;
case eChatMessageType::GM_ANNOUNCE:{ case MessageType::Chat::GM_ANNOUNCE:
// we just forward this packet to every connected server // we just forward this packet to every connected server
inStream.ResetReadPointer(); inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
} break;
break; case MessageType::Chat::UNEXPECTED_DISCONNECT:
case eChatMessageType::UNEXPECTED_DISCONNECT: Game::playerContainer.ScheduleRemovePlayer(packet);
Game::playerContainer.RemovePlayer(packet); break;
break; case MessageType::Chat::WHO:
case eChatMessageType::WHO: ChatPacketHandler::HandleWho(packet);
ChatPacketHandler::HandleWho(packet); break;
break; case MessageType::Chat::SHOW_ALL:
case eChatMessageType::SHOW_ALL: ChatPacketHandler::HandleShowAll(packet);
ChatPacketHandler::HandleShowAll(packet); break;
break; case MessageType::Chat::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE: case MessageType::Chat::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_DISCONNECT_REQUEST: case MessageType::Chat::WORLD_PROXIMITY_RESPONSE:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE: case MessageType::Chat::WORLD_PARCEL_RESPONSE:
case eChatMessageType::WORLD_PARCEL_RESPONSE: case MessageType::Chat::TEAM_MISSED_INVITE_CHECK:
case eChatMessageType::TEAM_MISSED_INVITE_CHECK: case MessageType::Chat::GUILD_CREATE:
case eChatMessageType::GUILD_CREATE: case MessageType::Chat::GUILD_INVITE:
case eChatMessageType::GUILD_INVITE: case MessageType::Chat::GUILD_INVITE_RESPONSE:
case eChatMessageType::GUILD_INVITE_RESPONSE: case MessageType::Chat::GUILD_LEAVE:
case eChatMessageType::GUILD_LEAVE: case MessageType::Chat::GUILD_KICK:
case eChatMessageType::GUILD_KICK: case MessageType::Chat::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_STATUS: case MessageType::Chat::GUILD_GET_ALL:
case eChatMessageType::GUILD_GET_ALL: case MessageType::Chat::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODERATED: case MessageType::Chat::BLUEPRINT_MODEL_READY:
case eChatMessageType::BLUEPRINT_MODEL_READY: case MessageType::Chat::PROPERTY_READY_FOR_APPROVAL:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL: case MessageType::Chat::PROPERTY_MODERATION_CHANGED:
case eChatMessageType::PROPERTY_MODERATION_CHANGED: case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED: case MessageType::Chat::PROPERTY_BUILDMODE_CHANGED_REPORT:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT: case MessageType::Chat::MAIL:
case eChatMessageType::MAIL: case MessageType::Chat::WORLD_INSTANCE_LOCATION_REQUEST:
case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST: case MessageType::Chat::REPUTATION_UPDATE:
case eChatMessageType::REPUTATION_UPDATE: case MessageType::Chat::SEND_CANNED_TEXT:
case eChatMessageType::SEND_CANNED_TEXT: case MessageType::Chat::CHARACTER_NAME_CHANGE_REQUEST:
case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST: case MessageType::Chat::CSR_REQUEST:
case eChatMessageType::CSR_REQUEST: case MessageType::Chat::CSR_REPLY:
case eChatMessageType::CSR_REPLY: case MessageType::Chat::GM_KICK:
case eChatMessageType::GM_KICK: case MessageType::Chat::WORLD_ROUTE_PACKET:
case eChatMessageType::WORLD_ROUTE_PACKET: case MessageType::Chat::GET_ZONE_POPULATIONS:
case eChatMessageType::GET_ZONE_POPULATIONS: case MessageType::Chat::REQUEST_MINIMUM_CHAT_MODE:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE: case MessageType::Chat::MATCH_REQUEST:
case eChatMessageType::MATCH_REQUEST: case MessageType::Chat::UGCMANIFEST_REPORT_MISSING_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE: case MessageType::Chat::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE: case MessageType::Chat::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT: case MessageType::Chat::UGCC_REQUEST:
case eChatMessageType::UGCC_REQUEST: case MessageType::Chat::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: case MessageType::Chat::ACHIEVEMENT_NOTIFY:
case eChatMessageType::ACHIEVEMENT_NOTIFY: case MessageType::Chat::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW: case MessageType::Chat::PLAYER_READY:
case eChatMessageType::PLAYER_READY: case MessageType::Chat::GET_DONATION_TOTAL:
case eChatMessageType::GET_DONATION_TOTAL: case MessageType::Chat::UPDATE_DONATION:
case eChatMessageType::UPDATE_DONATION: case MessageType::Chat::PRG_CSR_COMMAND:
case eChatMessageType::PRG_CSR_COMMAND: case MessageType::Chat::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD: case MessageType::Chat::UPDATE_FREE_TRIAL_STATUS:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS: LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID); break;
break; default:
default: LOG("Unknown CHAT Message id: %i", chatMessageID);
LOG("Unknown CHAT Message id: %i", chatMessageID);
} }
} }

209
dChatServer/ChatWebAPI.cpp Normal file
View File

@@ -0,0 +1,209 @@
#include "ChatWebAPI.h"
#include "Logger.h"
#include "Game.h"
#include "json.hpp"
#include "dCommonVars.h"
#include "MessageType/Chat.h"
#include "dServer.h"
#include "dConfig.h"
#include "PlayerContainer.h"
#include "GeneralUtils.h"
#include "JSONUtils.h"
#include "eHTTPMethod.h"
#include "eHTTPStatusCode.h"
#include "magic_enum.hpp"
#include "ChatPackets.h"
#include "StringifiedEnum.h"
#include "eCSRCommand.h"
#include "CSRCommandHandler.h"
#ifdef DARKFLAME_PLATFORM_WIN32
#pragma push_macro("DELETE")
#undef DELETE
#endif
using json = nlohmann::json;
typedef struct mg_connection mg_connection;
typedef struct mg_http_message mg_http_message;
namespace {
const std::string root_path = "/api/v1/";
const char* json_content_type = "Content-Type: application/json\r\n";
}
void HandlePlayersRequest(HTTPReply& reply) {
const json data = Game::playerContainer;
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Players Online\"}" : data.dump();
}
void HandleTeamsRequest(HTTPReply& reply) {
const json data = Game::playerContainer.GetTeamContainer();
reply.status = data.empty() ? eHTTPStatusCode::NO_CONTENT : eHTTPStatusCode::OK;
reply.message = data.empty() ? "{\"error\":\"No Teams Online\"}" : data.dump();
}
void HandleInvalidRoute(HTTPReply& reply) {
reply.status = eHTTPStatusCode::NOT_FOUND;
reply.message = "{\"error\":\"Invalid Route\"}";
}
void HandleGET(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
switch (route) {
case ChatWebAPI::eRoute::PLAYERS:
HandlePlayersRequest(reply);
break;
case ChatWebAPI::eRoute::TEAMS:
HandleTeamsRequest(reply);
break;
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
}
void HandleAnnounceRequest(HTTPReply& reply, const std::optional<json>& data) {
if (!JSONUtils::Validate(data, reply)) return;
const auto& good_data = data.value();
auto check = JSONUtils::CheckRequiredData(good_data, { "title", "message" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
ChatPackets::Announcement announcement;
announcement.title = good_data["title"];
announcement.message = good_data["message"];
announcement.Send();
reply.status = eHTTPStatusCode::OK;
reply.message = "{\"status\":\"Announcement Sent\"}";
}
}
void HandleCSRCommandRequest(HTTPReply& reply, const std::optional<json>& data) {
if (!JSONUtils::Validate(data, reply)) return;
const auto& good_data = data.value();
auto check = JSONUtils::CheckRequiredData(good_data, { "command" });
if (!check.empty()) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = check;
} else {
CSRCommandHandler::HandleCSRCommand(reply, good_data);
}
}
void HandlePOST(HTTPReply& reply, const ChatWebAPI::eRoute& route , const std::string& body) {
auto data = GeneralUtils::TryParse<json>(body);
switch (route) {
case ChatWebAPI::eRoute::ANNOUNCE:
HandleAnnounceRequest(reply, data.value());
break;
case ChatWebAPI::eRoute::CSR:
HandleCSRCommandRequest(reply, data.value());
break;
case ChatWebAPI::eRoute::INVALID:
default:
HandleInvalidRoute(reply);
break;
}
}
void HandleHTTPMessage(mg_connection* connection, const mg_http_message* http_msg) {
HTTPReply reply;
if (!http_msg) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid Request\"}";
} else {
// convert method from cstring to std string
std::string method_string(http_msg->method.buf, http_msg->method.len);
// get mehtod from mg to enum
const eHTTPMethod method = magic_enum::enum_cast<eHTTPMethod>(method_string).value_or(eHTTPMethod::INVALID);
// convert uri from cstring to std string
std::string uri(http_msg->uri.buf, http_msg->uri.len);
// check for root path
if (uri.find(root_path) == 0) {
// remove root path from uri
uri.erase(0, root_path.length());
// convert uri to uppercase
std::transform(uri.begin(), uri.end(), uri.begin(), ::toupper);
// convert uri string to route enum
ChatWebAPI::eRoute route = magic_enum::enum_cast<ChatWebAPI::eRoute>(uri).value_or(ChatWebAPI::eRoute::INVALID);
// convert body from cstring to std string
std::string body(http_msg->body.buf, http_msg->body.len);
switch (method) {
case eHTTPMethod::GET:
HandleGET(reply, route, body);
break;
case eHTTPMethod::POST:
HandlePOST(reply, route, body);
break;
case eHTTPMethod::PUT:
case eHTTPMethod::DELETE:
case eHTTPMethod::HEAD:
case eHTTPMethod::CONNECT:
case eHTTPMethod::OPTIONS:
case eHTTPMethod::TRACE:
case eHTTPMethod::PATCH:
case eHTTPMethod::INVALID:
default:
reply.status = eHTTPStatusCode::METHOD_NOT_ALLOWED;
reply.message = "{\"error\":\"Invalid Method\"}";
break;
}
}
}
mg_http_reply(connection, static_cast<int>(reply.status), json_content_type, reply.message.c_str());
}
void HandleRequests(mg_connection* connection, int request, void* request_data) {
switch (request) {
case MG_EV_HTTP_MSG:
HandleHTTPMessage(connection, static_cast<mg_http_message*>(request_data));
break;
default:
break;
}
}
#ifdef DARKFLAME_PLATFORM_WIN32
#pragma pop_macro("DELETE")
#endif
ChatWebAPI::ChatWebAPI() {
mg_log_set(MG_LL_NONE);
mg_mgr_init(&mgr); // Initialize event manager
}
ChatWebAPI::~ChatWebAPI() {
mg_mgr_free(&mgr);
}
void ChatWebAPI::Listen() {
// make listen address
std::string listen_ip = Game::config->GetValue("web_server_listen_ip");
if (listen_ip == "localhost") listen_ip = "127.0.0.1";
const std::string& listen_port = Game::config->GetValue("web_server_listen_port");
const std::string& listen_address = "http://" + listen_ip + ":" + listen_port;
LOG("Starting web server on %s", listen_address.c_str());
// Create HTTP listener
if (!mg_http_listen(&mgr, listen_address.c_str(), HandleRequests, NULL)) {
LOG("Failed to create web server listener");
}
}
void ChatWebAPI::ReceiveRequests() {
mg_mgr_poll(&mgr, 15);
}

30
dChatServer/ChatWebAPI.h Normal file
View File

@@ -0,0 +1,30 @@
#ifndef CHATWEBAPI_H
#define CHATWEBAPI_H
#include "mongoose.h"
typedef struct mg_mgr mg_mgr;
class ChatWebAPI {
public:
ChatWebAPI();
~ChatWebAPI();
void ReceiveRequests();
void Listen();
enum class eRoute {
// GET
PLAYERS,
TEAMS,
// POST
ANNOUNCE,
CSR,
// INVALID
INVALID
};
private:
mg_mgr mgr;
};
#endif

68
dChatServer/JSONUtils.cpp Normal file
View File

@@ -0,0 +1,68 @@
#include "JSONUtils.h"
#include "json.hpp"
#include "eHTTPStatusCode.h"
using json = nlohmann::json;
void to_json(json& data, const PlayerData& playerData) {
data["id"] = playerData.playerID;
data["name"] = playerData.playerName;
data["gm_level"] = playerData.gmLevel;
data["muted"] = playerData.GetIsMuted();
auto& zoneID = data["zone_id"];
zoneID["map_id"] = playerData.zoneID.GetMapID();
zoneID["instance_id"] = playerData.zoneID.GetInstanceID();
zoneID["clone_id"] = playerData.zoneID.GetCloneID();
}
void to_json(json& data, const PlayerContainer& playerContainer) {
data = playerContainer.GetAllPlayers();
}
void to_json(json& data, const TeamContainer& teamContainer) {
for (auto& teamData : Game::playerContainer.GetTeams()) {
if (!teamData) continue;
data.push_back(*teamData);
}
}
void to_json(json& data, const TeamData& teamData) {
data["id"] = teamData.teamID;
data["loot_flag"] = teamData.lootFlag;
data["local"] = teamData.local;
auto& leader = Game::playerContainer.GetPlayerData(teamData.leaderID);
data["leader"] = leader.playerName;
auto& members = data["members"];
for (auto& member : teamData.memberIDs) {
auto& playerData = Game::playerContainer.GetPlayerData(member);
if (!playerData) continue;
members.push_back(playerData);
}
}
std::string JSONUtils::CheckRequiredData(const json& data, const std::vector<std::string>& requiredData) {
json check;
check["error"] = json::array();
for (const auto& required : requiredData) {
if (!data.contains(required)) {
check["error"].push_back("Missing Parameter: " + required);
} else if (data[required] == "") {
check["error"].push_back("Empty Parameter: " + required);
}
}
return check["error"].empty() ? "" : check.dump();
}
bool JSONUtils::Validate(const std::optional<json>& data, HTTPReply& reply) {
if (!data) {
reply.status = eHTTPStatusCode::BAD_REQUEST;
reply.message = "{\"error\":\"Invalid JSON\"}";
return false;
}
return true;
}

24
dChatServer/JSONUtils.h Normal file
View File

@@ -0,0 +1,24 @@
#ifndef __JSONUTILS_H__
#define __JSONUTILS_H__
#include "json_fwd.hpp"
#include "PlayerContainer.h"
#include "eHTTPStatusCode.h"
struct HTTPReply {
eHTTPStatusCode status = eHTTPStatusCode::NOT_FOUND;
std::string message = "{\"error\":\"Not Found\"}";
};
void to_json(nlohmann::json& data, const PlayerData& playerData);
void to_json(nlohmann::json& data, const PlayerContainer& playerContainer);
void to_json(nlohmann::json& data, const TeamContainer& teamData);
void to_json(nlohmann::json& data, const TeamData& teamData);
namespace JSONUtils {
// check required data for reqeust
std::string CheckRequiredData(const nlohmann::json& data, const std::vector<std::string>& requiredData);
bool Validate(const std::optional<nlohmann::json>& data, HTTPReply& reply);
}
#endif // __JSONUTILS_H__

View File

@@ -11,7 +11,7 @@
#include "eConnectionType.h" #include "eConnectionType.h"
#include "ChatPackets.h" #include "ChatPackets.h"
#include "dConfig.h" #include "dConfig.h"
#include "eChatMessageType.h" #include "MessageType/Chat.h"
void PlayerContainer::Initialize() { void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends = m_MaxNumberOfBestFriends =
@@ -36,16 +36,19 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.playerID = playerId; data.playerID = playerId;
uint32_t len; uint32_t len;
inStream.Read<uint32_t>(len); if (!inStream.Read<uint32_t>(len)) return;
for (int i = 0; i < len; i++) { if (len > 33) {
char character; inStream.Read<char>(character); LOG("Received a really long player name, probably a fake packet %i.", len);
data.playerName += character; return;
} }
inStream.Read(data.zoneID); data.playerName.resize(len);
inStream.Read(data.muteExpire); inStream.ReadAlignedBytes(reinterpret_cast<unsigned char*>(data.playerName.data()), len);
inStream.Read(data.gmLevel);
if (!inStream.Read(data.zoneID)) return;
if (!inStream.Read(data.muteExpire)) return;
if (!inStream.Read(data.gmLevel)) return;
data.sysAddr = packet->systemAddress; data.sysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
@@ -54,13 +57,32 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID()); Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
m_PlayersToRemove.erase(playerId);
} }
void PlayerContainer::RemovePlayer(Packet* packet) { void PlayerContainer::ScheduleRemovePlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
LWOOBJID playerID; LWOOBJID playerID{ LWOOBJID_EMPTY };
inStream.Read(playerID); inStream.Read(playerID);
constexpr float updatePlayerOnLogoutTime = 20.0f;
if (playerID != LWOOBJID_EMPTY) m_PlayersToRemove.insert_or_assign(playerID, updatePlayerOnLogoutTime);
}
void PlayerContainer::Update(const float deltaTime) {
for (auto it = m_PlayersToRemove.begin(); it != m_PlayersToRemove.end();) {
auto& [id, time] = *it;
time -= deltaTime;
if (time <= 0.0f) {
RemovePlayer(id);
it = m_PlayersToRemove.erase(it);
} else {
++it;
}
}
}
void PlayerContainer::RemovePlayer(const LWOOBJID playerID) {
//Before they get kicked, we need to also send a message to their friends saying that they disconnected. //Before they get kicked, we need to also send a message to their friends saying that they disconnected.
const auto& player = GetPlayerData(playerID); const auto& player = GetPlayerData(playerID);
@@ -122,6 +144,11 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
size_t membersSize = 0; size_t membersSize = 0;
inStream.Read(membersSize); inStream.Read(membersSize);
if (membersSize >= 4) {
LOG("Tried to create a team with more than 4 players");
return;
}
std::vector<LWOOBJID> members; std::vector<LWOOBJID> members;
members.reserve(membersSize); members.reserve(membersSize);
@@ -140,14 +167,13 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
if (team != nullptr) { if (team != nullptr) {
team->zoneId = zoneId; team->zoneId = zoneId;
UpdateTeamsOnWorld(team, false);
} }
UpdateTeamsOnWorld(team, false);
} }
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GM_MUTE);
bitStream.Write(player); bitStream.Write(player);
bitStream.Write(time); bitStream.Write(time);
@@ -190,7 +216,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
team->leaderID = leader; team->leaderID = leader;
team->local = local; team->local = local;
mTeams.push_back(team); GetTeamsMut().push_back(team);
AddMember(team, leader); AddMember(team, leader);
@@ -198,7 +224,7 @@ TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
} }
TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) { TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
for (auto* team : mTeams) { for (auto* team : GetTeams()) {
if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue; if (std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID) == team->memberIDs.end()) continue;
return team; return team;
@@ -306,9 +332,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
} }
void PlayerContainer::DisbandTeam(TeamData* team) { void PlayerContainer::DisbandTeam(TeamData* team) {
const auto index = std::find(mTeams.begin(), mTeams.end(), team); const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
if (index == mTeams.end()) return; if (index == GetTeams().end()) return;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
const auto& otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
@@ -323,15 +349,15 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
UpdateTeamsOnWorld(team, true); UpdateTeamsOnWorld(team, true);
mTeams.erase(index); GetTeamsMut().erase(index);
delete team; delete team;
} }
void PlayerContainer::TeamStatusUpdate(TeamData* team) { void PlayerContainer::TeamStatusUpdate(TeamData* team) {
const auto index = std::find(mTeams.begin(), mTeams.end(), team); const auto index = std::find(GetTeams().begin(), GetTeams().end(), team);
if (index == mTeams.end()) return; if (index == GetTeams().end()) return;
const auto& leader = GetPlayerData(team->leaderID); const auto& leader = GetPlayerData(team->leaderID);
@@ -354,7 +380,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; 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(team->teamID);
bitStream.Write(deleteTeam); bitStream.Write(deleteTeam);
@@ -410,3 +436,13 @@ const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) {
const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) { const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) {
return GetPlayerDataMutable(playerName); return GetPlayerDataMutable(playerName);
} }
void PlayerContainer::Shutdown() {
m_Players.erase(LWOOBJID_EMPTY);
while (!m_Players.empty()) {
const auto& [id, playerData] = *m_Players.begin();
Database::Get()->UpdateActivityLog(id, eActivityType::PlayerLoggedOut, playerData.zoneID.GetMapID());
m_Players.erase(m_Players.begin());
}
for (auto* team : GetTeams()) if (team) delete team;
}

View File

@@ -9,6 +9,12 @@
enum class eGameMasterLevel : uint8_t; enum class eGameMasterLevel : uint8_t;
struct TeamData;
struct TeamContainer {
std::vector<TeamData*> mTeams;
};
struct IgnoreData { struct IgnoreData {
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {} IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
inline bool operator==(const std::string& other) const noexcept { inline bool operator==(const std::string& other) const noexcept {
@@ -48,6 +54,7 @@ struct PlayerData {
bool isFTP = false; bool isFTP = false;
}; };
struct TeamData { struct TeamData {
TeamData(); TeamData();
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
@@ -62,10 +69,12 @@ class PlayerContainer {
public: public:
void Initialize(); void Initialize();
void InsertPlayer(Packet* packet); void InsertPlayer(Packet* packet);
void RemovePlayer(Packet* packet); void ScheduleRemovePlayer(Packet* packet);
void RemovePlayer(const LWOOBJID playerID);
void MuteUpdate(Packet* packet); void MuteUpdate(Packet* packet);
void CreateTeamServer(Packet* packet); void CreateTeamServer(Packet* packet);
void BroadcastMuteUpdate(LWOOBJID player, time_t time); void BroadcastMuteUpdate(LWOOBJID player, time_t time);
void Shutdown();
const PlayerData& GetPlayerData(const LWOOBJID& playerID); const PlayerData& GetPlayerData(const LWOOBJID& playerID);
const PlayerData& GetPlayerData(const std::string& playerName); const PlayerData& GetPlayerData(const std::string& playerName);
@@ -73,7 +82,7 @@ public:
PlayerData& GetPlayerDataMutable(const std::string& playerName); PlayerData& GetPlayerDataMutable(const std::string& playerName);
uint32_t GetPlayerCount() { return m_PlayerCount; }; uint32_t GetPlayerCount() { return m_PlayerCount; };
uint32_t GetSimCount() { return m_SimCount; }; uint32_t GetSimCount() { return m_SimCount; };
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; }; const std::map<LWOOBJID, PlayerData>& GetAllPlayers() const { return m_Players; };
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members); TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false); TeamData* CreateTeam(LWOOBJID leader, bool local = false);
@@ -88,12 +97,19 @@ public:
LWOOBJID GetId(const std::u16string& playerName); LWOOBJID GetId(const std::u16string& playerName);
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; } uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; } uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
const TeamContainer& GetTeamContainer() { return teamContainer; }
std::vector<TeamData*>& GetTeamsMut() { return teamContainer.mTeams; };
const std::vector<TeamData*>& GetTeams() { return GetTeamsMut(); };
void Update(const float deltaTime);
bool PlayerBeingRemoved(const LWOOBJID playerID) { return m_PlayersToRemove.contains(playerID); }
private: private:
LWOOBJID m_TeamIDCounter = 0; LWOOBJID m_TeamIDCounter = 0;
std::map<LWOOBJID, PlayerData> m_Players; std::map<LWOOBJID, PlayerData> m_Players;
std::vector<TeamData*> mTeams; TeamContainer teamContainer{};
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> m_Names;
std::map<LWOOBJID, float> m_PlayersToRemove;
uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50; uint32_t m_MaxNumberOfFriends = 50;
uint32_t m_PlayerCount = 0; uint32_t m_PlayerCount = 0;

View File

@@ -9,73 +9,54 @@
* AMF3 Deserializer written by EmosewaMC * AMF3 Deserializer written by EmosewaMC
*/ */
AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream& inStream) { std::unique_ptr<AMFBaseValue> AMFDeserialize::Read(RakNet::BitStream& inStream) {
AMFBaseValue* returnValue = nullptr;
// Read in the value type from the bitStream // Read in the value type from the bitStream
eAmf marker; eAmf marker;
inStream.Read(marker); inStream.Read(marker);
// Based on the typing, create the value associated with that and return the base value class // Based on the typing, create the value associated with that and return the base value class
switch (marker) { switch (marker) {
case eAmf::Undefined: { case eAmf::Undefined:
returnValue = new AMFBaseValue(); return std::make_unique<AMFBaseValue>();
break; case eAmf::Null:
} return std::make_unique<AMFNullValue>();
case eAmf::False:
case eAmf::Null: { return std::make_unique<AMFBoolValue>(false);
returnValue = new AMFNullValue(); case eAmf::True:
break; return std::make_unique<AMFBoolValue>(true);
} case eAmf::Integer:
return ReadAmfInteger(inStream);
case eAmf::False: { case eAmf::Double:
returnValue = new AMFBoolValue(false); return ReadAmfDouble(inStream);
break; case eAmf::String:
} return ReadAmfString(inStream);
case eAmf::Array:
case eAmf::True: { return ReadAmfArray(inStream);
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;
}
// These values are unimplemented in the live client and will remain unimplemented // These values are unimplemented in the live client and will remain unimplemented
// unless someone modifies the client to allow serializing of these values. // unless someone modifies the client to allow serializing of these values.
case eAmf::XMLDoc: case eAmf::XMLDoc:
[[fallthrough]];
case eAmf::Date: case eAmf::Date:
[[fallthrough]];
case eAmf::Object: case eAmf::Object:
[[fallthrough]];
case eAmf::XML: case eAmf::XML:
[[fallthrough]];
case eAmf::ByteArray: case eAmf::ByteArray:
[[fallthrough]];
case eAmf::VectorInt: case eAmf::VectorInt:
[[fallthrough]];
case eAmf::VectorUInt: case eAmf::VectorUInt:
[[fallthrough]];
case eAmf::VectorDouble: case eAmf::VectorDouble:
[[fallthrough]];
case eAmf::VectorObject: case eAmf::VectorObject:
case eAmf::Dictionary: { [[fallthrough]];
case eAmf::Dictionary:
throw marker; throw marker;
break;
}
default: default:
throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker))); 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) { 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; double value;
inStream.Read<double>(value); inStream.Read<double>(value);
return new AMFDoubleValue(value); return std::make_unique<AMFDoubleValue>(value);
} }
AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) { std::unique_ptr<AMFArrayValue> AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
auto arrayValue = new AMFArrayValue(); auto arrayValue = std::make_unique<AMFArrayValue>();
// Read size of dense array // Read size of dense array
const auto sizeOfDenseArray = (ReadU29(inStream) >> 1); const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
@@ -143,10 +124,10 @@ AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
return arrayValue; return arrayValue;
} }
AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) { std::unique_ptr<AMFStringValue> AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
return new AMFStringValue(ReadString(inStream)); return std::make_unique<AMFStringValue>(ReadString(inStream));
} }
AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) { std::unique_ptr<AMFIntValue> AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
return new AMFIntValue(ReadU29(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 #pragma once
#include "Amf3.h"
#include "BitStream.h" #include "BitStream.h"
#include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
class AMFBaseValue;
class AMFDeserialize { class AMFDeserialize {
public: public:
/** /**
@@ -15,7 +15,7 @@ public:
* @param inStream inStream to read value from. * @param inStream inStream to read value from.
* @return Returns an AMFValue with all the information from the bitStream in it. * @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: private:
/** /**
* @brief Private method to read a U29 integer from a bitstream * @brief Private method to read a U29 integer from a bitstream
@@ -39,7 +39,7 @@ private:
* @param inStream bitStream to read data from * @param inStream bitStream to read data from
* @return Double value represented as an AMFValue * @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 * @brief Read an AMFArray from a bitStream
@@ -47,7 +47,7 @@ private:
* @param inStream bitStream to read data from * @param inStream bitStream to read data from
* @return Array value represented as an AMFValue * @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 * @brief Read an AMFString from a bitStream
@@ -55,7 +55,7 @@ private:
* @param inStream bitStream to read data from * @param inStream bitStream to read data from
* @return String value represented as an AMFValue * @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 * @brief Read an AMFInteger from a bitStream
@@ -63,7 +63,7 @@ private:
* @param inStream bitStream to read data from * @param inStream bitStream to read data from
* @return Integer value represented as an AMFValue * @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. * 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. * and are not to be deleted by a caller.
*/ */
class AMFArrayValue : public AMFBaseValue { class AMFArrayValue : public AMFBaseValue {
using AMFAssociative = std::unordered_map<std::string, AMFBaseValue*>; using AMFAssociative =
using AMFDense = std::vector<AMFBaseValue*>; 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: public:
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; } [[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 * Returns the Associative portion of the object
*/ */
@@ -151,30 +138,32 @@ public:
* or nullptr if a key existed and was not the same type * or nullptr if a key existed and was not the same type
*/ */
template <typename ValueType> 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); const auto element = m_Associative.find(key);
AMFValue<ValueType>* val = nullptr; AMFValue<ValueType>* val = nullptr;
bool found = true; bool found = true;
if (element == m_Associative.cend()) { if (element == m_Associative.cend()) {
val = new AMFValue<ValueType>(value); auto newVal = std::make_unique<AMFValue<ValueType>>(value);
m_Associative.emplace(key, val); val = newVal.get();
m_Associative.emplace(key, std::move(newVal));
} else { } else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second); val = dynamic_cast<AMFValue<ValueType>*>(element->second.get());
found = false; found = false;
} }
return std::make_pair(val, found); return std::make_pair(val, found);
} }
// Associates an array with a string key // 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); const auto element = m_Associative.find(key);
AMFArrayValue* val = nullptr; AMFArrayValue* val = nullptr;
bool found = true; bool found = true;
if (element == m_Associative.cend()) { if (element == m_Associative.cend()) {
val = new AMFArrayValue(); auto newVal = std::make_unique<AMFArrayValue>();
m_Associative.emplace(key, val); val = newVal.get();
m_Associative.emplace(key, std::move(newVal));
} else { } else {
val = dynamic_cast<AMFArrayValue*>(element->second); val = dynamic_cast<AMFArrayValue*>(element->second.get());
found = false; found = false;
} }
return std::make_pair(val, found); return std::make_pair(val, found);
@@ -182,15 +171,13 @@ public:
// Associates an array with an integer key // Associates an array with an integer key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) { [[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
AMFArrayValue* val = nullptr;
bool inserted = false; bool inserted = false;
if (index >= m_Dense.size()) { if (index >= m_Dense.size()) {
m_Dense.resize(index + 1); m_Dense.resize(index + 1);
val = new AMFArrayValue(); m_Dense.at(index) = std::make_unique<AMFArrayValue>();
m_Dense.at(index) = val;
inserted = true; 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> template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) { [[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
AMFValue<ValueType>* val = nullptr;
bool inserted = false; bool inserted = false;
if (index >= m_Dense.size()) { if (index >= m_Dense.size()) {
m_Dense.resize(index + 1); m_Dense.resize(index + 1);
val = new AMFValue<ValueType>(value); m_Dense.at(index) = std::make_unique<AMFValue<ValueType>>(value);
m_Dense.at(index) = val;
inserted = true; 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 key The key to associate with the value
* @param value The value to insert * @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); const auto element = m_Associative.find(key);
if (element != m_Associative.cend() && element->second) { if (element != m_Associative.cend() && element->second) {
delete element->second; element->second = std::move(value);
element->second = value;
} else { } 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 key The key to associate with the value
* @param value The value to insert * @param value The value to insert
*/ */
void Insert(const size_t index, AMFBaseValue* const value) { void Insert(const size_t index, std::unique_ptr<AMFBaseValue> value) {
if (index < m_Dense.size()) { if (index >= m_Dense.size()) {
const AMFDense::const_iterator itr = m_Dense.cbegin() + index;
if (*itr) delete m_Dense.at(index);
} else {
m_Dense.resize(index + 1); 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) { void Remove(const std::string& key, const bool deleteValue = true) {
const AMFAssociative::const_iterator it = m_Associative.find(key); const AMFAssociative::const_iterator it = m_Associative.find(key);
if (it != m_Associative.cend()) { if (it != m_Associative.cend()) {
if (deleteValue) delete it->second; if (deleteValue) m_Associative.erase(it);
m_Associative.erase(it);
} }
} }
@@ -290,7 +270,6 @@ public:
void Remove(const size_t index) { void Remove(const size_t index) {
if (!m_Dense.empty() && index < m_Dense.size()) { if (!m_Dense.empty() && index < m_Dense.size()) {
const auto itr = m_Dense.cbegin() + index; const auto itr = m_Dense.cbegin() + index;
if (*itr) delete (*itr);
m_Dense.erase(itr); m_Dense.erase(itr);
} }
} }
@@ -299,16 +278,16 @@ public:
if (!m_Dense.empty()) Remove(m_Dense.size() - 1); 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); 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 { [[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); return static_cast<AMFArrayValue*>(Insert(key).first);
} }
@@ -330,17 +309,17 @@ public:
* @return The AMFValue * @return The AMFValue
*/ */
template <typename AmfType> 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); const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ? return it != m_Associative.cend() ?
dynamic_cast<AMFValue<AmfType>*>(it->second) : dynamic_cast<AMFValue<AmfType>*>(it->second.get()) :
nullptr; nullptr;
} }
// Get from the array but dont cast it // 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); 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> template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const { [[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
return index < m_Dense.size() ? return index < m_Dense.size() ?
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index)) : dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index).get()) :
nullptr; nullptr;
} }
// Get from the dense but dont cast it // Get from the dense but dont cast it
[[nodiscard]] AMFBaseValue* Get(const size_t index) const { [[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: private:

View File

@@ -2,16 +2,25 @@
#include <string> #include <string>
//For reading null-terminated strings //For reading null-terminated strings
std::string BinaryIO::ReadString(std::istream& instream) { template<typename StringType>
std::string toReturn; StringType ReadString(std::istream& instream) {
char buffer; StringType toReturn{};
typename StringType::value_type buffer{};
BinaryIO::BinaryRead(instream, buffer); BinaryIO::BinaryRead(instream, buffer);
while (buffer != 0x00) { while (buffer != 0x00) {
toReturn += buffer; toReturn += buffer;
BinaryRead(instream, buffer); BinaryIO::BinaryRead(instream, buffer);
} }
return toReturn; return toReturn;
} }
std::string BinaryIO::ReadString(std::istream& instream) {
return ::ReadString<std::string>(instream);
}
std::u8string BinaryIO::ReadU8String(std::istream& instream) {
return ::ReadString<std::u8string>(instream);
}

View File

@@ -65,6 +65,8 @@ namespace BinaryIO {
std::string ReadString(std::istream& instream); std::string ReadString(std::istream& instream);
std::u8string ReadU8String(std::istream& instream);
inline bool DoesFileExist(const std::string& name) { inline bool DoesFileExist(const std::string& name) {
std::ifstream f(name.c_str()); std::ifstream f(name.c_str());
return f.good(); return f.good();

View File

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

View File

@@ -37,7 +37,6 @@ target_include_directories(dCommon
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase" "${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables" "${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase" "${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
) )
if (UNIX) if (UNIX)

View File

@@ -65,13 +65,14 @@ int64_t FdbToSqlite::Convert::ReadInt64(std::istream& cdClientBuffer) {
return value; return value;
} }
// cdclient is encoded in latin1
std::string FdbToSqlite::Convert::ReadString(std::istream& cdClientBuffer) { std::string FdbToSqlite::Convert::ReadString(std::istream& cdClientBuffer) {
int32_t prevPosition = SeekPointer(cdClientBuffer); int32_t prevPosition = SeekPointer(cdClientBuffer);
auto readString = BinaryIO::ReadString(cdClientBuffer); const auto readString = BinaryIO::ReadU8String(cdClientBuffer);
cdClientBuffer.seekg(prevPosition); cdClientBuffer.seekg(prevPosition);
return readString; return GeneralUtils::Latin1ToUTF8(readString);
} }
int32_t FdbToSqlite::Convert::SeekPointer(std::istream& cdClientBuffer) { int32_t FdbToSqlite::Convert::SeekPointer(std::istream& cdClientBuffer) {

View File

@@ -7,6 +7,10 @@
#include <filesystem> #include <filesystem>
#include <map> #include <map>
#include "json.hpp"
using json = nlohmann::json;
template <typename T> template <typename T>
static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) { static inline size_t MinSize(const size_t size, const std::basic_string_view<T> string) {
if (size == SIZE_MAX || size > string.size()) { if (size == SIZE_MAX || size > string.size()) {
@@ -167,6 +171,15 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const s
return ret; return ret;
} }
std::string GeneralUtils::Latin1ToUTF8(const std::u8string_view string, const size_t size) {
std::string toReturn{};
for (const auto u : string) {
PushUTF8CodePoint(toReturn, u);
}
return toReturn;
}
//! Converts a (potentially-ill-formed) UTF-16 string to UTF-8 //! Converts a (potentially-ill-formed) UTF-16 string to UTF-8
//! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16> //! See: <http://simonsapin.github.io/wtf-8/#decoding-ill-formed-utf-16>
std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) { std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
@@ -175,9 +188,9 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const si
ret.reserve(newSize); ret.reserve(newSize);
for (size_t i = 0; i < newSize; ++i) { for (size_t i = 0; i < newSize; ++i) {
const char16_t u = string[i]; const auto u = string[i];
if (IsLeadSurrogate(u) && (i + 1) < newSize) { if (IsLeadSurrogate(u) && (i + 1) < newSize) {
const char16_t next = string[i + 1]; const auto next = string[i + 1];
if (IsTrailSurrogate(next)) { if (IsTrailSurrogate(next)) {
i += 1; i += 1;
const char32_t cp = 0x10000 const char32_t cp = 0x10000
@@ -291,11 +304,12 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) { std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string_view folder) {
// Because we dont know how large the initial number before the first _ is we need to make it a map like so. // Because we dont know how large the initial number before the first _ is we need to make it a map like so.
std::map<uint32_t, std::string> filenames{}; std::map<uint32_t, std::string> filenames{};
for (const auto& t : std::filesystem::directory_iterator(folder)) { for (const auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string(); if (t.is_directory() || t.is_symlink()) continue;
const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0)); auto filename = t.path().filename().string();
filenames.emplace(index, std::move(filename)); const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.emplace(index, std::move(filename));
} }
// Now sort the map by the oldest migration. // Now sort the map by the oldest migration.
@@ -317,6 +331,17 @@ std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::stri
return sortedFiles; return sortedFiles;
} }
template<>
[[nodiscard]] std::optional<json> GeneralUtils::TryParse(std::string_view str) {
try {
return json::parse(str);
} catch (const std::exception& e) {
return std::nullopt;
} catch (...) {
return std::nullopt;
}
}
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924) #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse function specializations // MacOS floating-point parse function specializations

View File

@@ -51,6 +51,14 @@ namespace GeneralUtils {
bool _NextUTF8Char(std::string_view& slice, uint32_t& out); bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
} }
//! Converts a Latin1 string to a UTF-8 string
/*!
\param string The string to convert
\param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-8 representation of the string
*/
std::string Latin1ToUTF8(const std::u8string_view string, const size_t size = SIZE_MAX);
//! Converts a UTF-16 string to a UTF-8 string //! Converts a UTF-16 string to a UTF-8 string
/*! /*!
\param string The string to convert \param string The string to convert
@@ -129,6 +137,29 @@ namespace GeneralUtils {
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder); 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 // Concept constraining to enum types
template <typename T> template <typename T>
concept Enum = std::is_enum_v<T>; concept Enum = std::is_enum_v<T>;
@@ -170,6 +201,10 @@ namespace GeneralUtils {
return isParsed ? static_cast<T>(result) : std::optional<T>{}; return isParsed ? static_cast<T>(result) : std::optional<T>{};
} }
template<typename T>
requires(!Numeric<T>)
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924) #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse helper function specializations // MacOS floating-point parse helper function specializations

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

@@ -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 <set>
#include "BitStream.h" #include "BitStream.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eClientMessageType.h" #include "MessageType/Client.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings #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 CBITSTREAM RakNet::BitStream bitStream;
#define CINSTREAM RakNet::BitStream inStream(packet->data, packet->length, false); #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 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 Game::server->Send(bitStream, sysAddr, false);
#define SEND_PACKET_BROADCAST Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); #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

@@ -0,0 +1,54 @@
#ifndef __ECSRCOMMAND__H__
#define __ECSRCOMMAND__H__
enum class eCSRCommand {
QUERY_SERVER_STATUS,
QUERY_CHARACTER_LOCATION,
QUERY_CHARACTER_ONLINE_STATUS,
INVENTORY_ADD_ITEM,
INVENTORY_DELETE_ITEM,
MODERATE_MUTE_ACCOUNT,
MODERATE_BAN_ACCOUNT,
MODERATE_EDUCATE_CHARACTER,
MODERATE_KICK_CHARACTER,
MODERATE_WARN_CHARACTER,
MODERATE_RENAME_CHARACTER,
MODERATE_DELETE_CHARACTER_FRIEND,
MODERATE_KILL_CHARACTER,
UPDATE_CHARACTER_HEALTH,
UPDATE_CHARACTER_ARMOR,
UPDATE_CHARACTER_IMAGINATION,
UPDATE_CHARACTER_MAX_HEALTH,
UPDATE_CHARACTER_MAX_ARMOR,
UPDATE_CHARACTER_MAX_IMAGINATION,
UPDATE_CHARACTER_CURRENCY,
UPDATE_CHARACTER_REPUTATION,
UPDATE_CHARACTER_LEGO_SCORE,
UPDATE_CHARACTER_EMOTES,
UPDATE_CHARACTER_ADD_ACHIEVEMENT,
UPDATE_CHARACTER_COMPLETE_ACHIEVEMENT,
UPDATE_CHARACTER_REMOVE_ACHIEVEMENT,
UPDATE_CHARACTER_POSITION_OFFLINE,
UPDATE_CHARACTER_INV_SLOT_AMOUNT,
UTILITY_SAVE_CHARACTER,
UTILITY_SEND_MAIL,
UTILITY_GIVE_ITEM_TO_ALL_PLAYERS_ONLINE,
METRICS_CONFIGURE,
DISABLE_ZONE,
INIT_DONATION_AMOUNT,
KILL_SERVERS_COUNTDOWN,
DISABLE_FAQ,
THROTTLEQUEUE,
GATEGM_ACCESS,
RECONNECT_CRISP,
MODERATE_KICK_ACCOUNT,
TOGGLE_CRISP_SERVER,
QUICK_DRAIN_SERVER,
QUICK_DRAIN_SERVER_RENEW,
REPLICATE_CHARACTER,
GET_SERVER_STATUS,
RELOAD_SERVER_INIS,
INVALID
};
#endif // !__ECSRCOMMAND__H__

View File

@@ -15,7 +15,8 @@ enum class eCharacterVersion : uint32_t {
// Fixes vault size value // Fixes vault size value
VAULT_SIZE, VAULT_SIZE,
// Fixes speed base value in level component // 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__ #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

@@ -0,0 +1,26 @@
#ifndef __EHTTPMETHODS__H__
#define __EHTTPMETHODS__H__
#ifdef DARKFLAME_PLATFORM_WIN32
#pragma push_macro("DELETE")
#undef DELETE
#endif
enum class eHTTPMethod {
GET,
POST,
PUT,
DELETE,
HEAD,
CONNECT,
OPTIONS,
TRACE,
PATCH,
INVALID
};
#ifdef DARKFLAME_PLATFORM_WIN32
#pragma pop_macro("DELETE")
#endif
#endif // __EHTTPMETHODS__H__

View File

@@ -0,0 +1,72 @@
#ifndef __EHTTPSTATUSCODE__H__
#define __EHTTPSTATUSCODE__H__
#include <cstdint>
// verbose list of http codes
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Status
enum class eHTTPStatusCode : uint32_t {
CONTINUE = 100,
SWITCHING_PROTOCOLS = 101,
PROCESSING = 102,
EARLY_HINTS = 103,
OK = 200,
CREATED = 201,
ACCEPTED = 202,
NON_AUTHORITATIVE_INFORMATION = 203,
NO_CONTENT = 204,
RESET_CONTENT = 205,
PARTIAL_CONTENT = 206,
MULTI_STATUS = 207,
ALREADY_REPORTED = 208,
IM_USED = 226,
MULTIPLE_CHOICES = 300,
MOVED_PERMANENTLY = 301,
FOUND = 302,
SEE_OTHER = 303,
NOT_MODIFIED = 304,
USE_PROXY = 305,
SWITCH_PROXY = 306,
TEMPORARY_REDIRECT = 307,
PERMANENT_REDIRECT = 308,
BAD_REQUEST = 400,
UNAUTHORIZED = 401,
PAYMENT_REQUIRED = 402,
FORBIDDEN = 403,
NOT_FOUND = 404,
METHOD_NOT_ALLOWED = 405,
NOT_ACCEPTABLE = 406,
PROXY_AUTHENTICATION_REQUIRED = 407,
REQUEST_TIMEOUT = 408,
CONFLICT = 409,
GONE = 410,
LENGTH_REQUIRED = 411,
PRECONDITION_FAILED = 412,
PAYLOAD_TOO_LARGE = 413,
URI_TOO_LONG = 414,
UNSUPPORTED_MEDIA_TYPE = 415,
RANGE_NOT_SATISFIABLE = 416,
EXPECTATION_FAILED = 417,
I_AM_A_TEAPOT = 418,
MISDIRECTED_REQUEST = 421,
UNPROCESSABLE_ENTITY = 422,
LOCKED = 423,
FAILED_DEPENDENCY = 424,
UPGRADE_REQUIRED = 426,
PRECONDITION_REQUIRED = 428,
TOO_MANY_REQUESTS = 429,
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
INTERNAL_SERVER_ERROR = 500,
NOT_IMPLEMENTED = 501,
BAD_GATEWAY = 502,
SERVICE_UNAVAILABLE = 503,
GATEWAY_TIMEOUT = 504,
HTTP_VERSION_NOT_SUPPORTED = 505,
VARIANT_ALSO_NEGOTIATES = 506,
INSUFFICIENT_STORAGE = 507,
LOOP_DETECTED = 508,
NOT_EXTENDED = 510,
NETWORK_AUTHENTICATION_REQUIRED = 511
};
#endif // !__EHTTPSTATUSCODE__H__

View File

@@ -4,6 +4,9 @@
#define __EINVENTORYTYPE__H__ #define __EINVENTORYTYPE__H__
#include <cstdint> #include <cstdint>
#include "magic_enum.hpp"
static const uint8_t NUMBER_OF_INVENTORIES = 17; static const uint8_t NUMBER_OF_INVENTORIES = 17;
/** /**
* Represents the different types of inventories an entity may have * 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__ #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

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

@@ -2,6 +2,12 @@ add_subdirectory(CDClientDatabase)
add_subdirectory(GameDatabase) add_subdirectory(GameDatabase)
add_library(dDatabase STATIC "MigrationRunner.cpp") add_library(dDatabase STATIC "MigrationRunner.cpp")
add_custom_target(conncpp_dylib
${CMAKE_COMMAND} -E copy $<TARGET_FILE:MariaDB::ConnCpp> ${PROJECT_BINARY_DIR})
add_dependencies(dDatabase conncpp_dylib)
target_include_directories(dDatabase PUBLIC ".") target_include_directories(dDatabase PUBLIC ".")
target_link_libraries(dDatabase target_link_libraries(dDatabase
PUBLIC dDatabaseCDClient dDatabaseGame) PUBLIC dDatabaseCDClient dDatabaseGame)

View File

@@ -8,15 +8,28 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}") set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}")
endforeach() endforeach()
add_subdirectory(SQLite)
foreach(file ${DDATABSE_DATABSES_SQLITE_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "SQLite/${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}) add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES})
target_include_directories(dDatabaseGame PUBLIC "." target_include_directories(dDatabaseGame PUBLIC "."
"ITables" PRIVATE "MySQL" "ITables" PRIVATE "MySQL" "SQLite" "TestSQL"
"${PROJECT_SOURCE_DIR}/dCommon" "${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums" "${PROJECT_SOURCE_DIR}/dCommon/dEnums"
) )
target_link_libraries(dDatabaseGame target_link_libraries(dDatabaseGame
PUBLIC MariaDB::ConnCpp INTERFACE dCommon
INTERFACE dCommon) PRIVATE sqlite3 MariaDB::ConnCpp)
# Glob together all headers that need to be precompiled # Glob together all headers that need to be precompiled
file( file(

View File

@@ -2,22 +2,46 @@
#include "Game.h" #include "Game.h"
#include "dConfig.h" #include "dConfig.h"
#include "Logger.h" #include "Logger.h"
#include "MySQLDatabase.h"
#include "DluAssert.h" #include "DluAssert.h"
#include "SQLiteDatabase.h"
#include "MySQLDatabase.h"
#include <ranges>
#pragma warning (disable:4251) //Disables SQL warnings #pragma warning (disable:4251) //Disables SQL warnings
namespace { namespace {
GameDatabase* database = nullptr; GameDatabase* database = nullptr;
} }
std::string Database::GetMigrationFolder() {
const std::set<std::string> validMysqlTypes = { "mysql", "mariadb", "maria" };
auto databaseType = Game::config->GetValue("database_type");
std::ranges::transform(databaseType, databaseType.begin(), ::tolower);
if (databaseType == "sqlite") return "sqlite";
else if (validMysqlTypes.contains(databaseType)) return "mysql";
else {
LOG("No database specified, using MySQL");
return "mysql";
}
}
void Database::Connect() { void Database::Connect() {
if (database) { if (database) {
LOG("Tried to connect to database when it's already connected!"); LOG("Tried to connect to database when it's already connected!");
return; return;
} }
database = new MySQLDatabase(); const auto databaseType = GetMigrationFolder();
if (databaseType == "sqlite") database = new SQLiteDatabase();
else if (databaseType == "mysql") database = new MySQLDatabase();
else {
LOG("Invalid database type specified in config, using MySQL");
database = new MySQLDatabase();
}
database->Connect(); database->Connect();
} }
@@ -38,3 +62,8 @@ void Database::Destroy(std::string source) {
LOG("Trying to destroy database when it's not connected!"); LOG("Trying to destroy database when it's not connected!");
} }
} }
void Database::_setDatabase(GameDatabase* const db) {
if (database) delete database;
database = db;
}

View File

@@ -1,7 +1,6 @@
#pragma once #pragma once
#include <string> #include <string>
#include <conncpp.hpp>
#include "GameDatabase.h" #include "GameDatabase.h"
@@ -9,4 +8,10 @@ namespace Database {
void Connect(); void Connect();
GameDatabase* Get(); GameDatabase* Get();
void Destroy(std::string source = ""); 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);
std::string GetMigrationFolder();
}; };

View File

@@ -24,14 +24,10 @@
#include "IIgnoreList.h" #include "IIgnoreList.h"
#include "IAccountsRewardCodes.h" #include "IAccountsRewardCodes.h"
#include "IBehaviors.h" #include "IBehaviors.h"
#include "IUgcModularBuild.h"
namespace sql {
class Statement;
class PreparedStatement;
};
#ifdef _DEBUG #ifdef _DEBUG
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (sql::SQLException& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0) # define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (std::exception& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
#else #else
# define DLU_SQL_TRY_CATCH_RETHROW(x) x # define DLU_SQL_TRY_CATCH_RETHROW(x) x
#endif // _DEBUG #endif // _DEBUG
@@ -42,14 +38,13 @@ class GameDatabase :
public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, 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 IBehaviors, public IUgcModularBuild {
public: public:
virtual ~GameDatabase() = default; virtual ~GameDatabase() = default;
// TODO: These should be made private. // TODO: These should be made private.
virtual void Connect() = 0; virtual void Connect() = 0;
virtual void Destroy(std::string source = "") = 0; virtual void Destroy(std::string source = "") = 0;
virtual void ExecuteCustomQuery(const std::string_view query) = 0; virtual void ExecuteCustomQuery(const std::string_view query) = 0;
virtual sql::PreparedStatement* CreatePreppedStmt(const std::string& query) = 0;
virtual void Commit() = 0; virtual void Commit() = 0;
virtual bool GetAutoCommit() = 0; virtual bool GetAutoCommit() = 0;
virtual void SetAutoCommit(bool value) = 0; virtual void SetAutoCommit(bool value) = 0;

View File

@@ -33,6 +33,11 @@ public:
// Add a new account to the database. // Add a new account to the database.
virtual void InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) = 0; 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;
virtual uint32_t GetAccountCount() = 0;
}; };
#endif //!__IACCOUNTS__H__ #endif //!__IACCOUNTS__H__

View File

@@ -3,12 +3,45 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
#include <string>
#include <vector>
class ILeaderboard { class ILeaderboard {
public: public:
struct Entry {
uint32_t charId{};
uint32_t lastPlayedTimestamp{};
float primaryScore{};
float secondaryScore{};
float tertiaryScore{};
uint32_t numWins{};
uint32_t numTimesPlayed{};
uint32_t ranking{};
std::string name{};
};
struct Score {
auto operator<=>(const Score& rhs) const = default;
float primaryScore{ 0.0f };
float secondaryScore{ 0.0f };
float tertiaryScore{ 0.0f };
};
// Get the donation total for the given activity id. // Get the donation total for the given activity id.
virtual std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) = 0; virtual std::optional<uint32_t> GetDonationTotal(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) = 0;
virtual std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) = 0;
virtual std::optional<Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) = 0;
virtual void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) = 0;
virtual void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) = 0;
virtual void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) = 0;
}; };
#endif //!__ILEADERBOARD__H__ #endif //!__ILEADERBOARD__H__

View File

@@ -4,6 +4,8 @@
#include <cstdint> #include <cstdint>
#include <optional> #include <optional>
enum ePropertySortType : int32_t;
class IProperty { class IProperty {
public: public:
struct Info { struct Info {
@@ -18,11 +20,33 @@ public:
uint32_t lastUpdatedTime{}; uint32_t lastUpdatedTime{};
uint32_t claimedTime{}; uint32_t claimedTime{};
uint32_t reputation{}; 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. // Get the property info for the given property id.
virtual std::optional<IProperty::Info> GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) = 0; 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. // Update the property moderation info for the given property id.
virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0; virtual void UpdatePropertyModerationInfo(const IProperty::Info& info) = 0;

View File

@@ -0,0 +1,14 @@
#ifndef IUGCMODULARBUILD_H
#define IUGCMODULARBUILD_H
#include <cstdint>
#include <optional>
#include <string>
class IUgcModularBuild {
public:
virtual void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) = 0;
virtual void DeleteUgcBuild(const LWOOBJID bigId) = 0;
};
#endif //!IUGCMODULARBUILD_H

View File

@@ -4,6 +4,7 @@
#include "Game.h" #include "Game.h"
#include "dConfig.h" #include "dConfig.h"
#include "Logger.h" #include "Logger.h"
#include "dPlatforms.h"
namespace { namespace {
std::string databaseName; std::string databaseName;
@@ -13,6 +14,7 @@ namespace {
}; };
void MySQLDatabase::Connect() { void MySQLDatabase::Connect() {
LOG("Using MySQL database");
driver = sql::mariadb::get_driver_instance(); driver = sql::mariadb::get_driver_instance();
// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where // The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
@@ -39,14 +41,13 @@ void MySQLDatabase::Connect() {
properties["autoReconnect"] = "true"; properties["autoReconnect"] = "true";
databaseName = Game::config->GetValue("mysql_database").c_str(); databaseName = Game::config->GetValue("mysql_database").c_str();
// `connect(const Properties& props)` segfaults in windows debug, but // `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 // `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()) { #if defined(DARKFLAME_PLATFORM_WIN32) && defined(_DEBUG)
con = driver->connect(properties);
} else {
con = driver->connect(properties["hostName"].c_str(), properties["user"].c_str(), properties["password"].c_str()); 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()); con->setSchema(databaseName.c_str());
} }
@@ -67,7 +68,7 @@ void MySQLDatabase::ExecuteCustomQuery(const std::string_view query) {
sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) { sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) {
if (!con) { if (!con) {
Connect(); Database::Get()->Connect();
LOG("Trying to reconnect to MySQL"); LOG("Trying to reconnect to MySQL");
} }
@@ -76,7 +77,7 @@ sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& quer
con = nullptr; con = nullptr;
Connect(); Database::Get()->Connect();
LOG("Trying to reconnect to MySQL from invalid or closed connection"); LOG("Trying to reconnect to MySQL from invalid or closed connection");
} }

View File

@@ -7,6 +7,7 @@
#include "GameDatabase.h" #include "GameDatabase.h"
typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef; typedef std::unique_ptr<sql::PreparedStatement>& UniquePreppedStmtRef;
typedef std::unique_ptr<sql::ResultSet> UniqueResultSet;
// Purposefully no definition for this to provide linker errors in the case someone tries to // Purposefully no definition for this to provide linker errors in the case someone tries to
// bind a parameter to a type that isn't defined. // bind a parameter to a type that isn't defined.
@@ -29,7 +30,6 @@ public:
void Connect() override; void Connect() override;
void Destroy(std::string source = "") override; void Destroy(std::string source = "") override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override;
void Commit() override; void Commit() override;
bool GetAutoCommit() override; bool GetAutoCommit() override;
void SetAutoCommit(bool value) override; void SetAutoCommit(bool value) override;
@@ -111,6 +111,21 @@ public:
void AddBehavior(const IBehaviors::Info& info) override; void AddBehavior(const IBehaviors::Info& info) override;
std::string GetBehavior(const int32_t behaviorId) override; std::string GetBehavior(const int32_t behaviorId) override;
void RemoveBehavior(const int32_t characterId) 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;
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override;
void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override;
void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override;
void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
uint32_t GetAccountCount() override;
private: private:
// Generic query functions that can be used for any query. // Generic query functions that can be used for any query.

View File

@@ -35,3 +35,12 @@ void MySQLDatabase::UpdateAccountPassword(const uint32_t accountId, const std::s
void MySQLDatabase::InsertNewAccount(const std::string_view username, const std::string_view bcryptpassword) { 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)); 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);
}
uint32_t MySQLDatabase::GetAccountCount() {
auto res = ExecuteSelect("SELECT COUNT(*) as count FROM accounts;");
return res->next() ? res->getUInt("count") : 0;
}

View File

@@ -20,6 +20,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"PropertyContents.cpp" "PropertyContents.cpp"
"Servers.cpp" "Servers.cpp"
"Ugc.cpp" "Ugc.cpp"
"UgcModularBuild.cpp"
PARENT_SCOPE PARENT_SCOPE
) )

View File

@@ -1,5 +1,9 @@
#include "MySQLDatabase.h" #include "MySQLDatabase.h"
#include "Game.h"
#include "Logger.h"
#include "dConfig.h"
std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityId) { std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityId) {
auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId); auto donation_total = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId);
@@ -9,3 +13,79 @@ std::optional<uint32_t> MySQLDatabase::GetDonationTotal(const uint32_t activityI
return donation_total->getUInt("donation_total"); return donation_total->getUInt("donation_total");
} }
std::vector<ILeaderboard::Entry> ProcessQuery(UniqueResultSet& rows) {
std::vector<ILeaderboard::Entry> entries;
entries.reserve(rows->rowsCount());
while (rows->next()) {
auto& entry = entries.emplace_back();
entry.charId = rows->getUInt("character_id");
entry.lastPlayedTimestamp = rows->getUInt("lp_unix");
entry.primaryScore = rows->getFloat("primaryScore");
entry.secondaryScore = rows->getFloat("secondaryScore");
entry.tertiaryScore = rows->getFloat("tertiaryScore");
entry.numWins = rows->getUInt("numWins");
entry.numTimesPlayed = rows->getUInt("timesPlayed");
entry.name = rows->getString("char_name");
// entry.ranking is never set because its calculated in leaderboard in code.
}
return entries;
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetDescendingLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetAscendingLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore ASC, secondaryscore ASC, tertiaryScore ASC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetAgsLeaderboard(const uint32_t activityId) {
auto query = Game::config->GetValue("classic_survival_scoring") != "1" ?
"SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;" :
"SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY secondaryscore DESC, primaryscore DESC, tertiaryScore DESC, last_played ASC;";
auto leaderboard = ExecuteSelect(query, activityId);
return ProcessQuery(leaderboard);
}
std::vector<ILeaderboard::Entry> MySQLDatabase::GetNsLeaderboard(const uint32_t activityId) {
auto leaderboard = ExecuteSelect("SELECT *, UNIX_TIMESTAMP(last_played) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore ASC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(leaderboard);
}
void MySQLDatabase::SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("INSERT leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, character_id = ?, game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
void MySQLDatabase::UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("UPDATE leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
void MySQLDatabase::IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId);
}
std::optional<ILeaderboard::Score> MySQLDatabase::GetPlayerScore(const uint32_t playerId, const uint32_t gameId) {
std::optional<ILeaderboard::Score> toReturn = std::nullopt;
auto res = ExecuteSelect("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;", playerId, gameId);
if (res->next()) {
toReturn = ILeaderboard::Score{
.primaryScore = res->getFloat("primaryScore"),
.secondaryScore = res->getFloat("secondaryScore"),
.tertiaryScore = res->getFloat("tertiaryScore")
};
}
return toReturn;
}
void MySQLDatabase::IncrementNumWins(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;", playerId, gameId);
}

View File

@@ -1,8 +1,143 @@
#include "MySQLDatabase.h" #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()) {
if (!result) result = IProperty::PropertyEntranceResult();
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()) {
if (!result) result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count->getUInt("count");
}
}
while (properties->next()) {
if (!result) result = IProperty::PropertyEntranceResult();
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) { std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID mapId, const LWOCLONEID cloneId) {
auto propertyEntry = ExecuteSelect( 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); "FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId);
if (!propertyEntry->next()) { if (!propertyEntry->next()) {
@@ -21,6 +156,7 @@ std::optional<IProperty::Info> MySQLDatabase::GetPropertyInfo(const LWOMAPID map
toReturn.claimedTime = propertyEntry->getUInt("time_claimed"); toReturn.claimedTime = propertyEntry->getUInt("time_claimed");
toReturn.reputation = propertyEntry->getUInt("reputation"); toReturn.reputation = propertyEntry->getUInt("reputation");
toReturn.modApproved = propertyEntry->getUInt("mod_approved"); toReturn.modApproved = propertyEntry->getUInt("mod_approved");
toReturn.performanceCost = propertyEntry->getFloat("performance_cost");
return toReturn; return toReturn;
} }

View File

@@ -0,0 +1,9 @@
#include "MySQLDatabase.h"
void MySQLDatabase::InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) {
ExecuteInsert("INSERT INTO ugc_modular_build (ugc_id, ldf_config, character_id) VALUES (?,?,?)", bigId, modules, characterId);
}
void MySQLDatabase::DeleteUgcBuild(const LWOOBJID bigId) {
ExecuteDelete("DELETE FROM ugc_modular_build WHERE ugc_id = ?;", bigId);
}

View File

@@ -0,0 +1,11 @@
SET(DDATABSE_DATABSES_SQLITE_SOURCES
"SQLiteDatabase.cpp"
)
add_subdirectory(Tables)
foreach(file ${DDATABASES_DATABASES_SQLITE_TABLES_SOURCES})
set(DDATABSE_DATABSES_SQLITE_SOURCES ${DDATABSE_DATABSES_SQLITE_SOURCES} "Tables/${file}")
endforeach()
set(DDATABSE_DATABSES_SQLITE_SOURCES ${DDATABSE_DATABSES_SQLITE_SOURCES} PARENT_SCOPE)

View File

@@ -0,0 +1,81 @@
#include "SQLiteDatabase.h"
#include "Database.h"
#include "Game.h"
#include "dConfig.h"
#include "Logger.h"
#include "dPlatforms.h"
#include "BinaryPathFinder.h"
// Static Variables
// Status Variables
namespace {
CppSQLite3DB* con = nullptr;
bool isConnected = false;
};
void SQLiteDatabase::Connect() {
LOG("Using SQLite database");
con = new CppSQLite3DB();
const auto path = BinaryPathFinder::GetBinaryDir() / Game::config->GetValue("sqlite_database_path");
if (!std::filesystem::exists(path)) {
LOG("Creating sqlite path %s", path.string().c_str());
std::filesystem::create_directories(path.parent_path());
}
con->open(path.string().c_str());
isConnected = true;
// Make sure wal is enabled for the database.
con->execQuery("PRAGMA journal_mode = WAL;");
}
void SQLiteDatabase::Destroy(std::string source) {
if (!con) return;
if (source.empty()) LOG("Destroying SQLite connection!");
else LOG("Destroying SQLite connection from %s!", source.c_str());
con->close();
delete con;
con = nullptr;
}
void SQLiteDatabase::ExecuteCustomQuery(const std::string_view query) {
con->compileStatement(query.data()).execDML();
}
CppSQLite3Statement SQLiteDatabase::CreatePreppedStmt(const std::string& query) {
return con->compileStatement(query.c_str());
}
void SQLiteDatabase::Commit() {
if (!con->IsAutoCommitOn()) con->compileStatement("COMMIT;").execDML();
}
bool SQLiteDatabase::GetAutoCommit() {
return con->IsAutoCommitOn();
}
void SQLiteDatabase::SetAutoCommit(bool value) {
if (value) {
if (GetAutoCommit()) con->compileStatement("BEGIN;").execDML();
} else {
if (!GetAutoCommit()) con->compileStatement("COMMIT;").execDML();
}
}
void SQLiteDatabase::DeleteCharacter(const uint32_t characterId) {
ExecuteDelete("DELETE FROM charxml WHERE id=?;", characterId);
ExecuteDelete("DELETE FROM command_log WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM friends WHERE player_id=? OR friend_id=?;", characterId, characterId);
ExecuteDelete("DELETE FROM leaderboard WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM properties_contents WHERE property_id IN (SELECT id FROM properties WHERE owner_id=?);", characterId);
ExecuteDelete("DELETE FROM properties WHERE owner_id=?;", characterId);
ExecuteDelete("DELETE FROM ugc WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM activity_log WHERE character_id=?;", characterId);
ExecuteDelete("DELETE FROM mail WHERE receiver_id=?;", characterId);
ExecuteDelete("DELETE FROM charinfo WHERE id=?;", characterId);
}

View File

@@ -0,0 +1,270 @@
#ifndef SQLITEDATABASE_H
#define SQLITEDATABASE_H
#include "CppSQLite3.h"
#include "GameDatabase.h"
using PreppedStmtRef = CppSQLite3Statement&;
// Purposefully no definition for this to provide linker errors in the case someone tries to
// bind a parameter to a type that isn't defined.
template<typename ParamType>
inline void SetParam(PreppedStmtRef stmt, const int index, const ParamType param);
// This is a function to set each parameter in a prepared statement.
// This is accomplished with a combination of parameter packing and Fold Expressions.
// The constexpr if statement is used to prevent the compiler from trying to call SetParam with 0 arguments.
template<typename... Args>
void SetParams(PreppedStmtRef stmt, Args&&... args) {
if constexpr (sizeof...(args) != 0) {
int i = 1;
(SetParam(stmt, i++, args), ...);
}
}
class SQLiteDatabase : public GameDatabase {
public:
void Connect() override;
void Destroy(std::string source = "") 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 characterId) override;
void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) override;
std::optional<IProperty::PropertyEntranceResult> GetProperties(const IProperty::PropertyLookup& params) override;
std::vector<ILeaderboard::Entry> GetDescendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAscendingLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetNsLeaderboard(const uint32_t activityId) override;
std::vector<ILeaderboard::Entry> GetAgsLeaderboard(const uint32_t activityId) override;
void SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
void UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) override;
std::optional<ILeaderboard::Score> GetPlayerScore(const uint32_t playerId, const uint32_t gameId) override;
void IncrementNumWins(const uint32_t playerId, const uint32_t gameId) override;
void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override;
uint32_t GetAccountCount() override;
private:
CppSQLite3Statement CreatePreppedStmt(const std::string& query);
// Generic query functions that can be used for any query.
// Return type may be different depending on the query, so it is up to the caller to check the return type.
// The first argument is the query string, and the rest are the parameters to bind to the query.
// The return type is a unique_ptr to the result set, which is deleted automatically when it goes out of scope
template<typename... Args>
inline std::pair<CppSQLite3Statement, CppSQLite3Query> ExecuteSelect(const std::string& query, Args&&... args) {
std::pair<CppSQLite3Statement, CppSQLite3Query> toReturn;
toReturn.first = CreatePreppedStmt(query);
SetParams(toReturn.first, std::forward<Args>(args)...);
DLU_SQL_TRY_CATCH_RETHROW(toReturn.second = toReturn.first.execQuery());
return toReturn;
}
template<typename... Args>
inline void ExecuteDelete(const std::string& query, Args&&... args) {
auto preppedStmt = CreatePreppedStmt(query);
SetParams(preppedStmt, std::forward<Args>(args)...);
DLU_SQL_TRY_CATCH_RETHROW(preppedStmt.execDML());
}
template<typename... Args>
inline int32_t ExecuteUpdate(const std::string& query, Args&&... args) {
auto preppedStmt = CreatePreppedStmt(query);
SetParams(preppedStmt, std::forward<Args>(args)...);
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt.execDML());
}
template<typename... Args>
inline int ExecuteInsert(const std::string& query, Args&&... args) {
auto preppedStmt = CreatePreppedStmt(query);
SetParams(preppedStmt, std::forward<Args>(args)...);
DLU_SQL_TRY_CATCH_RETHROW(return preppedStmt.execDML());
}
};
// Below are each of the definitions of SetParam for each supported type.
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string_view param) {
LOG("%s", param.data());
stmt.bind(index, param.data());
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const char* param) {
LOG("%s", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::string param) {
LOG("%s", param.c_str());
stmt.bind(index, param.c_str());
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int8_t param) {
LOG("%u", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint8_t param) {
LOG("%d", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int16_t param) {
LOG("%u", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint16_t param) {
LOG("%d", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint32_t param) {
LOG("%u", param);
stmt.bind(index, static_cast<int32_t>(param));
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int32_t param) {
LOG("%d", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const int64_t param) {
LOG("%llu", param);
stmt.bind(index, static_cast<sqlite_int64>(param));
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const uint64_t param) {
LOG("%llu", param);
stmt.bind(index, static_cast<sqlite_int64>(param));
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const float param) {
LOG("%f", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const double param) {
LOG("%f", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const bool param) {
LOG("%d", param);
stmt.bind(index, param);
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::istream* param) {
LOG("Blob");
// This is the one time you will ever see me use const_cast.
std::stringstream stream;
stream << param->rdbuf();
stmt.bind(index, reinterpret_cast<const unsigned char*>(stream.str().c_str()), stream.str().size());
}
template<>
inline void SetParam(PreppedStmtRef stmt, const int index, const std::optional<uint32_t> param) {
if (param) {
LOG("%d", param.value());
stmt.bind(index, static_cast<int>(param.value()));
} else {
LOG("Null");
stmt.bindNull(index);
}
}
#endif //!SQLITEDATABASE_H

View File

@@ -0,0 +1,49 @@
#include "SQLiteDatabase.h"
#include "eGameMasterLevel.h"
#include "Database.h"
std::optional<IAccounts::Info> SQLiteDatabase::GetAccountInfo(const std::string_view username) {
auto [_, result] = ExecuteSelect("SELECT * FROM accounts WHERE name = ? LIMIT 1", username);
if (result.eof()) {
return std::nullopt;
}
IAccounts::Info toReturn;
toReturn.id = result.getIntField("id");
toReturn.maxGmLevel = static_cast<eGameMasterLevel>(result.getIntField("gm_level"));
toReturn.bcryptPassword = result.getStringField("password");
toReturn.banned = result.getIntField("banned");
toReturn.locked = result.getIntField("locked");
toReturn.playKeyId = result.getIntField("play_key_id");
return toReturn;
}
void SQLiteDatabase::UpdateAccountUnmuteTime(const uint32_t accountId, const uint64_t timeToUnmute) {
ExecuteUpdate("UPDATE accounts SET mute_expire = ? WHERE id = ?;", timeToUnmute, accountId);
}
void SQLiteDatabase::UpdateAccountBan(const uint32_t accountId, const bool banned) {
ExecuteUpdate("UPDATE accounts SET banned = ? WHERE id = ?;", banned, accountId);
}
void SQLiteDatabase::UpdateAccountPassword(const uint32_t accountId, const std::string_view bcryptpassword) {
ExecuteUpdate("UPDATE accounts SET password = ? WHERE id = ?;", bcryptpassword, accountId);
}
void SQLiteDatabase::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 SQLiteDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
}
uint32_t SQLiteDatabase::GetAccountCount() {
auto [_, res] = ExecuteSelect("SELECT COUNT(*) as count FROM accounts;");
if (res.eof()) return 0;
return res.getIntField("count");
}

View File

@@ -0,0 +1,17 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) {
ExecuteInsert("INSERT OR IGNORE INTO accounts_rewardcodes (account_id, rewardcode) VALUES (?, ?);", account_id, reward_code);
}
std::vector<uint32_t> SQLiteDatabase::GetRewardCodesByAccountID(const uint32_t account_id) {
auto [_, result] = ExecuteSelect("SELECT rewardcode FROM accounts_rewardcodes WHERE account_id = ?;", account_id);
std::vector<uint32_t> toReturn;
while (!result.eof()) {
toReturn.push_back(result.getIntField("rewardcode"));
result.nextRow();
}
return toReturn;
}

View File

@@ -0,0 +1,6 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::UpdateActivityLog(const uint32_t characterId, const eActivityType activityType, const LWOMAPID mapId) {
ExecuteInsert("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);",
characterId, static_cast<uint32_t>(activityType), static_cast<uint32_t>(time(NULL)), mapId);
}

View File

@@ -0,0 +1,19 @@
#include "IBehaviors.h"
#include "SQLiteDatabase.h"
void SQLiteDatabase::AddBehavior(const IBehaviors::Info& info) {
ExecuteInsert(
"INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON CONFLICT(behavior_id) DO UPDATE SET behavior_info = ?",
info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo
);
}
void SQLiteDatabase::RemoveBehavior(const int32_t behaviorId) {
ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId);
}
std::string SQLiteDatabase::GetBehavior(const int32_t behaviorId) {
auto [_, result] = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId);
return !result.eof() ? result.getStringField("behavior_info") : "";
}

View File

@@ -0,0 +1,6 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::InsertNewBugReport(const IBugReports::Info& info) {
ExecuteInsert("INSERT INTO `bug_reports`(body, client_version, other_player_id, selection, reporter_id) VALUES (?, ?, ?, ?, ?)",
info.body, info.clientVersion, info.otherPlayer, info.selection, info.characterId);
}

View File

@@ -0,0 +1,26 @@
set(DDATABASES_DATABASES_SQLITE_TABLES_SOURCES
"Accounts.cpp"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp"
"CharInfo.cpp"
"CharXml.cpp"
"CommandLog.cpp"
"Friends.cpp"
"IgnoreList.cpp"
"Leaderboard.cpp"
"Mail.cpp"
"MigrationHistory.cpp"
"ObjectIdTracker.cpp"
"PetNames.cpp"
"PlayerCheatDetections.cpp"
"PlayKeys.cpp"
"Property.cpp"
"PropertyContents.cpp"
"Servers.cpp"
"Ugc.cpp"
"UgcModularBuild.cpp"
PARENT_SCOPE
)

View File

@@ -0,0 +1,79 @@
#include "SQLiteDatabase.h"
std::vector<std::string> SQLiteDatabase::GetApprovedCharacterNames() {
auto [_, result] = ExecuteSelect("SELECT name FROM charinfo;");
std::vector<std::string> toReturn;
while (!result.eof()) {
toReturn.push_back(result.getStringField("name"));
result.nextRow();
}
return toReturn;
}
std::optional<ICharInfo::Info> CharInfoFromQueryResult(CppSQLite3Query stmt) {
if (stmt.eof()) {
return std::nullopt;
}
ICharInfo::Info toReturn;
toReturn.id = stmt.getIntField("id");
toReturn.name = stmt.getStringField("name");
toReturn.pendingName = stmt.getStringField("pending_name");
toReturn.needsRename = stmt.getIntField("needs_rename");
toReturn.cloneId = stmt.getInt64Field("prop_clone_id");
toReturn.accountId = stmt.getIntField("account_id");
toReturn.permissionMap = static_cast<ePermissionMap>(stmt.getIntField("permission_map"));
return toReturn;
}
std::optional<ICharInfo::Info> SQLiteDatabase::GetCharacterInfo(const uint32_t charId) {
return CharInfoFromQueryResult(
ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE id = ? LIMIT 1;", charId).second
);
}
std::optional<ICharInfo::Info> SQLiteDatabase::GetCharacterInfo(const std::string_view name) {
return CharInfoFromQueryResult(
ExecuteSelect("SELECT name, pending_name, needs_rename, prop_clone_id, permission_map, id, account_id FROM charinfo WHERE name = ? LIMIT 1;", name).second
);
}
std::vector<uint32_t> SQLiteDatabase::GetAccountCharacterIds(const uint32_t accountId) {
auto [_, result] = ExecuteSelect("SELECT id FROM charinfo WHERE account_id = ? ORDER BY last_login DESC LIMIT 4;", accountId);
std::vector<uint32_t> toReturn;
while (!result.eof()) {
toReturn.push_back(result.getIntField("id"));
result.nextRow();
}
return toReturn;
}
void SQLiteDatabase::InsertNewCharacter(const ICharInfo::Info info) {
ExecuteInsert(
"INSERT INTO `charinfo`(`id`, `account_id`, `name`, `pending_name`, `needs_rename`, `last_login`, `prop_clone_id`) VALUES (?,?,?,?,?,?,(SELECT IFNULL(MAX(`prop_clone_id`), 0) + 1 FROM `charinfo`))",
info.id,
info.accountId,
info.name,
info.pendingName,
false,
static_cast<uint32_t>(time(NULL)));
}
void SQLiteDatabase::SetCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET name = ?, pending_name = '', needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId);
}
void SQLiteDatabase::SetPendingCharacterName(const uint32_t characterId, const std::string_view name) {
ExecuteUpdate("UPDATE charinfo SET pending_name = ?, needs_rename = 0, last_login = ? WHERE id = ?;", name, static_cast<uint32_t>(time(NULL)), characterId);
}
void SQLiteDatabase::UpdateLastLoggedInCharacter(const uint32_t characterId) {
ExecuteUpdate("UPDATE charinfo SET last_login = ? WHERE id = ?;", static_cast<uint32_t>(time(NULL)), characterId);
}

View File

@@ -0,0 +1,19 @@
#include "SQLiteDatabase.h"
std::string SQLiteDatabase::GetCharacterXml(const uint32_t charId) {
auto [_, result] = ExecuteSelect("SELECT xml_data FROM charxml WHERE id = ? LIMIT 1;", charId);
if (result.eof()) {
return "";
}
return result.getStringField("xml_data");
}
void SQLiteDatabase::UpdateCharacterXml(const uint32_t charId, const std::string_view lxfml) {
ExecuteUpdate("UPDATE charxml SET xml_data = ? WHERE id = ?;", lxfml, charId);
}
void SQLiteDatabase::InsertCharacterXml(const uint32_t characterId, const std::string_view lxfml) {
ExecuteInsert("INSERT INTO `charxml` (`id`, `xml_data`) VALUES (?,?)", characterId, lxfml);
}

View File

@@ -0,0 +1,5 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::InsertSlashCommandUsage(const uint32_t characterId, const std::string_view command) {
ExecuteInsert("INSERT INTO command_log (character_id, command) VALUES (?, ?);", characterId, command);
}

View File

@@ -0,0 +1,73 @@
#include "SQLiteDatabase.h"
std::vector<FriendData> SQLiteDatabase::GetFriendsList(const uint32_t charId) {
auto [_, friendsList] = ExecuteSelect(
R"QUERY(
SELECT fr.requested_player AS player, best_friend AS bff, ci.name AS name FROM
(
SELECT CASE
WHEN player_id = ? THEN friend_id
WHEN friend_id = ? THEN player_id
END AS requested_player, best_friend FROM friends
) AS fr
JOIN charinfo AS ci ON ci.id = fr.requested_player
WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?;
)QUERY", charId, charId, charId);
std::vector<FriendData> toReturn;
while (!friendsList.eof()) {
FriendData fd;
fd.friendID = friendsList.getIntField("player");
fd.isBestFriend = friendsList.getIntField("bff") == 3; // 0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
fd.friendName = friendsList.getStringField("name");
toReturn.push_back(fd);
friendsList.nextRow();
}
return toReturn;
}
std::optional<IFriends::BestFriendStatus> SQLiteDatabase::GetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
auto [_, result] = ExecuteSelect("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;",
playerCharacterId,
friendCharacterId,
friendCharacterId,
playerCharacterId
);
if (result.eof()) {
return std::nullopt;
}
IFriends::BestFriendStatus toReturn;
toReturn.playerCharacterId = result.getIntField("player_id");
toReturn.friendCharacterId = result.getIntField("friend_id");
toReturn.bestFriendStatus = result.getIntField("best_friend");
return toReturn;
}
void SQLiteDatabase::SetBestFriendStatus(const uint32_t playerCharacterId, const uint32_t friendCharacterId, const uint32_t bestFriendStatus) {
ExecuteUpdate("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?);",
bestFriendStatus,
playerCharacterId,
friendCharacterId,
friendCharacterId,
playerCharacterId
);
}
void SQLiteDatabase::AddFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
ExecuteInsert("INSERT OR IGNORE INTO friends (player_id, friend_id, best_friend) VALUES (?, ?, 0);", playerCharacterId, friendCharacterId);
}
void SQLiteDatabase::RemoveFriend(const uint32_t playerCharacterId, const uint32_t friendCharacterId) {
ExecuteDelete("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?);",
playerCharacterId,
friendCharacterId,
friendCharacterId,
playerCharacterId
);
}

View File

@@ -0,0 +1,22 @@
#include "SQLiteDatabase.h"
std::vector<IIgnoreList::Info> SQLiteDatabase::GetIgnoreList(const uint32_t playerId) {
auto [_, result] = ExecuteSelect("SELECT ci.name AS name, il.ignored_player_id AS ignore_id FROM ignore_list AS il JOIN charinfo AS ci ON il.ignored_player_id = ci.id WHERE il.player_id = ?", playerId);
std::vector<IIgnoreList::Info> ignoreList;
while (!result.eof()) {
ignoreList.push_back(IIgnoreList::Info{ result.getStringField("name"), static_cast<uint32_t>(result.getIntField("ignore_id")) });
result.nextRow();
}
return ignoreList;
}
void SQLiteDatabase::AddIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteInsert("INSERT OR IGNORE INTO ignore_list (player_id, ignored_player_id) VALUES (?, ?)", playerId, ignoredPlayerId);
}
void SQLiteDatabase::RemoveIgnore(const uint32_t playerId, const uint32_t ignoredPlayerId) {
ExecuteDelete("DELETE FROM ignore_list WHERE player_id = ? AND ignored_player_id = ?", playerId, ignoredPlayerId);
}

View File

@@ -0,0 +1,91 @@
#include "SQLiteDatabase.h"
#include "Game.h"
#include "Logger.h"
#include "dConfig.h"
std::optional<uint32_t> SQLiteDatabase::GetDonationTotal(const uint32_t activityId) {
auto [_, donation_total] = ExecuteSelect("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;", activityId);
if (donation_total.eof()) {
return std::nullopt;
}
return donation_total.getIntField("donation_total");
}
std::vector<ILeaderboard::Entry> ProcessQuery(CppSQLite3Query& rows) {
std::vector<ILeaderboard::Entry> entries;
while (!rows.eof()) {
auto& entry = entries.emplace_back();
entry.charId = rows.getIntField("character_id");
entry.lastPlayedTimestamp = rows.getIntField("lp_unix");
entry.primaryScore = rows.getFloatField("primaryScore");
entry.secondaryScore = rows.getFloatField("secondaryScore");
entry.tertiaryScore = rows.getFloatField("tertiaryScore");
entry.numWins = rows.getIntField("numWins");
entry.numTimesPlayed = rows.getIntField("timesPlayed");
entry.name = rows.getStringField("char_name");
// entry.ranking is never set because its calculated in leaderboard in code.
rows.nextRow();
}
return entries;
}
std::vector<ILeaderboard::Entry> SQLiteDatabase::GetDescendingLeaderboard(const uint32_t activityId) {
auto [_, result] = ExecuteSelect("SELECT *, CAST(strftime('%s', last_played) as INT) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(result);
}
std::vector<ILeaderboard::Entry> SQLiteDatabase::GetAscendingLeaderboard(const uint32_t activityId) {
auto [_, result] = ExecuteSelect("SELECT *, CAST(strftime('%s', last_played) as INT) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore ASC, secondaryscore ASC, tertiaryScore ASC, last_played ASC;", activityId);
return ProcessQuery(result);
}
std::vector<ILeaderboard::Entry> SQLiteDatabase::GetAgsLeaderboard(const uint32_t activityId) {
auto query = Game::config->GetValue("classic_survival_scoring") != "1" ?
"SELECT *, CAST(strftime('%s', last_played) as INT) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore DESC, tertiaryScore DESC, last_played ASC;" :
"SELECT *, CAST(strftime('%s', last_played) as INT) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY secondaryscore DESC, primaryscore DESC, tertiaryScore DESC, last_played ASC;";
auto [_, result] = ExecuteSelect(query, activityId);
return ProcessQuery(result);
}
std::vector<ILeaderboard::Entry> SQLiteDatabase::GetNsLeaderboard(const uint32_t activityId) {
auto [_, result] = ExecuteSelect("SELECT *, CAST(strftime('%s', last_played) as INT) as lp_unix, ci.name as char_name FROM leaderboard lb JOIN charinfo ci on ci.id = lb.character_id where game_id = ? ORDER BY primaryscore DESC, secondaryscore ASC, tertiaryScore DESC, last_played ASC;", activityId);
return ProcessQuery(result);
}
void SQLiteDatabase::SaveScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("INSERT INTO leaderboard (primaryScore, secondaryScore, tertiaryScore, character_id, game_id, last_played) VALUES (?,?,?,?,?,CURRENT_TIMESTAMP) ;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
void SQLiteDatabase::UpdateScore(const uint32_t playerId, const uint32_t gameId, const Score& score) {
ExecuteInsert("UPDATE leaderboard SET primaryScore = ?, secondaryScore = ?, tertiaryScore = ?, timesPlayed = timesPlayed + 1, last_played = CURRENT_TIMESTAMP WHERE character_id = ? AND game_id = ?;",
score.primaryScore, score.secondaryScore, score.tertiaryScore, playerId, gameId);
}
std::optional<ILeaderboard::Score> SQLiteDatabase::GetPlayerScore(const uint32_t playerId, const uint32_t gameId) {
std::optional<ILeaderboard::Score> toReturn = std::nullopt;
auto [_, res] = ExecuteSelect("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;", playerId, gameId);
if (!res.eof()) {
toReturn = ILeaderboard::Score{
.primaryScore = static_cast<float>(res.getFloatField("primaryScore")),
.secondaryScore = static_cast<float>(res.getFloatField("secondaryScore")),
.tertiaryScore = static_cast<float>(res.getFloatField("tertiaryScore"))
};
}
return toReturn;
}
void SQLiteDatabase::IncrementNumWins(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET numWins = numWins + 1, last_played = CURRENT_TIMESTAMP WHERE character_id = ? AND game_id = ?;", playerId, gameId);
}
void SQLiteDatabase::IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) {
ExecuteUpdate("UPDATE leaderboard SET timesPlayed = timesPlayed + 1, last_played = CURRENT_TIMESTAMP WHERE character_id = ? AND game_id = ?;", playerId, gameId);
}

View File

@@ -0,0 +1,83 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::InsertNewMail(const IMail::MailInfo& mail) {
ExecuteInsert(
"INSERT INTO `mail` "
"(`sender_id`, `sender_name`, `receiver_id`, `receiver_name`, `time_sent`, `subject`, `body`, `attachment_id`, `attachment_lot`, `attachment_subkey`, `attachment_count`, `was_read`)"
" VALUES (?,?,?,?,?,?,?,?,?,?,?,0)",
mail.senderId,
mail.senderUsername,
mail.receiverId,
mail.recipient,
static_cast<uint32_t>(time(NULL)),
mail.subject,
mail.body,
mail.itemID,
mail.itemLOT,
0,
mail.itemCount);
}
std::vector<IMail::MailInfo> SQLiteDatabase::GetMailForPlayer(const uint32_t characterId, const uint32_t numberOfMail) {
auto [_, res] = ExecuteSelect(
"SELECT id, subject, body, sender_name, attachment_id, attachment_lot, attachment_subkey, attachment_count, was_read, time_sent"
" FROM mail WHERE receiver_id=? limit ?;",
characterId, numberOfMail);
std::vector<IMail::MailInfo> toReturn;
while (!res.eof()) {
IMail::MailInfo mail;
mail.id = res.getInt64Field("id");
mail.subject = res.getStringField("subject");
mail.body = res.getStringField("body");
mail.senderUsername = res.getStringField("sender_name");
mail.itemID = res.getIntField("attachment_id");
mail.itemLOT = res.getIntField("attachment_lot");
mail.itemSubkey = res.getIntField("attachment_subkey");
mail.itemCount = res.getIntField("attachment_count");
mail.timeSent = res.getInt64Field("time_sent");
mail.wasRead = res.getIntField("was_read");
toReturn.push_back(std::move(mail));
res.nextRow();
}
return toReturn;
}
std::optional<IMail::MailInfo> SQLiteDatabase::GetMail(const uint64_t mailId) {
auto [_, res] = ExecuteSelect("SELECT attachment_lot, attachment_count FROM mail WHERE id=? LIMIT 1;", mailId);
if (res.eof()) {
return std::nullopt;
}
IMail::MailInfo toReturn;
toReturn.itemLOT = res.getIntField("attachment_lot");
toReturn.itemCount = res.getIntField("attachment_count");
return toReturn;
}
uint32_t SQLiteDatabase::GetUnreadMailCount(const uint32_t characterId) {
auto [_, res] = ExecuteSelect("SELECT COUNT(*) AS number_unread FROM mail WHERE receiver_id=? AND was_read=0;", characterId);
if (res.eof()) {
return 0;
}
return res.getIntField("number_unread");
}
void SQLiteDatabase::MarkMailRead(const uint64_t mailId) {
ExecuteUpdate("UPDATE mail SET was_read=1 WHERE id=?;", mailId);
}
void SQLiteDatabase::ClaimMailItem(const uint64_t mailId) {
ExecuteUpdate("UPDATE mail SET attachment_lot=0 WHERE id=?;", mailId);
}
void SQLiteDatabase::DeleteMail(const uint64_t mailId) {
ExecuteDelete("DELETE FROM mail WHERE id=?;", mailId);
}

View File

@@ -0,0 +1,13 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::CreateMigrationHistoryTable() {
ExecuteInsert("CREATE TABLE IF NOT EXISTS migration_history (name TEXT NOT NULL, date DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);");
}
bool SQLiteDatabase::IsMigrationRun(const std::string_view str) {
return !ExecuteSelect("SELECT name FROM migration_history WHERE name = ?;", str).second.eof();
}
void SQLiteDatabase::InsertMigration(const std::string_view str) {
ExecuteInsert("INSERT INTO migration_history (name) VALUES (?);", str);
}

View File

@@ -0,0 +1,17 @@
#include "SQLiteDatabase.h"
std::optional<uint32_t> SQLiteDatabase::GetCurrentPersistentId() {
auto [_, result] = ExecuteSelect("SELECT last_object_id FROM object_id_tracker");
if (result.eof()) {
return std::nullopt;
}
return result.getIntField("last_object_id");
}
void SQLiteDatabase::InsertDefaultPersistentId() {
ExecuteInsert("INSERT INTO object_id_tracker VALUES (1);");
}
void SQLiteDatabase::UpdatePersistentId(const uint32_t newId) {
ExecuteUpdate("UPDATE object_id_tracker SET last_object_id = ?;", newId);
}

View File

@@ -0,0 +1,26 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::SetPetNameModerationStatus(const LWOOBJID& petId, const IPetNames::Info& info) {
ExecuteInsert(
"INSERT INTO `pet_names` (`id`, `pet_name`, `approved`) VALUES (?, ?, ?) "
"ON CONFLICT(id) DO UPDATE SET pet_name = ?, approved = ?;",
petId,
info.petName,
info.approvalStatus,
info.petName,
info.approvalStatus);
}
std::optional<IPetNames::Info> SQLiteDatabase::GetPetNameInfo(const LWOOBJID& petId) {
auto [_, result] = ExecuteSelect("SELECT pet_name, approved FROM pet_names WHERE id = ? LIMIT 1;", petId);
if (result.eof()) {
return std::nullopt;
}
IPetNames::Info toReturn;
toReturn.petName = result.getStringField("pet_name");
toReturn.approvalStatus = result.getIntField("approved");
return toReturn;
}

View File

@@ -0,0 +1,11 @@
#include "SQLiteDatabase.h"
std::optional<bool> SQLiteDatabase::IsPlaykeyActive(const int32_t playkeyId) {
auto [_, keyCheckRes] = ExecuteSelect("SELECT active FROM `play_keys` WHERE id=?", playkeyId);
if (keyCheckRes.eof()) {
return std::nullopt;
}
return keyCheckRes.getIntField("active");
}

View File

@@ -0,0 +1,7 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::InsertCheatDetection(const IPlayerCheatDetections::Info& info) {
ExecuteInsert(
"INSERT INTO player_cheat_detections (account_id, name, violation_msg, violation_system_address) VALUES (?, ?, ?, ?)",
info.userId, info.username, info.extraMessage, info.systemAddress);
}

View File

@@ -0,0 +1,195 @@
#include "SQLiteDatabase.h"
#include "ePropertySortType.h"
std::optional<IProperty::PropertyEntranceResult> SQLiteDatabase::GetProperties(const IProperty::PropertyLookup& params) {
std::optional<IProperty::PropertyEntranceResult> result;
std::string query;
std::pair<CppSQLite3Statement, CppSQLite3Query> propertiesRes;
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 ?;";
propertiesRes = 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.eof()) {
result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count.getIntField("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 ?;";
propertiesRes = 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.eof()) {
result = IProperty::PropertyEntranceResult();
result->totalEntriesMatchingQuery = count.getIntField("count");
}
}
auto& [_, properties] = propertiesRes;
if (!properties.eof() && !result.has_value()) result = IProperty::PropertyEntranceResult();
while (!properties.eof()) {
auto& entry = result->entries.emplace_back();
entry.id = properties.getInt64Field("id");
entry.ownerId = properties.getInt64Field("owner_id");
entry.cloneId = properties.getInt64Field("clone_id");
entry.name = properties.getStringField("name");
entry.description = properties.getStringField("description");
entry.privacyOption = properties.getIntField("privacy_option");
entry.rejectionReason = properties.getStringField("rejection_reason");
entry.lastUpdatedTime = properties.getIntField("last_updated");
entry.claimedTime = properties.getIntField("time_claimed");
entry.reputation = properties.getIntField("reputation");
entry.modApproved = properties.getIntField("mod_approved");
entry.performanceCost = properties.getFloatField("performance_cost");
properties.nextRow();
}
return result;
}
std::optional<IProperty::Info> SQLiteDatabase::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, performance_cost "
"FROM properties WHERE zone_id = ? AND clone_id = ?;", mapId, cloneId);
if (propertyEntry.eof()) {
return std::nullopt;
}
IProperty::Info toReturn;
toReturn.id = propertyEntry.getInt64Field("id");
toReturn.ownerId = propertyEntry.getInt64Field("owner_id");
toReturn.cloneId = propertyEntry.getInt64Field("clone_id");
toReturn.name = propertyEntry.getStringField("name");
toReturn.description = propertyEntry.getStringField("description");
toReturn.privacyOption = propertyEntry.getIntField("privacy_option");
toReturn.rejectionReason = propertyEntry.getStringField("rejection_reason");
toReturn.lastUpdatedTime = propertyEntry.getIntField("last_updated");
toReturn.claimedTime = propertyEntry.getIntField("time_claimed");
toReturn.reputation = propertyEntry.getIntField("reputation");
toReturn.modApproved = propertyEntry.getIntField("mod_approved");
toReturn.performanceCost = propertyEntry.getFloatField("performance_cost");
return toReturn;
}
void SQLiteDatabase::UpdatePropertyModerationInfo(const IProperty::Info& info) {
ExecuteUpdate("UPDATE properties SET privacy_option = ?, rejection_reason = ?, mod_approved = ? WHERE id = ?;",
info.privacyOption,
info.rejectionReason,
info.modApproved,
info.id);
}
void SQLiteDatabase::UpdatePropertyDetails(const IProperty::Info& info) {
ExecuteUpdate("UPDATE properties SET name = ?, description = ? WHERE id = ?;", info.name, info.description, info.id);
}
void SQLiteDatabase::UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) {
ExecuteUpdate("UPDATE properties SET performance_cost = ? WHERE zone_id = ? AND clone_id = ?;", performanceCost, zoneId.GetMapID(), zoneId.GetCloneID());
}
void SQLiteDatabase::InsertNewProperty(const IProperty::Info& info, const uint32_t templateId, const LWOZONEID& zoneId) {
auto insertion = ExecuteInsert(
"INSERT INTO properties"
" (id, owner_id, template_id, clone_id, name, description, zone_id, rent_amount, rent_due, privacy_option, last_updated, time_claimed, rejection_reason, reputation, performance_cost)"
" VALUES (?, ?, ?, ?, ?, ?, ?, 0, 0, 0, CAST(strftime('%s', 'now') as INT), CAST(strftime('%s', 'now') as INT), '', 0, 0.0)",
info.id,
info.ownerId,
templateId,
zoneId.GetCloneID(),
info.name,
info.description,
zoneId.GetMapID()
);
}

View File

@@ -0,0 +1,65 @@
#include "SQLiteDatabase.h"
std::vector<IPropertyContents::Model> SQLiteDatabase::GetPropertyModels(const LWOOBJID& 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;
while (!result.eof()) {
IPropertyContents::Model model;
model.id = result.getInt64Field("id");
model.lot = static_cast<LOT>(result.getIntField("lot"));
model.position.x = result.getFloatField("x");
model.position.y = result.getFloatField("y");
model.position.z = result.getFloatField("z");
model.rotation.w = result.getFloatField("rw");
model.rotation.x = result.getFloatField("rx");
model.rotation.y = result.getFloatField("ry");
model.rotation.z = result.getFloatField("rz");
model.ugcId = result.getInt64Field("ugc_id");
model.behaviors[0] = result.getIntField("behavior_1");
model.behaviors[1] = result.getIntField("behavior_2");
model.behaviors[2] = result.getIntField("behavior_3");
model.behaviors[3] = result.getIntField("behavior_4");
model.behaviors[4] = result.getIntField("behavior_5");
toReturn.push_back(std::move(model));
result.nextRow();
}
return toReturn;
}
void SQLiteDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) {
try {
ExecuteInsert(
"INSERT INTO properties_contents"
"(id, property_id, ugc_id, lot, x, y, z, rx, ry, rz, rw, model_name, model_description, behavior_1, behavior_2, behavior_3, behavior_4, behavior_5)"
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
// 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 18
model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<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.
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 (std::exception& e) {
LOG("Error inserting new property model: %s", e.what());
}
}
void SQLiteDatabase::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 = ?, "
"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 SQLiteDatabase::RemoveModel(const LWOOBJID& modelId) {
ExecuteDelete("DELETE FROM properties_contents WHERE id = ?;", modelId);
}

View File

@@ -0,0 +1,23 @@
#include "SQLiteDatabase.h"
void SQLiteDatabase::SetMasterIp(const std::string_view ip, const uint32_t port) {
// We only want our 1 entry anyways, so we can just delete all and reinsert the one we want
// since it would be two queries anyways.
ExecuteDelete("DELETE FROM servers;");
ExecuteInsert("INSERT INTO `servers` (`name`, `ip`, `port`, `state`, `version`) VALUES ('master', ?, ?, 0, 171022)", ip, port);
}
std::optional<IServers::MasterInfo> SQLiteDatabase::GetMasterInfo() {
auto [_, result] = ExecuteSelect("SELECT ip, port FROM servers WHERE name='master' LIMIT 1;");
if (result.eof()) {
return std::nullopt;
}
MasterInfo toReturn;
toReturn.ip = result.getStringField("ip");
toReturn.port = result.getIntField("port");
return toReturn;
}

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