Compare commits

...

81 Commits

Author SHA1 Message Date
EmosewaMC
6a4f310d89 Player: Saving fixes
Address issue of allowing a character to be saved before they were ready for updates.
2023-12-04 11:22:43 -08:00
3d85f6639e chore: rename VehiclePhysics to HavokVehiclePhysics (#1331)
since that is it's true name
and there's another VehiclePhysics component that is simply named that
2023-12-04 09:20:41 -06:00
David Markowitz
731b828c12 properties: fix visiting multiple owned by same person (#1325) 2023-12-01 10:13:01 -06:00
David Markowitz
eca87c7257 fix: Buff FX not playing and general BuffComponent improvements (#1296)
* fix: Buff FX not playing

Fixes an issue where buff effects would not play at all.

Tested that frakjaw and maelstrom dagger now play their respective effects when you are effected by them

fix: buffs general improvements

add new arguments

* Remove duplicated code

* fix times and remove buff
2023-12-01 10:12:48 -06:00
David Markowitz
0b9dbaedbf Add HailStorms guide to README (#1329) 2023-11-30 16:11:45 -08:00
7de07a7722 fix: use defaults when getting variables for jetpack behavior (#1328)
Tested that the hover jetpack now works and that the normal jetpack still works
2023-11-30 02:21:18 -08:00
David Markowitz
62a1e135c3 Fix backwards name check (#1327)
Untested.  Fixes not being able to rename a character at character create
2023-11-29 00:25:06 -08:00
Gie "Max" Vanommeslaeghe
de0b560a8c Merge pull request #1326 from DarkflameUniverse/use-more-tacarc-vars
fix: tacarc not using offset or checking upper/lower bounds
2023-11-28 22:44:00 +01:00
e8a0f50ec9 use std::abs 2023-11-27 22:18:27 -06:00
b3564cb9ea fix: tacarc not using offset or checking upper/lower bounds 2023-11-27 22:16:31 -06:00
jadebenn
3df3552467 fix: respawn rotation (#1323)
* fixed respawn rotation

* even though the condensed calls work, splitting em
2023-11-26 14:30:39 -08:00
David Markowitz
a1f8ab763d Entity: Fix death behavior of 0 (#1319) 2023-11-25 22:50:28 -06:00
David Markowitz
0c32be01ba fix: nexus tower smashable (#1320) 2023-11-25 22:50:20 -06:00
David Markowitz
8a15906885 fix: Racing end of race leaderboards now work (#1317)
* Fix UB in remote input info

Yes i should have made this first
no this wouldnt have happened with rust

* fix end of race leaderboard

Tested that with two players, both players see the others time at the end of the race and all other metrics are shown correctly.

Technically the outBitStream->Write(static_cast<uint16_t>(m_RacingPlayers.size())); should only be written once but how we do it now it is written as we load players in and this is the cheap option compared to the number of bits we are supposed to waste at the end of races
2023-11-24 01:46:26 -08:00
Gie "Max" Vanommeslaeghe
af70f871cb Merge pull request #1307 from DarkflameUniverse/985
feat: Implement ignore list
2023-11-24 00:38:03 +01:00
David Markowitz
fd20baaf09 Fix UB in remote input info (#1316)
Yes i should have made this first
no this wouldnt have happened with rust
2023-11-23 05:48:06 -06:00
jadebenn
0217f88c44 pet emote fix (#1315) 2023-11-22 08:06:09 -06:00
David Markowitz
198b3371c5 chore: Update old character create code (#1291)
* create new character fixes

Don't use persistentIds, guarantee ids are unique by using do while to generate the id

* Update queries to actually use prep stmt

* Update UserManager.cpp

* Update UserManager.cpp
2023-11-21 18:14:30 -08:00
David Markowitz
4e5facd0c5 Merge branch 'main' into 985 2023-11-21 18:11:46 -08:00
David Markowitz
c931f5d456 Merge branch 'main' into 985 2023-11-21 18:10:45 -08:00
df83f0d847 feat: Reward codes (#1308)
* feat: reward codes
this is for giving rewards across characters as the did in live.
Tested that the default config works
Tested that all claim codes work
Tested that saving and loading claim codes work
Tested that mail sends correctly

* newlines

* include array

* delete cascade

* newline

* address feedback
2023-11-21 18:05:15 -08:00
David Markowitz
9c5388c70e feat: Add ability to toggle skipping of cinematics (#1312)
* Cinematics: Add ability to toggle skipping them

* Add docs

* Move sections
2023-11-21 18:04:44 -08:00
David Markowitz
1a199151da Remove std::couts littered throughout the base (#1313) 2023-11-21 18:04:23 -08:00
David Markowitz
98dc291b57 fix gating (#1306) 2023-11-19 12:01:34 -06:00
David Markowitz
1001e41528 simplify code path 2023-11-18 20:33:30 -08:00
David Markowitz
59bf91b14f fix up enum names
Update ChatIgnoreList.cpp

Add extra check for size on adding

Cant happen without hacks, but worth checking anyways

Update ChatIgnoreList.cpp
2023-11-18 20:21:13 -08:00
David Markowitz
50ad2a29ec Merge branch 'lol' into 985 2023-11-18 19:16:56 -08:00
David Markowitz
81b1b73f04 fix gating 2023-11-18 19:15:57 -08:00
David Markowitz
12ea2dfb2e Works for online players now 2023-11-18 18:37:06 -08:00
David Markowitz
d193fe61be Database working for ignores 2023-11-18 18:05:26 -08:00
David Markowitz
511407c8ea Removing ignore works now 2023-11-18 17:33:52 -08:00
David Markowitz
df3515f474 IgnoreList: Add and Populate fully working 2023-11-18 03:55:12 -08:00
8e84cafdfa feat: add configurable feature and versions (#1298)
* feat: add configurable feature and versions
to allow for easily swithing it out to enable features in the client for funsies
tested that this doesn't break anything and added test

* cleanup
2023-11-18 03:33:23 -06:00
David Markowitz
57e3a4f4ef fix: general issues with dismantling (#1304) 2023-11-18 01:15:47 -06:00
David Markowitz
98822d400f fix: ChatServer crash on startup (#1303) 2023-11-18 01:15:31 -06:00
David Markowitz
7f623d358c refactor: Database abstraction and organization of files (#1274)
* Database: Convert to proper namespace

* Database: Use base class and getter

* Database: Move files around

* Database: Add property Management query

Database: Move over user queries

Tested at gm 0 that pre-approved names are pre-approved, unapproved need moderator approval
deleting characters deletes the selcted one
refreshing the character page shows the last character you logged in as
tested all my characters show up when i login
tested that you can delete all 4 characters and the correct character is selected each time
tested renaming, approving names as gm0

Database: Add ugc model getter

Hey it works, look I got around the mariadb issue.

Database: Add queries

Database: consolidate name query

Database: Add friends list query

Update name of approved names query

Documentation

Database: Add name check

Database: Add BFF Query

Database: Move BFF Setter

Database: Move new friend query

Database: Add remove friend queries

Database: Add activity log

Database: Add ugc & prop content removal

Database: Add model update

Database: Add migration queries

Database: Add character and xml queries

Database: Add user queries

Untested, but compiling code

Need to test that new character names are properly assigned in the following scenarios
gm 0 and pre-approved name
gm 0 and unapproved name
gm 9 and pre-approved name
gm 9 and unapproved name

Database: constify function arguments

Database: Add pet queries

* Database: Move property model queries

Untested.  Need to test
placing a new model
moving existing one
removing ugc model
placing ugc model
moving ugc model(?)
changing privacy option variously
change description and name
approve property
can properly travel to property

* Property: Move stale reference deletion

* Database: Move performance update query

* Database: Add bug report query

* Database: Add cheat detection query

* Database: Add mail send query

* Untested code

need to test mailing from slash command, from all users of SendMail, getting bbb of a property and sending messages to bffs

* Update CDComponentsRegistryTable.h

Database: Rename and add further comments

Datavbase: Add comments

Add some comments

Build: Fix PCH directories

Database: Fix time

thanks apple

Database: Fix compiler warnings

Overload destructor
Define specialty for time_t
Use string instead of string_view for temp empty string

Update CDTable.h

Property: Update queries to use mapId

Database: Reorganize

Reorganize into CDClient folder and GameDatabase folder for clearer meanings and file structure

Folders: Rename to GameDatabase

MySQL: Remove MySQL Specifier from table

Database: Move Tables to Interfaces

Database: Reorder functions in header

Database: Simplify property queries

Database: Remove unused queries

Remove extra query definitions as well

Database: Consolidate User getters

Database: Comment logs

Update MySQLDatabase.cpp

Database: Use generic code

Playkey: Fix bad optional access

Database: Move stuff around

WorldServer: Update queries

Ugc reduced by many scopes
use new queries
very fast
tested that ugc still loads

Database: Add auth queries

I tested that only the correct password can sign into an account.
Tested that disabled playkeys do not allow the user to play the game

Database: Add donation query

Database: add objectId queries

Database: Add master queries

Database: Fix mis-named function

Database: Add slash command queries

Mail: Fix itemId type

CharFilter: Use new query

ObjectID: Remove duplicate code

SlashCommand: Update query with function

Database: Add mail queries

Ugc: Fix issues with saving models

Resolve large scope blocks as well

* Database: Add debug try catch rethrow macro

* General fixes

* fix play key not working

* Further fixes

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2023-11-17 18:47:18 -06:00
b68823b4cb fix: properly check friend list limits (#1300)
* fix: properly check friend list limits
added a config for friend list limit for the brave that want to mod the client to sanely go over 50
moved the best friend limit config to chatconfig.ini where it should be
cleanup loading these configs options a bit

Tested that the BFF limit works and that the new friend limit works as well

* fix typo

* fix member variable naming
2023-11-17 18:44:48 -06:00
jadebenn
7bbe5ffc39 Fix falling from Spider Cave not smashing you (#1302) 2023-11-17 18:43:01 -06:00
David Markowitz
0ddd20e2b5 fix: ugc Save rocket and car modular assembly data to database (#1279)
* Ugc: Use persistent Id for rocekts and cars

* Remove comment

* Ugc: Save rocket and car IDs to the database

* Correct names

* Database: formatting
2023-11-15 19:32:30 -06:00
David Markowitz
f59ca8b1da fix: Ugc model pickup (#1275)
* Ugc: Make it so we dont bin the model

Users must dismantle the model as opposed to accidentally picking it up in model mode

* Fix editing model in brick mode

* PropEntrance: Remove debug log
2023-11-15 19:32:17 -06:00
David Markowitz
a44f998bd1 Navmesh: Update Avant Gardens (#1288) 2023-11-15 19:30:59 -06:00
78d8c57752 feat: make the help menu top 5 issues work and configurable (#1293)
* feat: make make the help menu questions work

* address feedback

* typo

* update defaults

* fix and address feedback

* newline
2023-11-15 19:30:46 -06:00
David Markowitz
8b270ca97a fix: Dismantling basically being O(n!) (#1295) 2023-11-15 19:29:53 -06:00
David Markowitz
59303a232e Slash commands: Update compares (#1270)
use case insensitive compare
use character name for kill instead of account name

Use correct Id for muting when a player is found
2023-11-15 19:29:00 -06:00
8cd5bf7b8d feat: implement consume item behavior (#1098)
* feature: implement consume item behavior

* Cleanup

* tested with skill 456 and fixed some things

* remove logs
2023-11-14 19:38:52 -06:00
8a9883c224 feat: use more zoneTable options (#1273)
* feat: use more zoneTable options

Allow setting framrate for the zone
Allow setting if pets are allowed in the zone
Allow setting if mounts are allowed in a zone
Allow disabling saving location to a zone

* address feedback
2023-11-14 07:02:17 -06:00
Gie "Max" Vanommeslaeghe
79ff6e7ee4 Merge pull request #1292 from DarkflameUniverse/fix-dismantling
fix: Models leak
2023-11-14 13:38:32 +01:00
Gie "Max" Vanommeslaeghe
4167d98667 Merge pull request #1272 from DarkflameUniverse/vanity-reload-and-spawners
feat: Vanity reload and vanity spawners
2023-11-14 13:34:14 +01:00
David Markowitz
23c5d13151 Models: Fix leak 2023-11-13 22:51:45 -08:00
jadebenn
68e5737b74 Fix: Pets can no longer dig treasure without completing Bella Pepper's "Lost Tags" mission (#1287)
* Added mission check to pet digs

* Little formatting change

* More formatting clean-up
2023-11-13 02:41:27 -08:00
jadebenn
411dce7457 Adding damage cooldown/"invincibility frames" as in Live (#1276)
* Added cooldown handling

* Made most of the logs hidden outside of debug mode

* removed weird submodule

* kill this phantom submodule

* updated to reflect reviewed feedback

* Added IsCooldownImmune() method to DestroyableComponent

* friggin typo

* Implemented non-pending changes and added cooldown immunity functions to DestroyableComponentTests

* add trailing linebreak

* another typo :(

* flipped cooldown test order (not leaving immune)

* Clean up comment and add DestroyableComponent test
2023-11-12 05:53:03 -06:00
175b354e68 address feedback 2023-11-10 22:01:25 -06:00
4c3949e5d8 remove logs 2023-11-09 21:29:29 -06:00
f727e3951c consolidate code 2023-11-09 21:28:52 -06:00
4867136133 fix spawners and cleanup 2023-11-09 18:59:43 -06:00
6e07798023 try to kill spawners, they don't exist? 2023-11-09 18:44:51 -06:00
3dc7b6ef7f add spawner handeling and reload to deleted exsiting entities
spawned don't get deleted yet
2023-11-09 18:33:39 -06:00
Nathan Ogden
2c9a98313a chore: Notes for running as system service (#1252)
* Note for running as system service
Note for running as system service

* Additional detailing of linux service.

* Added darkflame.service file

changed readme to reference new file
2023-11-08 19:15:46 -08:00
David Markowitz
d1dc9f5403 fix: add Onedrive log message (#1269)
* fix: Onedrive log message

* Update README.md
2023-11-08 17:56:59 -08:00
David Markowitz
52b5994b98 Ugc: Add subkey for rockets and cars (#1266)
Tested that, if a user has followed the guide and turned UGCUSE3DSERVICES from 1 to 0, the do not get booted to login for having a rocket with a subkey in their inventory.

Add bouncer logic

Ugc: Use random Id
2023-11-08 12:18:02 -06:00
Gie "Max" Vanommeslaeghe
4c10faa852 Merge pull request #1265 from DarkflameUniverse/ugc_patch
fix: Ugc Remove async and second id usage
2023-11-07 19:09:37 +01:00
Gie "Max" Vanommeslaeghe
070d4a1fa8 Merge pull request #1267 from DarkflameUniverse/dummyfix
fix: Nexus Tower Combat Challenge exploding dummy
2023-11-07 19:03:12 +01:00
David Markowitz
6795dd189c CombatChallenge: Fix exploding dummy 2023-11-06 14:49:53 -08:00
David Markowitz
f40fce7711 Ugc: Remove async and second id usage 2023-11-06 01:41:28 -08:00
David Markowitz
797abb176a ChatFilter: Fix incorrect segment highlighting (#1255) 2023-11-05 17:19:26 -06:00
David Markowitz
0f9e951162 Database: Use null for accounts (#1253) 2023-11-05 01:00:31 -08:00
David Markowitz
cea0b98edf WorldServer: Fix crash from deleting last char (#1254)
Revert "WorldServer: Fix crash from deleting last char"

This reverts commit d9adafa1fef0ac88ed2b3b8ca6b97c2421a603e2.

Update WorldServer.cpp
2023-11-05 01:00:19 -08:00
David Markowitz
1e2d5605eb fix: Navmesh updates to Frostburgh and Forbidden Valley (#1251)
* Navmesh: Add Frostburgh mesh

* Navmesh: Add Frostburgh mesh

Also edit Forbidden Valley mesh
2023-11-04 10:42:39 -05:00
David Markowitz
32cf111810 Script: Fix incorrect kill method (#1248) 2023-11-04 10:42:28 -05:00
David Markowitz
65c743527e Add null check for loot drops (#1243)
fixes a crash
2023-10-28 05:32:19 -05:00
79752c9abc chore: remove rarity table debug log (#1245)
cause it's too noisy
2023-10-28 05:31:54 -05:00
David Markowitz
01efa72aad fix: Update navmeshes (#1229)
* re-add x and z checking for height

Now that we have better navmeshes, this will result in much better results and as such we can re-enable this check.

* Always run navmesh extraction

waste of time most of the time, but no other way to force update to the meshes easily.

* Navmeshes Version 2

- Add all missing zones
- Drastically improve several zones and their navmeshes, cleaning them up, making them more accurate and generally using more features of detour/recast.

* Update CMakeLists.txt

* update meshes

* Navmesh: Add pet cove navmesh

* Navmesh: Fix navmesh for fv
2023-10-27 23:19:43 -05:00
6f3950dae7 chore: remove uneeded old perm map check (#1240)
and remove unused softban perm
RIP DLU beta
2023-10-25 11:44:57 -05:00
David Markowitz
a5e46e2844 Chat: Fix possible nullptr access (#1238)
Fixes a possible nullptr access.  This is the only call to GetPlayerData where we do not check the result for some reason, so this PR adds in the check and a resulting log line.

Code compiles, unsure how to reproduce the issue, however here is the crash dump I used to deduce this being the possible issue

```

Error: signal 11:
[00] CatchUnhandled(int)(+0x316) [0x561469100336]
[01] /lib/x86_64-linux-gnu/libc.so.6(+0x42520) [0x7f65e8e45520]
[02] /DarkflameServer/build/ChatServer(+0x32719) [0x5614690fa719]
[03] HandlePacket(Packet*)(+0x2a0) [0x5614690fcfb0]
[04] /DarkflameServer/build/ChatServer(main+0x92e) [0x5614690fb75e]
[05] /lib/x86_64-linux-gnu/libc.so.6(+0x29d90) [0x7f65e8e2cd90]
[06] /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0x80) [0x7f65e8e2ce40]
[07] /DarkflameServer/build/ChatServer(_start+0x25) [0x5614690fc375]
```
2023-10-24 02:26:55 -07:00
David Markowitz
3a37f9581c Character Select: Fix deleted memory access (#1237) 2023-10-24 02:26:39 -07:00
David Markowitz
025ff593ce Script: Fix unupdated new component (#1236) 2023-10-23 13:25:07 -07:00
aab60567ba fix: adding a proximity monitor when one exists already (#1235) 2023-10-23 12:24:17 -05:00
David Markowitz
9aa81f95cc Script: Fix crashes (#1233) 2023-10-23 09:56:12 -05:00
David Markowitz
5ea06f9bda EntityManager: Fix iterator invalidation (#1234)
Tested that servers still start up, and that zones like bons no longer hard crash when all players have left the world.
2023-10-23 08:55:38 -05:00
David Markowitz
ae349d6b15 feat: Add isolated and simplified path to add components (#1204)
* Components: Make ComponentType inline

Prevents the next commits ODR violation

* Components: Add new components

* Entity: Add headers

inline script component ComponentType

* Components: Flip constructor argument order

Entity comes first always

* Entity: Add generic AddComponent

Allows for much easier adding of components and is error proof by not allowing the user to add more than 1 of a specific component type to an Entity.

* Entity: Migrate all component constructors

Move all to the new variadic templates AddComponent function to reduce clutter and ways the component map is modified.
The new function makes no assumptions.  Component is assumed to not exist and is checked for with operator[].  This will construct a null component which will then be newed if the component didnt exist, or it will just get the current component if it does already exist.  No new component will be allocated or constructed if the component already exists and the already existing pointer is returned instead.

* Entity: Add placement new

For the case where the component may already exist, use a placement new to construct the component again, it would be constructed again, but would not need to go through the allocator.

* Entity: Add comments on likely new code

* Tests: Fix tests

* Update Entity.cpp

* Update SGCannon.cpp

* Entity: call destructor when re-constructing

* Update Entity.cpp

Update Entity.cpp

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-10-22 20:08:49 -05:00
David Markowitz
22207ea9c9 Diagnostics: Fix error file (#1231) 2023-10-22 19:58:19 -05:00
296 changed files with 4366 additions and 2611 deletions

View File

@@ -148,17 +148,20 @@ foreach (resource_file ${RESOURCE_FILES})
endforeach()
message(STATUS "Resource file integrity check complete")
# Copy navmesh data on first build and extract it
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes/)
configure_file(
${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip
COPYONLY
)
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip)
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
# if navmeshes directory does not exist, create it
if (NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
endif()
# Copy navmesh data on first build and extract it
configure_file(
${CMAKE_SOURCE_DIR}/resources/navmeshes.zip ${PROJECT_BINARY_DIR}/navmeshes.zip
COPYONLY
)
file(ARCHIVE_EXTRACT INPUT ${PROJECT_BINARY_DIR}/navmeshes.zip DESTINATION ${PROJECT_BINARY_DIR}/navmeshes)
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip)
# Copy vanity files on first build
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml")
foreach(file ${VANITY_FILES})
@@ -211,7 +214,12 @@ set(INCLUDED_DIRECTORIES
"dNavigation/dTerrain"
"dZoneManager"
"dDatabase"
"dDatabase/Tables"
"dDatabase/CDClientDatabase"
"dDatabase/CDClientDatabase/CDClientTables"
"dDatabase/GameDatabase"
"dDatabase/GameDatabase/ITables"
"dDatabase/GameDatabase/MySQL"
"dDatabase/GameDatabase/MySQL/Tables"
"dNet"
"dScripts"
"dScripts/02_server"
@@ -326,8 +334,9 @@ add_subdirectory(thirdparty)
file(
GLOB HEADERS_DDATABASE
LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dDatabase/*.h
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/*.h
${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase/CDClientTables/*.h
${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables/*.h
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h
)

View File

@@ -23,6 +23,9 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
### Supply of resource files
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.
## Step by step walkthrough for a single-player server
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
## Steps to setup server
* [Clone this repository](#clone-the-repository)
* [Install dependencies](#install-dependencies)
@@ -224,6 +227,44 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer
```
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0.
### 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.
_Note: You could use screen or tmux instead for virtual terminals_
To run the server non-interactively, we can use a systemctl service by copying the following file:
```shell
cp ./systemd.example /etc/systemd/system/darkflame.service
```
Make sure to edit the file in `/etc/systemd/system/darkflame.service` and change the:
- `User` and `Group` to the user that runs the darkflame server.
- `ExecPath` to the full file path of the server executable.
To register, enable and start the service use the following commands:
- Reload the systemd manager configuration to make it aware of the new service file:
```shell
systemctl daemon-reload
```
- Start the service:
```shell
systemctl start darkflame.service
```
- Enable OR disable the service to start on boot using:
```shell
systemctl enable darkflame.service
systemctl disable darkflame.service
```
- Verify that the service is running without errors:
```shell
systemctl status darkflame.service
```
- You can also restart, stop, or check the logs of the service using journalctl
```shell
systemctl restart darkflame.service
systemctl stop darkflame.service
journalctl -xeu darkflame.service
```
### First admin user
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!
@@ -285,6 +326,7 @@ Below are known good SHA256 checksums of the client:
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed)
If the returned hash matches one of the lines above then you can continue with setting up the server. If you are using a fully downloaded and complete client from live, then it will work, but the hash above may not match. Otherwise you must obtain a full install of LEGO® Universe 1.10.64.
You must also make absolutely sure your LEGO Universe client is not in a Windows OneDrive. DLU is not and will not support a client being stored in a OneDrive, so ensure you have moved the client outside of that location.
### Darkflame Universe Client
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.

View File

@@ -55,14 +55,8 @@ int main(int argc, char** argv) {
LOG("Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
LOG("Compiled on: %s", __TIMESTAMP__);
//Connect to the MySQL Database
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
Database::Connect();
} catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("AuthServer");
@@ -74,15 +68,12 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1500;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
Game::randomEngine = std::mt19937(time(0));
@@ -103,6 +94,8 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0;
AuthPackets::LoadClaimCodes();
while (!Game::shouldShutdown) {
//Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) {
@@ -134,16 +127,12 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason:
std::string masterIP;
uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
delete res;
delete stmt;
framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++;

View File

@@ -32,15 +32,11 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
}
//Read player names that are ok as well:
auto stmt = Database::CreatePreppedStmt("select name from charinfo;");
auto res = stmt->executeQuery();
while (res->next()) {
std::string line = res->getString(1).c_str();
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
m_ApprovedWords.push_back(CalculateHash(line));
auto approvedNames = Database::Get()->GetApprovedCharacterNames();
for (auto& name : approvedNames) {
std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase
m_ApprovedWords.push_back(CalculateHash(name));
}
delete res;
delete stmt;
}
dChatFilter::~dChatFilter() {
@@ -144,7 +140,7 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
listOfBadSegments.emplace_back(position, originalSegment.length());
}
position += segment.length() + 1;
position += originalSegment.length() + 1;
}
return listOfBadSegments;

View File

@@ -1,4 +1,5 @@
set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp"
"PlayerContainer.cpp"
)

View File

@@ -0,0 +1,173 @@
#include "ChatIgnoreList.h"
#include "PlayerContainer.h"
#include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "PacketUtils.h"
#include "Game.h"
#include "Logger.h"
#include "eObjectBits.h"
#include "Database.h"
// A note to future readers, The client handles all the actual ignoring logic:
// not allowing teams, rejecting DMs, friends requets etc.
// The only thing not auto-handled is instance activities force joining the team on the server.
void WriteOutgoingReplyHeader(RakNet::BitStream& bitStream, const LWOOBJID& receivingPlayer, const ChatIgnoreList::Response type) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receivingPlayer);
//portion that will get routed:
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, type);
}
void ChatIgnoreList::GetIgnoreList(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
if (!receiver->ignoredPlayers.empty()) {
LOG_DEBUG("Player %llu already has an ignore list", playerId);
return;
}
auto ignoreList = Database::Get()->GetIgnoreList(static_cast<uint32_t>(playerId));
if (ignoreList.empty()) {
LOG_DEBUG("Player %llu has no ignores", playerId);
return;
}
for (auto& ignoredPlayer : ignoreList) {
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayer.id, ignoredPlayer.name });
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(receiver->ignoredPlayers.back().playerId, eObjectBits::PERSISTENT);
}
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::GET_IGNORE);
bitStream.Write<uint8_t>(false); // Probably is Is Free Trial, but we don't care about that
bitStream.Write<uint16_t>(0); // literally spacing due to struct alignment
bitStream.Write<uint16_t>(receiver->ignoredPlayers.size());
for (const auto& ignoredPlayer : receiver->ignoredPlayers) {
bitStream.Write(ignoredPlayer.playerId);
bitStream.Write(LUWString(ignoredPlayer.playerName, 36));
}
Game::server->Send(&bitStream, packet->systemAddress, false);
}
void ChatIgnoreList::AddIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
constexpr int32_t MAX_IGNORES = 32;
if (receiver->ignoredPlayers.size() > MAX_IGNORES) {
LOG_DEBUG("Player %llu has too many ignores", playerId);
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
LUWString toIgnoreName(33);
inStream.Read(toIgnoreName);
std::string toIgnoreStr = toIgnoreName.GetAsString();
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::ADD_IGNORE);
// Check if the player exists
LWOOBJID ignoredPlayerId = LWOOBJID_EMPTY;
if (toIgnoreStr == receiver->playerName || toIgnoreStr.find("[GM]") == 0) {
LOG_DEBUG("Player %llu tried to ignore themselves", playerId);
bitStream.Write(ChatIgnoreList::AddResponse::GENERAL_ERROR);
} else if (std::count(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), toIgnoreStr) > 0) {
LOG_DEBUG("Player %llu is already ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::ALREADY_IGNORED);
} else {
// Get the playerId falling back to query if not online
auto* playerData = Game::playerContainer.GetPlayerData(toIgnoreStr);
if (!playerData) {
// Fall back to query
auto player = Database::Get()->GetCharacterInfo(toIgnoreStr);
if (!player || player->name != toIgnoreStr) {
LOG_DEBUG("Player %s not found", toIgnoreStr.c_str());
} else {
ignoredPlayerId = player->id;
}
} else {
ignoredPlayerId = playerData->playerID;
}
if (ignoredPlayerId != LWOOBJID_EMPTY) {
Database::Get()->AddIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(ignoredPlayerId));
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::CHARACTER);
GeneralUtils::SetBit(ignoredPlayerId, eObjectBits::PERSISTENT);
receiver->ignoredPlayers.push_back(IgnoreData{ ignoredPlayerId, toIgnoreStr });
LOG_DEBUG("Player %llu is ignoring %s", playerId, toIgnoreStr.c_str());
bitStream.Write(ChatIgnoreList::AddResponse::SUCCESS);
} else {
bitStream.Write(ChatIgnoreList::AddResponse::PLAYER_NOT_FOUND);
}
}
LUWString playerNameSend(toIgnoreStr, 33);
bitStream.Write(playerNameSend);
bitStream.Write(ignoredPlayerId);
Game::server->Send(&bitStream, packet->systemAddress, false);
}
void ChatIgnoreList::RemoveIgnore(Packet* packet) {
CINSTREAM_SKIP_HEADER;
LWOOBJID playerId;
inStream.Read(playerId);
auto* receiver = Game::playerContainer.GetPlayerData(playerId);
if (!receiver) {
LOG("Tried to get ignore list, but player %llu not found in container", playerId);
return;
}
inStream.IgnoreBytes(4); // ignore some garbage zeros idk
LUWString removedIgnoreName(33);
inStream.Read(removedIgnoreName);
std::string removedIgnoreStr = removedIgnoreName.GetAsString();
auto toRemove = std::remove(receiver->ignoredPlayers.begin(), receiver->ignoredPlayers.end(), removedIgnoreStr);
if (toRemove == receiver->ignoredPlayers.end()) {
LOG_DEBUG("Player %llu is not ignoring %s", playerId, removedIgnoreStr.c_str());
return;
}
Database::Get()->RemoveIgnore(static_cast<uint32_t>(playerId), static_cast<uint32_t>(toRemove->playerId));
receiver->ignoredPlayers.erase(toRemove, receiver->ignoredPlayers.end());
CBITSTREAM;
WriteOutgoingReplyHeader(bitStream, receiver->playerID, ChatIgnoreList::Response::REMOVE_IGNORE);
bitStream.Write<int8_t>(0);
LUWString playerNameSend(removedIgnoreStr, 33);
bitStream.Write(playerNameSend);
Game::server->Send(&bitStream, packet->systemAddress, false);
}

View File

@@ -0,0 +1,27 @@
#ifndef __CHATIGNORELIST__H__
#define __CHATIGNORELIST__H__
struct Packet;
#include <cstdint>
namespace ChatIgnoreList {
void GetIgnoreList(Packet* packet);
void AddIgnore(Packet* packet);
void RemoveIgnore(Packet* packet);
enum class Response : uint8_t {
ADD_IGNORE = 32,
REMOVE_IGNORE = 33,
GET_IGNORE = 34,
};
enum class AddResponse : uint8_t {
SUCCESS,
ALREADY_IGNORED,
PLAYER_NOT_FOUND,
GENERAL_ERROR,
};
};
#endif //!__CHATIGNORELIST__H__

View File

@@ -19,46 +19,29 @@
#include "eClientMessageType.h"
#include "eGameMessageType.h"
extern PlayerContainer playerContainer;
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with:
CINSTREAM_SKIP_HEADER;
LWOOBJID playerID = 0;
inStream.Read(playerID);
auto player = playerContainer.GetPlayerData(playerID);
auto player = Game::playerContainer.GetPlayerData(playerID);
if (!player) return;
//Get our friends list from the Db. Using a derived table since the friend of a player can be in either column.
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt(
"SELECT fr.requested_player, best_friend, ci.name FROM "
"(SELECT CASE "
"WHEN player_id = ? THEN friend_id "
"WHEN friend_id = ? THEN player_id "
"END AS requested_player, best_friend FROM friends) AS fr "
"JOIN charinfo AS ci ON ci.id = fr.requested_player "
"WHERE fr.requested_player IS NOT NULL AND fr.requested_player != ?;"));
stmt->setUInt(1, static_cast<uint32_t>(playerID));
stmt->setUInt(2, static_cast<uint32_t>(playerID));
stmt->setUInt(3, static_cast<uint32_t>(playerID));
std::vector<FriendData> friends;
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
while (res->next()) {
auto friendsList = Database::Get()->GetFriendsList(playerID);
for (const auto& friendData : friendsList) {
FriendData fd;
fd.isFTP = false; // not a thing in DLU
fd.friendID = res->getUInt(1);
fd.friendID = friendData.friendID;
GeneralUtils::SetBit(fd.friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(fd.friendID, eObjectBits::CHARACTER);
fd.isBestFriend = res->getInt(2) == 3; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
fd.isBestFriend = friendData.isBestFriend; //0 = friends, 1 = left_requested, 2 = right_requested, 3 = both_accepted - are now bffs
if (fd.isBestFriend) player->countOfBestFriends += 1;
fd.friendName = res->getString(3);
fd.friendName = friendData.friendName;
//Now check if they're online:
auto fr = playerContainer.GetPlayerData(fd.friendID);
auto fr = Game::playerContainer.GetPlayerData(fd.friendID);
if (fr) {
fd.isOnline = true;
@@ -71,7 +54,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
fd.zoneID = LWOZONEID();
}
friends.push_back(fd);
player->friends.push_back(fd);
}
//Now, we need to send the friendlist to the server they came from:
@@ -83,22 +66,17 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
bitStream.Write<uint8_t>(0);
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write((uint16_t)friends.size());
bitStream.Write((uint16_t)player->friends.size());
for (auto& data : friends) {
for (auto& data : player->friends) {
data.Serialize(bitStream);
}
player->friends = friends;
SystemAddress sysAddr = player->sysAddr;
SEND_PACKET;
}
void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
auto maxNumberOfBestFriendsAsString = Game::config->GetValue("max_number_of_best_friends");
// If this config option doesn't exist, default to 5 which is what live used.
auto maxNumberOfBestFriends = maxNumberOfBestFriendsAsString != "" ? std::stoi(maxNumberOfBestFriendsAsString) : 5U;
CINSTREAM_SKIP_HEADER;
LWOOBJID requestorPlayerID;
inStream.Read(requestorPlayerID);
@@ -117,12 +95,17 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
char isBestFriendRequest{};
inStream.Read(isBestFriendRequest);
auto requestor = playerContainer.GetPlayerData(requestorPlayerID);
auto requestor = Game::playerContainer.GetPlayerData(requestorPlayerID);
if (!requestor) {
LOG("No requestor player %llu sent to %s found.", requestorPlayerID, playerName.c_str());
return;
}
if (requestor->playerName == playerName) {
SendFriendResponse(requestor, requestor, eAddFriendResponseType::MYTHRAN);
return;
};
std::unique_ptr<PlayerData> requestee(playerContainer.GetPlayerData(playerName));
std::unique_ptr<PlayerData> requestee(Game::playerContainer.GetPlayerData(playerName));
// Check if player is online first
if (isBestFriendRequest && !requestee) {
@@ -150,35 +133,26 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// If at this point we dont have a target, then they arent online and we cant send the request.
// Send the response code that corresponds to what the error is.
if (!requestee) {
std::unique_ptr<sql::PreparedStatement> nameQuery(Database::CreatePreppedStmt("SELECT name from charinfo where name = ?;"));
nameQuery->setString(1, playerName);
std::unique_ptr<sql::ResultSet> result(nameQuery->executeQuery());
requestee.reset(new PlayerData());
requestee->playerName = playerName;
auto responseType = Database::Get()->GetCharacterInfo(playerName)
? eAddFriendResponseType::NOTONLINE
: eAddFriendResponseType::INVALIDCHARACTER;
SendFriendResponse(requestor, requestee.get(), result->next() ? eAddFriendResponseType::NOTONLINE : eAddFriendResponseType::INVALIDCHARACTER);
SendFriendResponse(requestor, requestee.get(), responseType);
return;
}
if (isBestFriendRequest) {
std::unique_ptr<sql::PreparedStatement> friendUpdate(Database::CreatePreppedStmt("SELECT * FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
friendUpdate->setUInt(1, static_cast<uint32_t>(requestorPlayerID));
friendUpdate->setUInt(2, static_cast<uint32_t>(requestee->playerID));
friendUpdate->setUInt(3, static_cast<uint32_t>(requestee->playerID));
friendUpdate->setUInt(4, static_cast<uint32_t>(requestorPlayerID));
std::unique_ptr<sql::ResultSet> result(friendUpdate->executeQuery());
LWOOBJID queryPlayerID = LWOOBJID_EMPTY;
LWOOBJID queryFriendID = LWOOBJID_EMPTY;
uint8_t oldBestFriendStatus{};
uint8_t bestFriendStatus{};
if (result->next()) {
auto bestFriendInfo = Database::Get()->GetBestFriendStatus(requestorPlayerID, requestee->playerID);
if (bestFriendInfo) {
// Get the IDs
queryPlayerID = result->getInt(1);
queryFriendID = result->getInt(2);
oldBestFriendStatus = result->getInt(3);
LWOOBJID queryPlayerID = bestFriendInfo->playerCharacterId;
LWOOBJID queryFriendID = bestFriendInfo->friendCharacterId;
oldBestFriendStatus = bestFriendInfo->bestFriendStatus;
bestFriendStatus = oldBestFriendStatus;
// Set the bits
@@ -199,22 +173,17 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
// Only do updates if there was a change in the bff status.
if (oldBestFriendStatus != bestFriendStatus) {
if (requestee->countOfBestFriends >= maxNumberOfBestFriends || requestor->countOfBestFriends >= maxNumberOfBestFriends) {
if (requestee->countOfBestFriends >= maxNumberOfBestFriends) {
auto maxBestFriends = Game::playerContainer.GetMaxNumberOfBestFriends();
if (requestee->countOfBestFriends >= maxBestFriends || requestor->countOfBestFriends >= maxBestFriends) {
if (requestee->countOfBestFriends >= maxBestFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
}
if (requestor->countOfBestFriends >= maxNumberOfBestFriends) {
if (requestor->countOfBestFriends >= maxBestFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
}
} else {
// Then update the database with this new info.
std::unique_ptr<sql::PreparedStatement> updateQuery(Database::CreatePreppedStmt("UPDATE friends SET best_friend = ? WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
updateQuery->setUInt(1, bestFriendStatus);
updateQuery->setUInt(2, static_cast<uint32_t>(requestorPlayerID));
updateQuery->setUInt(3, static_cast<uint32_t>(requestee->playerID));
updateQuery->setUInt(4, static_cast<uint32_t>(requestee->playerID));
updateQuery->setUInt(5, static_cast<uint32_t>(requestorPlayerID));
updateQuery->executeUpdate();
Database::Get()->SetBestFriendStatus(requestorPlayerID, requestee->playerID, bestFriendStatus);
// Sent the best friend update here if the value is 3
if (bestFriendStatus == 3U) {
requestee->countOfBestFriends += 1;
@@ -237,8 +206,15 @@ void ChatPacketHandler::HandleFriendRequest(Packet* packet) {
if (requestor->sysAddr != UNASSIGNED_SYSTEM_ADDRESS) SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::WAITINGAPPROVAL, true, true);
}
} else {
// Do not send this if we are requesting to be a best friend.
SendFriendRequest(requestee.get(), requestor);
auto maxFriends = Game::playerContainer.GetMaxNumberOfFriends();
if (requestee->friends.size() >= maxFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::THEIRFRIENDLISTFULL, false);
} else if (requestor->friends.size() >= maxFriends) {
SendFriendResponse(requestor, requestee.get(), eAddFriendResponseType::YOURFRIENDSLISTFULL, false);
} else {
// Do not send this if we are requesting to be a best friend.
SendFriendRequest(requestee.get(), requestor);
}
}
// If the player is actually a player and not a ghost one defined above, release it from being deleted.
@@ -254,8 +230,8 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
std::string friendName = PacketUtils::ReadString(0x15, packet, true);
//Now to try and find both of these:
auto requestor = playerContainer.GetPlayerData(playerID);
auto requestee = playerContainer.GetPlayerData(friendName);
auto requestor = Game::playerContainer.GetPlayerData(playerID);
auto requestee = Game::playerContainer.GetPlayerData(friendName);
if (!requestor || !requestee) return;
eAddFriendResponseType serverResponseCode{};
@@ -309,11 +285,7 @@ void ChatPacketHandler::HandleFriendResponse(Packet* packet) {
requesteeData.isOnline = true;
requestor->friends.push_back(requesteeData);
std::unique_ptr<sql::PreparedStatement> statement(Database::CreatePreppedStmt("INSERT IGNORE INTO `friends` (`player_id`, `friend_id`, `best_friend`) VALUES (?,?,?);"));
statement->setUInt(1, static_cast<uint32_t>(requestor->playerID));
statement->setUInt(2, static_cast<uint32_t>(requestee->playerID));
statement->setInt(3, 0);
statement->execute();
Database::Get()->AddFriend(requestor->playerID, requestee->playerID);
}
if (serverResponseCode != eAddFriendResponseType::DECLINED) SendFriendResponse(requestor, requestee, serverResponseCode, isAlreadyBestFriends);
@@ -328,28 +300,20 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
//we'll have to query the db here to find the user, since you can delete them while they're offline.
//First, we need to find their ID:
std::unique_ptr<sql::PreparedStatement> stmt(Database::CreatePreppedStmt("SELECT id FROM charinfo WHERE name=? LIMIT 1;"));
stmt->setString(1, friendName.c_str());
LWOOBJID friendID = 0;
std::unique_ptr<sql::ResultSet> res(stmt->executeQuery());
while (res->next()) {
friendID = res->getUInt(1);
auto friendIdResult = Database::Get()->GetCharacterInfo(friendName);
if (friendIdResult) {
friendID = friendIdResult->id;
}
// Convert friendID to LWOOBJID
GeneralUtils::SetBit(friendID, eObjectBits::PERSISTENT);
GeneralUtils::SetBit(friendID, eObjectBits::CHARACTER);
std::unique_ptr<sql::PreparedStatement> deletestmt(Database::CreatePreppedStmt("DELETE FROM friends WHERE (player_id = ? AND friend_id = ?) OR (player_id = ? AND friend_id = ?) LIMIT 1;"));
deletestmt->setUInt(1, static_cast<uint32_t>(playerID));
deletestmt->setUInt(2, static_cast<uint32_t>(friendID));
deletestmt->setUInt(3, static_cast<uint32_t>(friendID));
deletestmt->setUInt(4, static_cast<uint32_t>(playerID));
deletestmt->execute();
Database::Get()->RemoveFriend(playerID, friendID);
//Now, we need to send an update to notify the sender (and possibly, receiver) that their friendship has been ended:
auto goonA = playerContainer.GetPlayerData(playerID);
auto goonA = Game::playerContainer.GetPlayerData(playerID);
if (goonA) {
// Remove the friend from our list of friends
for (auto friendData = goonA->friends.begin(); friendData != goonA->friends.end(); friendData++) {
@@ -362,7 +326,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
SendRemoveFriend(goonA, friendName, true);
}
auto goonB = playerContainer.GetPlayerData(friendID);
auto goonB = Game::playerContainer.GetPlayerData(friendID);
if (!goonB) return;
// Do it again for other person
for (auto friendData = goonB->friends.begin(); friendData != goonB->friends.end(); friendData++) {
@@ -373,7 +337,7 @@ void ChatPacketHandler::HandleRemoveFriend(Packet* packet) {
}
}
std::string goonAName = GeneralUtils::UTF16ToWTF8(playerContainer.GetName(playerID));
std::string goonAName = GeneralUtils::UTF16ToWTF8(Game::playerContainer.GetName(playerID));
SendRemoveFriend(goonB, goonAName, true);
}
@@ -382,11 +346,11 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
auto* sender = playerContainer.GetPlayerData(playerID);
auto* sender = Game::playerContainer.GetPlayerData(playerID);
if (sender == nullptr) return;
if (playerContainer.GetIsMuted(sender)) return;
if (Game::playerContainer.GetIsMuted(sender)) return;
const auto senderName = std::string(sender->playerName.c_str());
@@ -401,12 +365,12 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
if (channel != 8) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team == nullptr) return;
for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId);
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (otherMember == nullptr) return;
@@ -440,11 +404,11 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
//Get the bois:
auto goonA = playerContainer.GetPlayerData(senderID);
auto goonB = playerContainer.GetPlayerData(receiverName);
auto goonA = Game::playerContainer.GetPlayerData(senderID);
auto goonB = Game::playerContainer.GetPlayerData(receiverName);
if (!goonA || !goonB) return;
if (playerContainer.GetIsMuted(goonA)) return;
if (Game::playerContainer.GetIsMuted(goonA)) return;
std::string goonAName = goonA->playerName.c_str();
std::string goonBName = goonB->playerName.c_str();
@@ -502,25 +466,25 @@ void ChatPacketHandler::HandleTeamInvite(Packet* packet) {
inStream.Read(playerID);
std::string invitedPlayer = PacketUtils::ReadString(0x14, packet, true);
auto* player = playerContainer.GetPlayerData(playerID);
auto* player = Game::playerContainer.GetPlayerData(playerID);
if (player == nullptr) {
return;
}
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team == nullptr) {
team = playerContainer.CreateTeam(playerID);
team = Game::playerContainer.CreateTeam(playerID);
}
auto* other = playerContainer.GetPlayerData(invitedPlayer);
auto* other = Game::playerContainer.GetPlayerData(invitedPlayer);
if (other == nullptr) {
return;
}
if (playerContainer.GetTeam(other->playerID) != nullptr) {
if (Game::playerContainer.GetTeam(other->playerID) != nullptr) {
return;
}
@@ -553,12 +517,12 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return;
}
auto* team = playerContainer.GetTeam(leaderID);
auto* team = Game::playerContainer.GetTeam(leaderID);
if (team == nullptr) {
LOG("Failed to find team for leader (%llu)", leaderID);
team = playerContainer.GetTeam(playerID);
team = Game::playerContainer.GetTeam(playerID);
}
if (team == nullptr) {
@@ -566,7 +530,7 @@ void ChatPacketHandler::HandleTeamInviteResponse(Packet* packet) {
return;
}
playerContainer.AddMember(team, playerID);
Game::playerContainer.AddMember(team, playerID);
}
void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
@@ -576,12 +540,12 @@ void ChatPacketHandler::HandleTeamLeave(Packet* packet) {
uint32_t size = 0;
inStream.Read(size);
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
LOG("(%llu) leaving team", playerID);
if (team != nullptr) {
playerContainer.RemoveMember(team, playerID, false, false, true);
Game::playerContainer.RemoveMember(team, playerID, false, false, true);
}
}
@@ -594,24 +558,24 @@ void ChatPacketHandler::HandleTeamKick(Packet* packet) {
LOG("(%llu) kicking (%s) from team", playerID, kickedPlayer.c_str());
auto* kicked = playerContainer.GetPlayerData(kickedPlayer);
auto* kicked = Game::playerContainer.GetPlayerData(kickedPlayer);
LWOOBJID kickedId = LWOOBJID_EMPTY;
if (kicked != nullptr) {
kickedId = kicked->playerID;
} else {
kickedId = playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
kickedId = Game::playerContainer.GetId(GeneralUtils::UTF8ToUTF16(kickedPlayer));
}
if (kickedId == LWOOBJID_EMPTY) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
if (team->leaderID != playerID || team->leaderID == kickedId) return;
playerContainer.RemoveMember(team, kickedId, false, true, false);
Game::playerContainer.RemoveMember(team, kickedId, false, true, false);
}
}
@@ -624,16 +588,16 @@ void ChatPacketHandler::HandleTeamPromote(Packet* packet) {
LOG("(%llu) promoting (%s) to team leader", playerID, promotedPlayer.c_str());
auto* promoted = playerContainer.GetPlayerData(promotedPlayer);
auto* promoted = Game::playerContainer.GetPlayerData(promotedPlayer);
if (promoted == nullptr) return;
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
if (team->leaderID != playerID) return;
playerContainer.PromoteMember(team, promoted->playerID);
Game::playerContainer.PromoteMember(team, promoted->playerID);
}
}
@@ -647,16 +611,16 @@ void ChatPacketHandler::HandleTeamLootOption(Packet* packet) {
char option;
inStream.Read(option);
auto* team = playerContainer.GetTeam(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
if (team != nullptr) {
if (team->leaderID != playerID) return;
team->lootFlag = option;
playerContainer.TeamStatusUpdate(team);
Game::playerContainer.TeamStatusUpdate(team);
playerContainer.UpdateTeamsOnWorld(team, false);
Game::playerContainer.UpdateTeamsOnWorld(team, false);
}
}
@@ -665,18 +629,18 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
LWOOBJID playerID = LWOOBJID_EMPTY;
inStream.Read(playerID);
auto* team = playerContainer.GetTeam(playerID);
auto* data = playerContainer.GetPlayerData(playerID);
auto* team = Game::playerContainer.GetTeam(playerID);
auto* data = Game::playerContainer.GetPlayerData(playerID);
if (team != nullptr && data != nullptr) {
if (team->local && data->zoneID.GetMapID() != team->zoneId.GetMapID() && data->zoneID.GetCloneID() != team->zoneId.GetCloneID()) {
playerContainer.RemoveMember(team, playerID, false, false, true, true);
Game::playerContainer.RemoveMember(team, playerID, false, false, true, true);
return;
}
if (team->memberIDs.size() <= 1 && !team->local) {
playerContainer.DisbandTeam(team);
Game::playerContainer.DisbandTeam(team);
return;
}
@@ -687,16 +651,16 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
ChatPacketHandler::SendTeamSetLeader(data, LWOOBJID_EMPTY);
}
playerContainer.TeamStatusUpdate(team);
Game::playerContainer.TeamStatusUpdate(team);
const auto leaderName = GeneralUtils::UTF8ToUTF16(data->playerName);
for (const auto memberId : team->memberIDs) {
auto* otherMember = playerContainer.GetPlayerData(memberId);
auto* otherMember = Game::playerContainer.GetPlayerData(memberId);
if (memberId == playerID) continue;
const auto memberName = playerContainer.GetName(memberId);
const auto memberName = Game::playerContainer.GetName(memberId);
if (otherMember != nullptr) {
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, data->playerID, data->zoneID);
@@ -704,7 +668,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
ChatPacketHandler::SendTeamAddPlayer(data, false, team->local, false, memberId, memberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0));
}
playerContainer.UpdateTeamsOnWorld(team, false);
Game::playerContainer.UpdateTeamsOnWorld(team, false);
}
}

View File

@@ -19,6 +19,7 @@
#include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "Game.h"
@@ -34,14 +35,12 @@ namespace Game {
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
std::mt19937 randomEngine;
PlayerContainer playerContainer;
}
Logger* SetupLogger();
void HandlePacket(Packet* packet);
PlayerContainer playerContainer;
int main(int argc, char** argv) {
constexpr uint32_t chatFramerate = mediumFramerate;
constexpr uint32_t chatFrameDelta = mediumFrameDelta;
@@ -78,13 +77,8 @@ int main(int argc, char** argv) {
}
//Connect to the MySQL Database
std::string mysql_host = Game::config->GetValue("mysql_host");
std::string mysql_database = Game::config->GetValue("mysql_database");
std::string mysql_username = Game::config->GetValue("mysql_username");
std::string mysql_password = Game::config->GetValue("mysql_password");
try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password);
Database::Connect();
} catch (sql::SQLException& ex) {
LOG("Got an error while connecting to the database: %s", ex.what());
Database::Destroy("ChatServer");
@@ -96,16 +90,11 @@ int main(int argc, char** argv) {
//Find out the master's IP:
std::string masterIP;
uint32_t masterPort = 1000;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
delete res;
delete stmt;
//It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 50;
uint32_t ourPort = 1501;
@@ -118,6 +107,8 @@ int main(int argc, char** argv) {
Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize();
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();
Packet* packet = nullptr;
@@ -158,15 +149,12 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason:
std::string masterIP;
uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';");
auto res = stmt->executeQuery();
while (res->next()) {
masterIP = res->getString(1).c_str();
masterPort = res->getInt(2);
}
delete res;
delete stmt;
auto masterInfo = Database::Get()->GetMasterInfo();
if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++;
@@ -211,19 +199,19 @@ void HandlePacket(Packet* packet) {
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) {
switch (static_cast<eChatInternalMessageType>(packet->data[3])) {
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
playerContainer.InsertPlayer(packet);
Game::playerContainer.InsertPlayer(packet);
break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION:
playerContainer.RemovePlayer(packet);
Game::playerContainer.RemovePlayer(packet);
break;
case eChatInternalMessageType::MUTE_UPDATE:
playerContainer.MuteUpdate(packet);
Game::playerContainer.MuteUpdate(packet);
break;
case eChatInternalMessageType::CREATE_TEAM:
playerContainer.CreateTeamServer(packet);
Game::playerContainer.CreateTeamServer(packet);
break;
case eChatInternalMessageType::ANNOUNCEMENT: {
@@ -245,7 +233,15 @@ void HandlePacket(Packet* packet) {
break;
case eChatMessageType::GET_IGNORE_LIST:
LOG("Asked for ignore list, but is unimplemented right now.");
ChatIgnoreList::GetIgnoreList(packet);
break;
case eChatMessageType::ADD_IGNORE:
ChatIgnoreList::AddIgnore(packet);
break;
case eChatMessageType::REMOVE_IGNORE:
ChatIgnoreList::RemoveIgnore(packet);
break;
case eChatMessageType::TEAM_GET_STATUS:

View File

@@ -13,11 +13,13 @@
#include "ChatPackets.h"
#include "dConfig.h"
PlayerContainer::PlayerContainer() {
void PlayerContainer::Initialize() {
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends"), m_MaxNumberOfBestFriends);
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends"), m_MaxNumberOfFriends);
}
PlayerContainer::~PlayerContainer() {
mPlayers.clear();
m_Players.clear();
}
TeamData::TeamData() {
@@ -41,19 +43,12 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
inStream.Read(data->muteExpire);
data->sysAddr = packet->systemAddress;
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
m_Names[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName);
mPlayers.insert(std::make_pair(data->playerID, data));
m_Players.insert(std::make_pair(data->playerID, data));
LOG("Added user: %s (%llu), zone: %i", data->playerName.c_str(), data->playerID, data->zoneID.GetMapID());
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
insertLog->setInt(1, data->playerID);
insertLog->setInt(2, 0);
insertLog->setUInt64(3, time(nullptr));
insertLog->setInt(4, data->zoneID.GetMapID());
insertLog->executeUpdate();
Database::Get()->UpdateActivityLog(data->playerID, eActivityType::PlayerLoggedIn, data->zoneID.GetMapID());
}
void PlayerContainer::RemovePlayer(Packet* packet) {
@@ -88,16 +83,9 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
}
LOG("Removed user: %llu", playerID);
mPlayers.erase(playerID);
m_Players.erase(playerID);
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);");
insertLog->setInt(1, playerID);
insertLog->setInt(2, 1);
insertLog->setUInt64(3, time(nullptr));
insertLog->setInt(4, player->zoneID.GetMapID());
insertLog->executeUpdate();
Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player->zoneID.GetMapID());
}
void PlayerContainer::MuteUpdate(Packet* packet) {
@@ -191,7 +179,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
auto* team = new TeamData();
team->teamID = ++mTeamIDCounter;
team->teamID = ++m_TeamIDCounter;
team->leaderID = leader;
team->local = local;
@@ -376,15 +364,15 @@ void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
}
std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
const auto& pair = mNames.find(playerID);
const auto& pair = m_Names.find(playerID);
if (pair == mNames.end()) return u"";
if (pair == m_Names.end()) return u"";
return pair->second;
}
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
for (const auto& pair : mNames) {
for (const auto& pair : m_Names) {
if (pair.second == playerName) {
return pair.first;
}

View File

@@ -7,12 +7,26 @@
#include "dServer.h"
#include <unordered_map>
struct IgnoreData {
inline bool operator==(const std::string& other) const noexcept {
return playerName == other;
}
inline bool operator==(const LWOOBJID& other) const noexcept {
return playerId == other;
}
LWOOBJID playerId;
std::string playerName;
};
struct PlayerData {
LWOOBJID playerID;
std::string playerName;
SystemAddress sysAddr;
LWOZONEID zoneID;
std::vector<FriendData> friends;
std::vector<IgnoreData> ignoredPlayers;
time_t muteExpire;
uint8_t countOfBestFriends = 0;
};
@@ -29,9 +43,9 @@ struct TeamData {
class PlayerContainer {
public:
PlayerContainer();
~PlayerContainer();
void Initialize();
void InsertPlayer(Packet* packet);
void RemovePlayer(Packet* packet);
void MuteUpdate(Packet* packet);
@@ -39,13 +53,13 @@ public:
void BroadcastMuteUpdate(LWOOBJID player, time_t time);
PlayerData* GetPlayerData(const LWOOBJID& playerID) {
auto it = mPlayers.find(playerID);
if (it != mPlayers.end()) return it->second;
auto it = m_Players.find(playerID);
if (it != m_Players.end()) return it->second;
return nullptr;
}
PlayerData* GetPlayerData(const std::string& playerName) {
for (auto player : mPlayers) {
for (auto player : m_Players) {
if (player.second) {
std::string pn = player.second->playerName.c_str();
if (pn == playerName) return player.second;
@@ -67,13 +81,17 @@ public:
std::u16string GetName(LWOOBJID playerID);
LWOOBJID GetId(const std::u16string& playerName);
bool GetIsMuted(PlayerData* data);
uint32_t GetMaxNumberOfBestFriends() { return m_MaxNumberOfBestFriends; }
uint32_t GetMaxNumberOfFriends() { return m_MaxNumberOfFriends; }
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return mPlayers; }
std::map<LWOOBJID, PlayerData*>& GetAllPlayerData() { return m_Players; }
private:
LWOOBJID mTeamIDCounter = 0;
std::map<LWOOBJID, PlayerData*> mPlayers;
LWOOBJID m_TeamIDCounter = 0;
std::map<LWOOBJID, PlayerData*> m_Players;
std::vector<TeamData*> mTeams;
std::unordered_map<LWOOBJID, std::u16string> mNames;
std::unordered_map<LWOOBJID, std::u16string> m_Names;
uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50;
};

View File

@@ -13,9 +13,8 @@
//! Forward declarations
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
void WriteSd0Magic(char* input, uint32_t chunkSize);
bool CheckSd0Magic(sql::Blob* streamToCheck);
bool CheckSd0Magic(std::istream& streamToCheck);
/**
* @brief Truncates all models with broken data from the database.
@@ -24,28 +23,24 @@ bool CheckSd0Magic(sql::Blob* streamToCheck);
*/
uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
uint32_t modelsTruncated{};
auto modelsToTruncate = GetModelsFromDatabase();
bool previousCommitValue = Database::GetAutoCommit();
Database::SetAutoCommit(false);
while (modelsToTruncate->next()) {
std::unique_ptr<sql::PreparedStatement> ugcModelToDelete(Database::CreatePreppedStmt("DELETE FROM ugc WHERE ugc.id = ?;"));
std::unique_ptr<sql::PreparedStatement> pcModelToDelete(Database::CreatePreppedStmt("DELETE FROM properties_contents WHERE ugc_id = ?;"));
auto modelsToTruncate = Database::Get()->GetAllUgcModels();
bool previousCommitValue = Database::Get()->GetAutoCommit();
Database::Get()->SetAutoCommit(false);
for (auto& model : modelsToTruncate) {
std::string completeUncompressedModel{};
uint32_t chunkCount{};
uint64_t modelId = modelsToTruncate->getInt(1);
std::unique_ptr<sql::Blob> modelAsSd0(modelsToTruncate->getBlob(2));
// Check that header is sd0 by checking for the sd0 magic.
if (CheckSd0Magic(modelAsSd0.get())) {
if (CheckSd0Magic(model.lxfmlData)) {
while (true) {
uint32_t chunkSize{};
modelAsSd0->read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
model.lxfmlData.read(reinterpret_cast<char*>(&chunkSize), sizeof(uint32_t)); // Extract chunk size from istream
// Check if good here since if at the end of an sd0 file, this will have eof flagged.
if (!modelAsSd0->good()) break;
if (!model.lxfmlData.good()) break;
std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
for (uint32_t i = 0; i < chunkSize; i++) {
compressedChunk[i] = modelAsSd0->get();
compressedChunk[i] = model.lxfmlData.get();
}
// Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
@@ -59,7 +54,7 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
completeUncompressedModel.append((char*)uncompressedChunk.get());
completeUncompressedModel.resize(previousSize + actualUncompressedSize);
} else {
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, modelId, err);
LOG("Failed to inflate chunk %i for model %llu. Error: %i", chunkCount, model.id, err);
break;
}
chunkCount++;
@@ -75,26 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
"</LXFML>",
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
) {
LOG("Brick-by-brick model %llu will be deleted!", modelId);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
LOG("Brick-by-brick model %llu will be deleted!", model.id);
Database::Get()->DeleteUgcModelData(model.id);
modelsTruncated++;
}
}
} else {
LOG("Brick-by-brick model %llu will be deleted!", modelId);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
LOG("Brick-by-brick model %llu will be deleted!", model.id);
Database::Get()->DeleteUgcModelData(model.id);
modelsTruncated++;
}
}
Database::Commit();
Database::SetAutoCommit(previousCommitValue);
Database::Get()->Commit();
Database::Get()->SetAutoCommit(previousCommitValue);
return modelsTruncated;
}
@@ -106,21 +95,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
*/
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
uint32_t updatedModels = 0;
auto modelsToUpdate = GetModelsFromDatabase();
auto previousAutoCommitState = Database::GetAutoCommit();
Database::SetAutoCommit(false);
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;"));
while (modelsToUpdate->next()) {
int64_t modelId = modelsToUpdate->getInt64(1);
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
auto modelsToUpdate = Database::Get()->GetAllUgcModels();
auto previousAutoCommitState = Database::Get()->GetAutoCommit();
Database::Get()->SetAutoCommit(false);
for (auto& model : modelsToUpdate) {
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
// If it does, convert it to sd0.
if (oldLxfml->get() == 0x78 && oldLxfml->get() == 0xDA) {
if (model.lxfmlData.get() == 0x78 && model.lxfmlData.get() == 0xDA) {
// Get and save size of zlib compressed chunk.
oldLxfml->seekg(0, std::ios::end);
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg());
oldLxfml->seekg(0);
model.lxfmlData.seekg(0, std::ios::end);
uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
model.lxfmlData.seekg(0);
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
@@ -128,34 +113,27 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) {
sd0ConvertedModel.get()[i] = oldLxfml->get();
sd0ConvertedModel.get()[i] = model.lxfmlData.get();
}
std::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
std::istringstream outputStringStream(outputString);
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
insertionStatement->setInt64(2, modelId);
try {
insertionStatement->executeUpdate();
LOG("Updated model %i to sd0", modelId);
Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
LOG("Updated model %i to sd0", model.id);
updatedModels++;
} catch (sql::SQLException exception) {
LOG("Failed to update model %i. This model should be inspected manually to see why."
"The database error is %s", modelId, exception.what());
"The database error is %s", model.id, exception.what());
}
}
}
Database::Commit();
Database::SetAutoCommit(previousAutoCommitState);
Database::Get()->Commit();
Database::Get()->SetAutoCommit(previousAutoCommitState);
return updatedModels;
}
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase() {
std::unique_ptr<sql::PreparedStatement> modelsRawDataQuery(Database::CreatePreppedStmt("SELECT id, lxfml FROM ugc;"));
return std::unique_ptr<sql::ResultSet>(modelsRawDataQuery->executeQuery());
}
/**
* @brief Writes sd0 magic at the front of a char*
*
@@ -171,6 +149,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
}
bool CheckSd0Magic(sql::Blob* streamToCheck) {
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF;
bool CheckSd0Magic(std::istream& streamToCheck) {
return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
}

View File

@@ -134,6 +134,10 @@ void CatchUnhandled(int sig) {
// Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size);
FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
fprintf(file, "Error: signal %d:\n", sig);
}
// Print the stack trace
for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
@@ -155,19 +159,14 @@ void CatchUnhandled(int sig) {
}
LOG("[%02zu] %s", i, functionName.c_str());
if (file != NULL) {
fprintf(file, "[%02zu] %s\n", i, functionName.c_str());
}
}
# else // defined(__GNUG__)
backtrace_symbols_fd(array, size, STDOUT_FILENO);
# endif // defined(__GNUG__)
FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
// print out all the frames to stderr
fprintf(file, "Error: signal %d:\n", sig);
backtrace_symbols_fd(array, size, fileno(file));
fclose(file);
}
#else // __include_backtrace__
struct backtrace_state* state = backtrace_create_state(

View File

@@ -12,6 +12,7 @@ class AssetManager;
struct SystemAddress;
class EntityManager;
class dZoneManager;
class PlayerContainer;
namespace Game {
extern Logger* logger;
@@ -26,4 +27,5 @@ namespace Game {
extern bool shouldShutdown;
extern EntityManager* entityManager;
extern dZoneManager* zoneManager;
extern PlayerContainer playerContainer;
}

View File

@@ -151,6 +151,11 @@ namespace GeneralUtils {
return std::stod(value);
}
template <>
inline uint16_t Parse(const char* value) {
return std::stoul(value);
}
template <>
inline uint32_t Parse(const char* value) {
return std::stoul(value);

View File

@@ -38,13 +38,11 @@ const std::string& dConfig::GetValue(std::string key) {
}
void dConfig::ProcessLine(const std::string& line) {
auto splitLine = GeneralUtils::SplitString(line, '=');
if (splitLine.size() != 2) return;
auto splitLoc = line.find('=');
auto key = line.substr(0, splitLoc);
auto value = line.substr(splitLoc + 1);
//Make sure that on Linux, we remove special characters:
auto& key = splitLine.at(0);
auto& value = splitLine.at(1);
if (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;

View File

@@ -27,20 +27,6 @@ enum class ePermissionMap : uint64_t {
* The character has restricted chat access, bit 6.
*/
RestrictedChatAccess = 0x1 << 6,
//
// Combined permissions
//
/**
* The character is marked as 'old', restricted from trade and mail.
*/
Old = RestrictedTradeAccess | RestrictedMailAccess,
/**
* The character is soft banned, restricted from trade, mail, and chat.
*/
SoftBanned = RestrictedTradeAccess | RestrictedMailAccess | RestrictedChatAccess,
};
#endif //!__EPERMISSIONMAP__H__

View File

@@ -166,7 +166,8 @@ enum ePlayerFlag : int32_t {
NJ_LIGHTNING_SPINJITZU = 2031,
NJ_ICE_SPINJITZU = 2032,
NJ_FIRE_SPINJITZU = 2033,
NJ_WU_SHOW_DAILY_CHEST = 2099
NJ_WU_SHOW_DAILY_CHEST = 2099,
DLU_SKIP_CINEMATICS = 1'000'000,
};
#endif //!__EPLAYERFLAG__H__

View File

@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
PLATFORM_BOUNDARY,
MODULE,
ARCADE,
VEHICLE_PHYSICS, // Havok demo based
HAVOK_VEHICLE_PHYSICS,
MOVEMENT_AI,
EXHIBIT,
OVERHEAD_ICON,
@@ -50,7 +50,7 @@ enum class eReplicaComponentType : uint32_t {
PROPERTY_ENTRANCE,
FX,
PROPERTY_MANAGEMENT,
VEHICLE_PHYSICS_NEW, // internal physics based on havok
VEHICLE_PHYSICS,
PHYSICS_SYSTEM,
QUICK_BUILD,
SWITCH,

View File

@@ -36,7 +36,8 @@ enum class eWorldMessageType : uint32_t {
HANDLE_FUNNESS,
FAKE_PRG_CSR_MESSAGE,
REQUEST_FREE_TRIAL_REFRESH,
GM_SET_FREE_TRIAL_STATUS
GM_SET_FREE_TRIAL_STATUS,
UI_HELP_TOP_5 = 91
};
#endif //!__EWORLDMESSAGETYPE__H__

View File

@@ -37,6 +37,7 @@
#include "CDPropertyTemplateTable.h"
#include "CDFeatureGatingTable.h"
#include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h"
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
@@ -82,6 +83,7 @@ CDClientManager::CDClientManager() {
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
CDRarityTableTable::Instance().LoadValuesFromDatabase();
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();

View File

@@ -3,6 +3,8 @@
// Custom Classes
#include "CDTable.h"
#include <unordered_map>
enum class eReplicaComponentType : uint32_t;
struct CDComponentsRegistry {
unsigned int id; //!< The LOT is used as the ID

View File

@@ -42,9 +42,9 @@ std::vector<CDFeatureGating> CDFeatureGatingTable::Query(std::function<bool(CDFe
return data;
}
bool CDFeatureGatingTable::FeatureUnlocked(const std::string& feature) const {
bool CDFeatureGatingTable::FeatureUnlocked(const CDFeatureGating& feature) const {
for (const auto& entry : entries) {
if (entry.featureName == feature) {
if (entry.featureName == feature.featureName && feature >= entry) {
return true;
}
}

View File

@@ -9,6 +9,12 @@ struct CDFeatureGating {
int32_t current;
int32_t minor;
std::string description;
bool operator>=(const CDFeatureGating& b) const {
return (this->major > b.major) ||
(this->major == b.major && this->current > b.current) ||
(this->major == b.major && this->current == b.current && this->minor >= b.minor);
}
};
class CDFeatureGatingTable : public CDTable<CDFeatureGatingTable> {
@@ -21,7 +27,7 @@ public:
// Queries the table with a custom "where" clause
std::vector<CDFeatureGating> Query(std::function<bool(CDFeatureGating)> predicate);
bool FeatureUnlocked(const std::string& feature) const;
bool FeatureUnlocked(const CDFeatureGating& feature) const;
const std::vector<CDFeatureGating>& GetEntries(void) const;
};

View File

@@ -24,7 +24,6 @@ void SortTable(LootTableEntries& table) {
lootToInsert = oldItrInner;
}
}
Game::logger->LogDebug("CDLootTableTable", "highest rarity %i item id %i", highestLootRarity, lootToInsert->itemid);
std::swap(*oldItrOuter, *lootToInsert);
}
}

View File

@@ -0,0 +1,47 @@
#include "CDRewardCodesTable.h"
void CDRewardCodesTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes");
while (!tableData.eof()) {
CDRewardCode entry;
entry.id = tableData.getIntField("id", -1);
entry.code = tableData.getStringField("code", "");
entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1);
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1));
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", ""));
this->entries.push_back(entry);
tableData.nextRow();
}
}
LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const {
for (auto const &entry : this->entries){
if (rewardCodeId == entry.id) return entry.attachmentLOT;
}
return LOT_NULL;
}
uint32_t CDRewardCodesTable::GetCodeID(std::string code) const {
for (auto const &entry : this->entries){
if (code == entry.code) return entry.id;
}
return -1;
}

View File

@@ -0,0 +1,25 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDRewardCode {
uint32_t id;
std::string code;
LOT attachmentLOT;
UNUSED(uint32_t locStatus);
UNUSED(std::string gate_version);
};
class CDRewardCodesTable : public CDTable<CDRewardCodesTable> {
private:
std::vector<CDRewardCode> entries;
public:
void LoadValuesFromDatabase();
const std::vector<CDRewardCode>& GetEntries() const;
LOT GetAttachmentLOT(uint32_t rewardCodeId) const;
uint32_t GetCodeID(std::string code) const;
};

View File

@@ -8,6 +8,7 @@
#include <string>
#include <vector>
#include <map>
#include <cstdint>
// CPPLinq
#ifdef _WIN32

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