Compare commits

...

473 Commits

Author SHA1 Message Date
David Markowitz
8f6ee6327e add deep copy of config data 2024-05-24 21:17:56 -07:00
David Markowitz
d572ce1f1c add back deleter 2024-05-24 03:56:38 -07:00
David Markowitz
85cd609168 remove extra item code 2024-05-23 20:10:44 -07:00
David Markowitz
d4e6939dbd Merge branch 'main' into naming-of-models 2024-05-23 19:51:45 -07:00
David Markowitz
f8d5110290 Update PropertyManagementComponent.cpp
Revert "fix stale reference"

This reverts commit 03b17c0374787bca6dfec2da663da74ac76c26fb.

fix stale reference
2024-05-23 14:42:15 -07:00
David Markowitz
f0960d48b2 Add more modular saving of config data for items (#1591)
* stubbing for saving item extra data

* add declaration to header

* modularize loading for all possible extra data

* move logic to Item

* remove extra map
2024-05-22 17:06:52 -07:00
David Markowitz
dc430d9758 Add reputation as a repeatable mission reward (#1590)
This reverts commit 7d1a28b492b263aba2008a5984dc0f5e7348a068.

Add stubbing for abbreviations

Reward reputation always if possible
2024-05-22 16:35:45 -07:00
TAHuntling
dea10c6d56 Client commands implementation (#1592)
* Adding Client Commands

Adding list of client commands provided to me by EmosewaMC

* Finished adding client commands
2024-05-22 08:32:24 -05:00
TAHuntling
ed00551982 feat: Help Command Pagination (#1581)
* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Fixed Comments

Now able to do /command help to see info for said command. Additionally this works for aliases. Fixed serialization missing from merge.

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp
2024-05-21 20:02:07 -05:00
TAHuntling
d6cac65a8d fix: Falling Off Edge in Pet Puzzle (#1584)
* FloatFix

* game activity setting

* Update dNavMesh.cpp

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2024-05-21 20:01:44 -05:00
David Markowitz
a153d0a78c add settings if they dont exist
add helper for spawnerinfo config data
2024-05-21 04:27:17 -07:00
David Markowitz
bfeb10c972 update ldf logic 2024-05-21 04:06:55 -07:00
David Markowitz
82507e642a add erase var helper for entity 2024-05-21 04:06:16 -07:00
David Markowitz
abd978c348 names and descriptions work 2024-05-20 22:39:56 -07:00
David Markowitz
10f8d40b69 saving changes 2024-05-20 21:24:52 -07:00
David Markowitz
37c2c5db5d remove extra map 2024-05-20 20:51:03 -07:00
David Markowitz
693a2fef35 move logic to Item 2024-05-20 20:49:42 -07:00
David Markowitz
787237c930 modularize loading for all possible extra data 2024-05-20 14:33:10 -07:00
David Markowitz
0464f37fab add declaration to header 2024-05-20 13:39:40 -07:00
David Markowitz
78ae8bc6b6 stubbing for saving item extra data 2024-05-20 13:38:59 -07:00
David Markowitz
d8f079cb1b fix mpc resetting on each world load (#1588) 2024-05-20 02:43:57 -05:00
TAHuntling
c8e0bb0db0 feat: Command Sorting (#1580)
* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update SlashCommandHandler.cpp

* Update dGame/dUtilities/SlashCommandHandler.cpp

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

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-05-16 22:02:30 -07:00
David Markowitz
d9d262d3f1 prevent building in a folder which contains spaces (#1583) 2024-05-16 08:05:57 -05:00
TAHuntling
d0a5678290 chore: CppScripts refactor (#1579)
* Updating CppScripts

Rewrote file to use a lambda map rather than the massive if else chain. Kept the original comments alongside each of the different scripts they were by before.

* add script tests

* Update names

* More Changes to Scripts

* Update CppScripts.cpp

* Removing Unneeded Files

* Update CppScripts.cpp

* Delete tests/dGameTests/dScriptsTests/CMakeLists.txt

* Delete tests/dGameTests/dScriptsTests/dScriptsTests.cpp

* Delete tests/dGameTests/dScriptsTests/CppScriptsOld.cpp

* Delete tests/dGameTests/dScriptsTests/CppScriptsOld.h

* Update CMakeLists.txt

* finishing up

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2024-05-16 04:50:18 -05:00
David Markowitz
35321b22d9 script fixes (#1577)
fixes an issue where the sirens would not be destroyed correctly
fixes undefined behavior in buff station

ok for real this time

actual fix for mermaids

and for general death_behavior 0 skill stuff
2024-05-16 04:30:32 -05:00
David Markowitz
8837b110ab add include guards (#1569) 2024-05-16 04:30:00 -05:00
David Markowitz
09a8c99f3e fix: mail crash from underflow and document variables (#1582)
* fix mail crash and document variables

* const
2024-05-16 04:29:48 -05:00
Terrev
e3b108e00e fv race place atm (#1570) 2024-05-13 06:18:27 -05:00
David Markowitz
9f382aca42 fix: use after free in mission progression after removing item from inventory (#1567)
that method is cursed.

no longer has ub when deleting an item from the inventory
2024-05-12 07:30:03 -05:00
David Markowitz
4d1395e522 Update CheatDetection.cpp (#1559) 2024-05-10 16:20:42 -05:00
9e36510c6b chore: Bump verion to 2.3.0 (#1564)
fix versions.txt, update cmake version
2024-05-10 15:21:10 -05:00
David Markowitz
2ca61c3e57 feat: Dragonmaw (#1562)
* rigid as heck

* abstract physics creation to separate function

* loading

Update FvRacePillarDServer.cpp

consolidate abcd pillar logic

modularization

Update SimplePhysicsComponent.cpp

Update EntityManager.cpp

Update MovingPlatformComponent.cpp

still need another pass

* geiser works

* columns working finally

* consolidate logic

* constiness

* Update PhantomPhysicsComponent.cpp

* Update PhysicsComponent.cpp

* revert testing code

* add versions info

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2024-05-10 09:22:26 -05:00
07cb19cc30 chore: remove json (#1561)
we can't use it currently due to threadsafety issues, so just going to remove it until we actually need it and will re-add it as a vendored file later due to cmake issues pulling in things
2024-05-02 06:35:55 -05:00
David Markowitz
794b254fe7 remove md5 new (#1560)
tested that sqlite hash is still calculated
2024-05-02 06:35:44 -05:00
Gie "Max" Vanommeslaeghe
ab7f6f0b57 Merge pull request #1558 from DarkflameUniverse/swap-update-order
fix: out of order physics updates
2024-05-01 22:33:45 +02:00
David Markowitz
58cc569c75 fix out of order physics updates
fixes an issue where physics entities were not given a chance to be marked as sleeping, causing a initial sleeping calls to be missed and causing objects that collided with one another to not register new collisions since they were sleeping at the time the new collision fired off.

Tested that Brick Fury now corectly aggros the _first_ spawn of enemies near by to him.
Tested that the turrets in crux prime now correctly shoot the _first_ wave of enemies that spawn.
2024-04-30 23:09:35 -07:00
Daniel Seiler
35c463656d Use volume for mariadb persistence (#1555)
I initially used the bind mount because it's arguably easier to back up and move around than a volume, but turns out with https://github.com/DarkflameUniverse/NexusDashboard/issues/92 it's nothing we can recommend for Docker Desktop on WSL, which unfortunately is the primary setup newcomers will try this with. So changing the default to be a volume should address that (presumably by hosting the volume within the WSL Docker VM, as opposed to the host NTFS filesystem)
2024-04-29 22:51:13 +02:00
3801a97722 feat: add nlohmann/json lib (#1552)
* feat: add nlohmann/json lib

* remove build test off
2024-04-24 21:35:45 -05:00
jadebenn
0367c67c85 chore: move the pet minigame table loading logic out of petcomponent (#1551)
* move the pet minigame table loading logic out of petcomponent

* misc fixes

* actually, using paths is dumb here when they're already char strings. why bother? silly me.

* removed unga bunga reference-casting

* add back in puzzle not found error message

* pre-allocate unordered map and make getter const-correct

* Update dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp

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

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-04-24 10:09:15 -05:00
jadebenn
8fdc212cda chore: Convert heap allocation to optional (#1553)
* chore: Convert heap allocation to optional

* Update dGame/dComponents/PetComponent.h

Default-initialize
2024-04-24 10:09:04 -05:00
99e7349f6c feat: slashcommands for showall, findplayer, get/openhttpmoninfo, and debug world packet (#1545)
* feat: showall, findplayer, get/openhttpmoninfo

http monitor info is planned to be used later, just putting in info that i've since reverse engineered and don't want lost

Additionally add debug world packet for duture dev use

Tested all new commands and variation of command arguments

* fix missing newline at eofs

* address most feedback

* Compormise and use struct with (de)serialize

* remove httpmoninfo commands
2024-04-17 21:47:28 -05:00
jadebenn
fafe2aefad chore: Nitpicking-utils (#1549)
* nit

* GeneralUtils const-correctness and minor fixes

* use copy instead of reference for char iteration loops

* fix typo and reorganize some functions
2024-04-14 23:14:54 -07:00
David Markowitz
5049f215ba chore: Use string to access SQLite columns (#1535)
* use string to access field name

* Update DEVGMCommands.cpp

* corrected column name

* constexpr array

include <array>

Revert "constexpr array"

This reverts commit 1492e8b1773ed5fbbe767c74466ca263178ecdd4.

Revert "include <array>"

This reverts commit 2b7a67e89ad673d420f496be97f9bc51fd2d5e59.

include <array>

constexpr array

---------

Co-authored-by: jadebenn <jonahbenn@yahoo.com>
2024-04-13 23:41:51 -05:00
David Markowitz
3a6123fe36 fix console sound (#1547) 2024-04-11 10:29:49 -05:00
David Markowitz
b8b2b687e2 inherit exception for CppSQLite3Exception (#1544)
catch any exception just in case exception isnt inherited from
2024-04-10 07:32:54 -05:00
d067a8d12f chore: split out slash commands into multiple files (#1539)
* chore: split out slash commands into multiple files
Breakup the monolithic file
don't register slashcommands on startup

* fix typo
2024-04-09 20:15:51 -05:00
David Markowitz
1ee45639af Update GeneralUtils.h (#1541) 2024-04-09 00:20:25 -05:00
jadebenn
db192d2cde chore: Fix use of uninitialized variable in RemoveItemFromInventory (#1540) 2024-04-08 21:50:41 -07:00
David Markowitz
28ce8ac54d remove usage of xmldoc as a ptr (#1538)
resolves a memory leak in BrickDatabase, adds stability to character save doc.

Tested that saving manually via force-save, logout and /crash all saved my position and my removed banana as expected.
The doc was always deleted on character destruction and on any updates, so this is just a semantic change (and now we no longer have new'd tinyxml2::documents on the heap)
2024-04-08 15:13:49 -05:00
David Markowitz
be0a2f6f14 fix jittering (#1537) 2024-04-08 15:13:31 -05:00
David Markowitz
3260a063cb ignore whitespace in try parse (#1536) 2024-04-08 15:13:19 -05:00
feeac2e041 feat: refactor slash commands system into more scalable system (#1510)
* WIP, but working

* Scaffolding

* testing and making it compile again

* move all commands to functions

* renaming to compile

* fix failing tests

idk how these werent failing before.  Seems to have been magic.

* move commandss into their namespace
make help command useful
fix mac error

TODO: remove the multiple not founds/ rework the structure to split into help and handling

* Just need to fill out the fields, but it's all there templated

* Add all aliases, register missing commands

* All help text

* remove test logs

* improvements

pass through added code for optimizations and cleanup as well as reduce the amount of scoping for readability and maintainability

* Update SlashCommandHandler.cpp

* only save command if it is a GM command

* simplify if checks

* remove broken delimiter

* Update SlashCommandHandler.cpp

* Update SlashCommandHandler.cpp

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2024-04-08 15:11:59 -05:00
jadebenn
18c27b14c8 disable non conforming volatile behavior on MSVC (#1534) 2024-04-05 12:56:23 -05:00
jadebenn
bcfaa6c7fe const return oversight (#1532) 2024-04-05 01:14:52 -07:00
jadebenn
06e7d57e0d chore: Remove dpEntity pointers from collision checking (#1529)
* chore: Remove dpEntity pointers from collision checking

* Update fn documentation in ProximityMonitorComponent.h

* use more idiomatic method to calculate vector index

* feedback

* missed a ranges::find replacement

* adjust for feedback. last changes tonight.

* okay, also remove unneeded include. then sleep.

* for real tho

* update to use unordered_set instead of set
2024-04-05 00:52:26 -05:00
David Markowitz
b340d7c8f9 replace white and blacklist (#1530) 2024-04-05 00:51:40 -05:00
David Markowitz
24de0e5fdb Update GeneralUtils.cpp (#1528)
same check as the header
2024-04-03 19:06:29 -05:00
20408d8dfe chore: remove chat_internal and processes everything over chat connection (#1508)
* WIP

* get rid of redundent case and some formatting issues

* move some things around for cleaner diffs

* remove dead code that does nothing and add connection check

* fix whitespace

* address feedback
2024-03-31 22:27:50 -05:00
David Markowitz
c1c5db6593 update4 fp check (#1524) 2024-03-31 21:46:51 -05:00
David Markowitz
884a41f36a update to current knowledge (#1523)
Should be 100% live accurate as far as logic and bitstream reads goes.

Tested with all valiant weapons and crux prime weapons (drops from dragons) that combat does not desync and that the client reports the same level and amount of skill deserialize issues as before.
2024-03-30 11:16:06 -05:00
David Markowitz
bbc0908989 Update 9_Update_Leaderboard_Storage.sql (#1520) 2024-03-30 08:18:03 -05:00
David Markowitz
5996f3cbf4 fix stewblaster stopping for non-players (#1521)
fixes an issue when stew blaster would stop for non-players and would stand still permanently due to enemy hitboxes being removed.  Tested that stewblaster only stops for players and starts moving when there are no players in the vicinity
2024-03-30 08:17:56 -05:00
jadebenn
150031861d Update README.md (#1518) 2024-03-28 21:32:46 -05:00
jadebenn
9d8e0a9c4a unbreak the stacktraces (#1516) 2024-03-27 06:10:39 +01:00
David Markowitz
bd9b790e1d feat: Add MovingAI pathing for NPCs without combatAI (#1509)
* remove goto

* Update MovementAIComponent.cpp

* convert to PathWaypoint

Easier for usage with paths

* add path parsing

* ref removal, simplification of work

* it works

* Update MovementAIComponent.cpp

* disable pathing for combat

we just need it for npcs for now, combat ai can be done later

* fixed stuttery enemies

wow

* start at ramped up speed

* add pausing and resuming

* Update MovementAIComponent.cpp

* Update MovementAIComponent.h

* Update CMakeLists.txt
2024-03-26 21:06:22 -05:00
David Markowitz
39b81b6263 rename and shorted BehaviorTemplate enum (#1512)
just a renaming of the enum and the value names and deletion of the empty cpp file.  Code compiles still.
2024-03-26 06:35:35 -05:00
David Markowitz
1e09ec92e3 Update PlayerContainer.cpp (#1513)
Prevents a bad actor from possibly spamming the server with sequential IDs and allocating a bunch of memory.

Tested that I can still send and receive friend requests
2024-03-26 06:20:45 -05:00
David Markowitz
2b253a8248 fix: movement ai remove goto, do todo, remove unused call (#1505)
* remove goto

* Update MovementAIComponent.cpp
2024-03-24 22:24:38 -05:00
David Markowitz
3262bc3a86 chore: Remove news in Behavior members (#1504)
* Remove news in behavior members

Tested that GrowingFlowers still have their SkillEvent fired with the correct parameters, gftikitorch works, sharks eating stinky fish still work

* explicitly default move assignment and copy operators/constructors

---------

Co-authored-by: jadebenn <jadebenn@users.noreply.github.com>
2024-03-24 21:43:01 -05:00
David Markowitz
3a4e554da9 update switch behavior (#1503)
was using very old code from pre-foss that has not been updated with the new behavior knowledge.  The code has been updated accordingly to what the client expects.

Tested that ice shurikens can now destroy the legs of the skeleton towers in crux prime.  Tested that the following weapons can still do damage to enemies and objects in the world:
surikens of ice
serratorizer
Super Morning Star
Super Dagger
elite long barrel blaster (charge and normal)
Mosaic Wand
2024-03-24 14:01:12 -05:00
jadebenn
35ce8771e5 chore: supress warnings on external library headers and actually get rid of the last old-style casts (#1502)
* chore: supress warnings on external library headers and actually get rid of the last old-style casts

* remove commented out section I forgot

* update cmake required version to 3.25 unless we can find another way to do this

* update readme

* Update CMakeLists.txt
2024-03-17 20:48:09 -05:00
David Markowitz
b9092a3cce update serialization, remove unused variable (#1501)
Tested that players show up as normal on each others screens, tested that money magnet still works with item 8600, tested that gravity still works in Moon Base.
2024-03-10 01:15:43 -06:00
David Markowitz
0b4f70a76b Update StoryBoxInteractServer.cpp (#1500)
Update StoryBoxInteractServer.cpp
2024-03-08 19:29:40 -06:00
David Markowitz
4bc4624bc9 feat: add further MovementAI skeleton (#1499)
* add movement ai skeleton

Zone loading code is tested to load and read the correct values using logs.  other ldf data is unaffected as I walked around crux and dragons/apes can still spawn and be killed.

* format
2024-03-08 19:29:01 -06:00
jadebenn
3a6313a3ba chore: Table Loading Improvements (#1492)
* Assorted pet improvements

* remove unecessary include

* updates to address some feedback

* fixed database code for testing

* messinng around with tables

* updated to address feedback

* fix world hang

* Remove at() in CDLootTableTable.cpp

* Uncapitalize LOT variable

* Uncapitalize LOT variable
2024-03-06 23:45:24 -06:00
jadebenn
6e3b5acede chore: Less verbose name for enum underlying type casts (#1494)
* Less verbose name for enum underlying type casts

* Remove redundant call
2024-03-06 23:45:04 -06:00
David Markowitz
fe4b29f643 fix: commendation vendor cant accept missions (#1497)
* fix: incorrect serialization for commendation

* Update VendorComponent.h
2024-03-06 19:50:21 -06:00
David Markowitz
fcb89b3c7a Remove multiple Script syntax (#1496) 2024-03-06 19:49:29 -06:00
David Markowitz
1a0aaf3123 add info to debug logs (#1495) 2024-03-06 19:46:16 -06:00
jadebenn
9a26ba0a72 feat: Provide SerializeEntity constant reference overload (#1491) 2024-03-06 19:23:24 -06:00
jadebenn
6c9c826e19 chore: Set default symbol visibility to hidden in CMAKE (#1490)
* set default symbol visibility to hidden in CMAKE

* Update CMakeLists.txt with additional comments

* whoops, wrong comment type
2024-03-06 07:49:40 -06:00
Daniel Seiler
554a9a6806 fix: Dissolve more CMake dependencies (#1387)
* fix: more include changes

* fix: remove dZoneManager from global include

* fix: dDatabase

* fix: dCommon

* fix: object libs

* fix: rebase

* fix: bcrypt

* wip: try simplified connector build

* fix: update dockerfile

* fix: mariadb C/C++ on apple

* feat: Move scripts to CMAKE_MODULE_PATH

* fix: dPropertyBehaviors

* fix: macos?

* fix: Dockerfile

* fix: macos?

* fix: macos?

* fix: macos?

* fix: macos?

* fix: macos?

* try: install_name_tool

* fix not building on unix

* fix include paths

* Remove code changes

Will fix in another PR.

* format pass

remove 2 more included directories.
remove commented out code
add status to messages

* comments and format

surround include directories with quotes
remove commented out code
remove debug messages

* Update CMakeLists.txt

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-03-05 20:13:24 -06:00
c4c1e93dc8 fix: plaques would crash when interacting with no data provided (#1486)
Also docs
2024-03-04 06:00:34 -06:00
David Markowitz
15504e693b fix include paths (#1488) 2024-03-03 19:06:19 -06:00
Gie "Max" Vanommeslaeghe
7d626dc31b Merge pull request #1487 from DarkflameUniverse/voidptr
fix: remove void*
2024-03-02 09:26:31 +01:00
David Markowitz
6df6e3e313 remove void* 2024-03-02 00:25:16 -08:00
jadebenn
4ffdf851c6 Delete unused cpp file (#1485) 2024-03-01 23:32:05 -06:00
43707952d2 feat: move all ldf config to be in xml (#1482)
* feat: move all ldf config to be in xml
cleanup dev-tribute.xml
add comments to atm.xml
remove custom script tag in favor of ldfconfig for it

* replace sto* calls with tryParse's

* remove unesessary .has_value() calls and check for null_lot

* remove member variable naming that on on-member vars

* move max's vendor inventory to be configurable via vanity

* Consolidate triplecated vendor code

* don't write name if one is not given

* Updates to vanity xml's and demo for later docs

* rename vars
2024-02-28 17:16:47 -06:00
David Markowitz
ef3fdba621 fix: FrameStats serialization (#1481)
* fix serialization

Was incorrect before.  The only flags are if any data in the FrameStats has changed, you write them again.  Velocities also do not use dirty flags for their values, they use a flag to determine if their velocity if zero or non-zero.  if any velocity changes, re-write FrameStats.

Tested that 2 players can see each other move as before, enemies move as before and players racing is identical as before.

* Update HavokVehiclePhysicsComponent.cpp
2024-02-27 23:40:26 -06:00
398426545c fix: default chat to the correct port when no option is given (#1484) 2024-02-27 15:56:58 -06:00
David Markowitz
366a80ffd2 comments from movementAI branch (#1483)
tests

tested that red green and yellow bots waved when interacted with
tested that construction robot races when interacted with
wandering vendor does nothing before and after, but script is ready for use when npcs are implemented.

add scripts for robot city
2024-02-27 10:07:14 -06:00
David Markowitz
c9a8be4fb9 Remove extra speed class (#1480)
Speed is used in more waypoints than not so we may as well reduce repeated references.

tested that the data is still loaded as normal in avant gardens

Update Zone.cpp
2024-02-27 08:40:49 -06:00
jadebenn
424d54b98c squash commits (#1479) 2024-02-27 01:29:51 -06:00
jadebenn
b261e63233 chore: Change entity and component logic to use bitstream references (#1468)
* chore: Change entity and component logic to use bitstream references

* merge
2024-02-27 01:25:44 -06:00
jadebenn
75544e3eec chore: Change dServer and related code to use BitStream references (#1476) 2024-02-26 23:43:33 -06:00
jadebenn
9e0dd05d42 chore: Convert LeaderboardManager to use BitStream refs (#1469) 2024-02-26 23:25:45 -06:00
jadebenn
27d20dd8fa Convert AMFSerialize to use bitstream references (#1466) 2024-02-26 23:11:56 -06:00
jadebenn
4b0079c817 chore: Convert DoClientProjectileImpact to use bitstream refs (#1471) 2024-02-26 22:16:19 -06:00
jadebenn
30b9ef8ab2 chore: Change Mail to use BitStream references (#1474) 2024-02-26 22:01:18 -06:00
jadebenn
7235423c7b Convert game message bit stream raw pointers to references (#1465) 2024-02-26 08:17:22 -06:00
jadebenn
94a467b361 chore: Change LDFFormat to use BitStream references (#1467) 2024-02-26 08:15:29 -06:00
jadebenn
c3743877df chore: Change EchoStartSkill to use BitStream reference (#1472) 2024-02-26 08:09:45 -06:00
jadebenn
ab937055e7 chore: Convert EchoSyncSkill to use BitStream references (#1473) 2024-02-26 08:08:56 -06:00
jadebenn
5c1ed332c4 chore: Change AuthServer to use BitStream references (#1475) 2024-02-26 08:06:02 -06:00
jadebenn
95d687846a chore: Remove unnecessary heap allocations (#1478) 2024-02-25 19:35:07 -06:00
192c8cf974 feat: refactor vanity (#1477)
* feat: refactor vanity
cleanup code to be generalized for objects
remove unused party feature
add fallback to data to text
Allow for better organizing data in multiple files
remove special case flag values in favor of config data
general cleanup and fixes

* newline at eof's
2024-02-25 16:59:10 -06:00
David Markowitz
cf706d4974 Remove ag special case patch (#1462)
Tested that revision was never the poison value in any lvl file when starting zone 1100.
2024-02-25 13:56:01 +00:00
e729c7f846 feat: achievement vendor and vendor feedback (#1461)
* Groundwork

* movie buying logic out of gm handler
make transaction result more useful

* Full implementation
Cleanup and fix some calls in gamemessages

* Load the component in the entity
Patch Auth

* new line at eof

* cache lookups

* remove sort

* fix includes
2024-02-25 01:47:05 -06:00
David Markowitz
1328850a8d buffRemoval (#1464)
Update BuffComponent.cpp
2024-02-25 01:01:28 -06:00
jadebenn
721ea78bb4 Update Amf3.h member naming scheme (#1463) 2024-02-24 23:03:59 -06:00
jadebenn
5ae8fd8e0e chore: Remove anonymous namespace from GeneralUtils.h (#1460)
* remove anonymous namespace from GeneralUtils.h

* Put helper functions in nested details namespace to hide from hinting

* rename implementation functions to use lower case, leading underscore and move definitions to source file
2024-02-24 04:30:02 -06:00
David Markowitz
f38537aece fix: incorrectly inverted statement (#1459)
if we DONT find it, we want to kill/delete it.  not the other way around where if we find it we try to delete it again.

tested that you no longer crash when trying to login
tested that bird monument issues are fixed
2024-02-20 05:51:02 -06:00
jadebenn
b6af92ef81 refactor: Rewrite BehaviorMessage classes to use member initialization, preferred member naming conventions, and const-ref getters (#1456)
* Split out BehaviorMessage class changes from PR #1452

* remove <string_view> inclusion in ActionContext.h

* add the arguments nullptr check back in

* remove redundant std::string constructor calls

* Update AddStripMessage.cpp - change push_back to emplace_back
2024-02-18 00:38:26 -06:00
David Markowitz
c7b3d9e817 close trade window (#1457) 2024-02-11 21:00:39 -06:00
David Markowitz
c3fbc87f9e remove copy pasted logic (#1446)
tested that the ninjago platforms and fire still works, tested that point of interest work as well

Update PhantomPhysicsComponent.cpp
2024-02-11 14:36:15 -06:00
David Markowitz
dfb2fd93b4 chore: use ranges in EntityManager and touch up (#1451)
* EntityManager: ranges and cleanup

Use LWOOBJID for ghosting entities
use ranges::views::values for associative container iteration
remove dead code
comment magic numbers
little bit of optimization (not enough to be game changing or take the time to measure, they are free speedups anyways, we take those)
use cstdint types

* use size_t

* use lwoobjid for ghost candidate
2024-02-11 14:28:25 -06:00
David Markowitz
ddaac276fe fix: Remove hard coded groups for spawners (#1454)
* fix macros not trimming newline

* Remove hardcoded spawner groups

Was just missing an inline script implementation and using the wrong name
2024-02-11 00:38:21 -06:00
David Markowitz
f7e4a32621 Fix no effect playing when picking up item (#1455) 2024-02-11 00:29:05 -06:00
ecaaea3175 chore: make predefined name unique when the name files are not provided (#1453)
Multiple characters would not be allowed to be made since they would both have tha name INVALID, that is no longer the case
2024-02-11 00:08:22 -06:00
jadebenn
29666a1ff7 chore: General cleanup roundup (#1444)
* Moved unrelated changes out of the TryParse PR branch

* const correctness and cstdint type usage

* removing a few "== nullptr"

* amf constexpr, const-correctness, and attrib tagging

* update to account for feedback

* Fixing accidentally included header and hopefully fixing the MacOS issue too

* try reordering the amf3 specializations to fix the MacOS issue again

* Amf3 template class member func instantiation fix

* try including only on macos

* Using if constexpr rather than specialization

* Trying a different solution for the instantiation problem

* Remove #include "dPlatforms.h"
2024-02-10 13:44:40 -06:00
jadebenn
0c1ee0513d refactor: Change TryParse implementation (#1442)
* Changed how the TryParse function works (and also did some general cleanup along the way)

* Update noexcept attributes (verified these are correct)

* Add fp overload for MacOS functionality

* resolving some feedback

* Split out unrelated changes to CleanupRoundup branch

* Update in response to feedback

* the consequences of emo's member variable renaming request

* Revert "the consequences of emo's member variable renaming request"

This reverts commit bf318caeda.

* Fully revert renaming attempt

* Revert "the consequences of emo's member variable renaming request"

This reverts commit bf318caeda.

Fully revert renaming attempt

* Created ClientVersion.h and moved the client version defaults to it

* Fix partial parsing and MacOS floating point errors

* attempting fix to MacOS compiler error

* syntax pass (should be the last commit unless the CI fails)

* ah, wait, forgot to uncomment the preprocessor statements for MacOS. THIS should be the last commit pending CI

* Okay, one last thing I noticed: We were including C headers here. Now they're C++ headers. Pinky swear this is it!

* typo and I am OCD. please let this be the last

* hash is usally but not always noexcept, so the specifier should go

* Address MOST of the feedback

* address the claim codes issue
2024-02-10 05:05:25 -06:00
David Markowitz
62b670d283 remove sha512, move md5 to thirdparty (#1443)
title.

Tested that Auth runs and I can login still.
2024-02-09 09:15:28 -06:00
David Markowitz
d2aeebcd46 chore: Move database storage containers to be translation unit local to allow for safe references (#1434)
* Move CDClientManager to be a namespace

Tested that worlds still load data as expected.  Had no use being a singleton anyways.

* Move cdclient data storage to tu local containers

Allows some data from these containers to be saved on object by reference instead of always needing to copy.

iteration 2

- move all unnamed namespace containers to a singular spot
- use macro for template specialization and variable declaration
- use templates to allow for as little copy paste of types and functions as possible

* remember to use typename!

compiler believes T::StorageType is accessing a member, not a type.

* Update CDClientManager.cpp

* move to cpp?
2024-02-09 07:37:58 -06:00
David Markowitz
dc29f5962d Move CDClientManager to be a namespace (#1431)
Tested that worlds still load data as expected.  Had no use being a singleton anyways.
2024-02-08 23:40:43 -06:00
Gie "Max" Vanommeslaeghe
24f94edfeb Merge pull request #1447 from DarkflameUniverse/dpGrid_speedup
chore: Use vector instead of forward_list for dpGrid for ~50% physics loop speed improvement
2024-02-08 14:54:51 +01:00
David Markowitz
e8aa491904 Merge branch 'main' into dpGrid_speedup 2024-02-08 03:55:36 -08:00
David Markowitz
395e5c1c66 Remove transitive include for Detour (#1450)
Moves it to the 1 cpp file that uses it and locks down its header.

Still compiles and links.
2024-02-06 08:53:51 -06:00
David Markowitz
a88ef0c325 Update dpGrid.cpp 2024-02-04 16:44:40 -08:00
David Markowitz
8e29148137 actually use clamped value 2024-02-04 16:39:00 -08:00
David Markowitz
f0b6ad89d9 chore: Player class removal (#1445)
* SystemAddress and destructor

* move respawn logic to character comp

Tested that respawn pos and rot can be set as per previously by crossing a respawn point and smashing to see if I would respawn at the new place.

* Move loot cheat checking

* Remove GetParentUser overload

Tested completing missions
control behaviors
collecting life crate
completing a bunch of missions using macros
loading into worlds
brick-by-brick
placing models
digging the x spot in gnarled forest
can still ban and mute players
cheat detection is still doing its thing
flags are still set (checked with flag 45)
claim codes still work (created new char, checked the lego club mail was there)

* Move player constructor logic

Its now at the bottom of Entity constructor.  Time to remove Player

* Remove Player class

Removes the Player class.  Tested that I can still login and see another player in Venture Explorer and logging out a few times still works as well as smashing enemies

* store ptr

* Update SlashCommandHandler.cpp
2024-02-04 06:29:05 -08:00
David Markowitz
f2e7d2eaac Use vector instead of forward_list for dpGrid
also swap contains in for find != end on an associative container since we are in c++20 now :)
2024-02-04 06:10:07 -08:00
jadebenn
050184c558 chore: nitpicks on rendercomponent changes (#1440)
* nitpicks on rendercomponent

* undo constexpr
2024-02-01 09:43:28 -06:00
jadebenn
b23981e591 chore: Update render component and delete unused code (#1429)
* Update a few components to use smart pointers for memory management

* 'final' keyword added to classes

* removed duplicate 'const'

* removed unused code

* Updated render component to store effects directly in a vector

* Use move instead of copy

* make pointers const

* attribute tags

* nitpicking

* delete default effect constructor

* Added a vector size check to the RemoveEffect() function

* use empty() instead of size()
2024-01-31 08:38:38 -06:00
jadebenn
d78b50874c chore: upgrade MacOS build settings for better C++20 compatibility (#1435)
* upgrade MacOS build settings for better C++20 compatibility

* add fixes I forgot

* 3rd try

* Update UserManager.cpp

* Update CMakeLists.txt

* End with newline

* Update CMakeLists.txt

* update to reflect feedback

* Update CMakeLists.txt to disable deprecation warnings on SHA512

* attempt to disable sqlite warnings

* revert last attempt (didn't work)

* disable sqlite deprecation warnings on MacOS
2024-01-29 21:45:50 -06:00
jadebenn
a0d51e21ca refactor: allow usage of NiPoint3 and NiQuaternion in constexpr context (#1414)
* allow usage of NiPoint3 and NiQuaternion in constexpr context

* removed .cpp files entirely

* moving circular dependency circumvention stuff to an .inl file

* real world usage!!!!!

* reverting weird branch cross-pollination

* removing more weird branch cross-pollination

* remove comment

* added inverse header guard to inl file

* Update NiPoint3.inl

* trying different constructor syntax

* reorganize into .inl files for readability

* uncomment include

* moved non-constexpr definitions to cpp file

* moved static definitions back to inl files

* testing fix

* moved constants into seperate namespace

* Undo change in build-and-test.yml

* nodiscard
2024-01-29 01:53:12 -06:00
David Markowitz
2f247b1fc9 fix: faction changes not allowing updated targets (#1437)
* fix faction change issue

fixes an issue where enemies who would have their faction changed would not change aggro targets.

Tested that stromling mechs and ronin/horsemen in forbidden valley still aggro on spawn as expected.

* use erase remove if
2024-01-29 01:52:59 -06:00
Daniel Seiler
54ded62757 Update README.md (#1438)
Add warning about docker-compose on windows
2024-01-27 14:37:30 -06:00
jadebenn
5225c86d65 chore: Misc. component cleanup (#1433)
* Misc component cleanup

* Update InventoryComponent.h

* Update MissionComponent.h

* Update PropertyManagementComponent.h

* Update PropertyVendorComponent.h

* Update SkillComponent.h

maximum pedantry B)

* SoundTriggerComponent.h braces gone

* Rename SoundTriggerComponent.h braces gone to SoundTriggerComponent.h

I was tired
2024-01-23 23:13:23 -06:00
David Markowitz
d5e757bd9d fix spiders spawn camping in some circumstances (#1422)
Tested that now spiders cant spawn camp you at the entrance near the spider queen battle.
2024-01-21 03:29:46 -08:00
jadebenn
a1ac692b49 use reference syntax (#1430) 2024-01-20 17:22:56 -06:00
jadebenn
fcb9f671ae Upgrade Ubuntu to 22.04 (#1432) 2024-01-20 17:19:31 -06:00
David Markowitz
36f7b8a928 remove singleton for dpWorld (#1427)
Removes the singleton inheritance from dpWorld.
Tested that crux prime, nimbus station, avant gardens and nexus tower still use navmeshes and physics and that physics volumes are still collided with.
2024-01-19 15:12:05 -06:00
David Markowitz
ea5360cb99 fix: turn warnings into errors (#1425)
* Update PlayerContainer.cpp

fix: turn warnings into errors

for the few warnings we get, at least make sure we listen to them now on unix platforms.  Windows has too many right now to enable /WX

resolve warning

actually fix it

Update CMakeLists.txt

* detour pls

* Update CMakeLists.txt

* I HAVE 20 DOLLARS AND I NEED A WII GAME FOR MY KID

* I HAVE 0 DOLLARS NOW

* don't look don't look

* Revert "don't look don't look"

This reverts commit 5603eb5980.

* Revert "Revert "don't look don't look""

This reverts commit a334832a4d.

* could it be

* we found one (but its already reported)

not resolved yet though.

* Revert "don't look don't look"

This reverts commit 5603eb5980.

* ignore warning for file

* another one

* Update .gitmodules

* comments
2024-01-19 10:18:36 -06:00
David Markowitz
4f3b4f5f43 fix uninitialized variable in PlayerContainer (#1423) 2024-01-18 02:11:49 -06:00
David Markowitz
6bf084ef8f Add announcement for mismatched fdb (#1424)
adds an announcement sent to the system address which had the mismatched FDB to let the developer know they have a mis-matched one.

Tested that if a civilian tries to login without a gm level > developer, they are kicked.
Tested that if a GM is found to have a mismatched FDB, they are let in but have an announcement sent to them.

Use auth packets for msg

added comment as to why

ff

remove default

add comment

Remove broadcast
2024-01-18 02:10:52 -06:00
99b3705a76 Revert "fix: Remove pending timer logic" (#1417) 2024-01-14 22:05:50 +01:00
David Markowitz
c83ec8228c chore: Move Player ghosting functionality to GhostComponent (#1413)
* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.

* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.
- Remove static storage container (static containers can be destroyed before exit/terminate handler executes)

* remove player cast

* Remove extra includes

* Add a player manager

Used for the static Player functions.  Further removes stuff from the Player class/file.

* chore: Move ghosting functionality to component

Tested that ghosting still works and players are still firing off the OnPlayerLeave and relevant handlers.

* move to unordered_set
2024-01-14 13:10:13 -06:00
David Markowitz
0a30430c4f fix: Remove pending timer logic (#1416)
* remove pending timers

they serve no purpose anymore since iterator invalidation is a non-issue.  I added this initially to make it so if you added a timer this frame, there would be at least 1 frame before you would start it, but this in practice doesnt serve a purpose

* timers still work
2024-01-14 03:46:56 -06:00
6592bbea46 chore: remove all raw packet reading from chat packet handler (#1415)
* chore: default size to 33 on LU(W)Strings since that's the most common lenght
Was doing this on other places, but not the main one

* chore: remove all raw packet reading from chat packet handler

and general chat packet cleanup

* fix team invite/promote/kick

* Address feedback

* fix friends check

* update comments

* Address feedback
Add GM level handeling

* Address feedback
2024-01-14 01:03:01 -06:00
David Markowitz
a62f6d63c6 chore: Move static Player functions and internal linkage to manager class (#1412)
* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.

* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.
- Remove static storage container (static containers can be destroyed before exit/terminate handler executes)

* remove player cast

* Remove extra includes

* Add a player manager

Used for the static Player functions.  Further removes stuff from the Player class/file.
2024-01-13 03:40:56 -06:00
0bc12141c3 chore: default size to 33 on LU(W)Strings since that's the most common lenght (#1410)
Was doing this on other places, but not the main one
2024-01-12 14:23:44 -06:00
David Markowitz
8b6fb8fb44 Add ghost component (#1409)
will be used to migrate other Player functionality in a future PR.

Tested that I can login still.
2024-01-12 13:18:28 -06:00
David Markowitz
929d029f12 chore: Simplify and move Player functionality to relevant component (#1408)
* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.

* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.
- Remove static storage container (static containers can be destroyed before exit/terminate handler executes)

* remove player cast

* Remove extra includes
2024-01-12 11:39:51 -06:00
David Markowitz
66cc582a9a chore: update noninformative comments to be informative (#1407)
* better comments

* more comments
2024-01-10 20:57:41 -08:00
David Markowitz
e0ddbce8e7 chore: remove non cstdint integer types in client ORM (#1405) 2024-01-08 23:54:14 -08:00
jadebenn
4a50c60559 chore: Assorted pet improvements (#1402)
* Assorted pet improvements

* remove unecessary include

* updates to address some feedback

* fixed database code for testing

* Removed reference member (for now)

* Removed cmake flag
2024-01-08 15:32:09 -08:00
Daniel Seiler
fbdcc17bb5 fix: Docker Compose README (#1403)
* fix: Docker Compose README

* fix: add password requiremnts hint

* fix: note on sudo / docker compose

* fix: add note on UGCUSE3DSERVICES

* fix: reorder

* fix: duplicate word

* fix: move broken line

* fix(compose): enable USER_ENABLE_REGISTER by default
2024-01-08 09:13:28 -06:00
David Markowitz
e4469f997e fix: activity id not using overridden id (#1400)
* patch activity id not using overridden id

Update ActivityComponent.h

* Update ActivityComponent.cpp

* Update ActivityComponent.cpp
2024-01-07 07:43:53 -06:00
dbe4a0ced3 chore: continue work on removing raw packet reading (#1404)
* chore: continue work on removing raw packet reading
tested that logging in, deleted a char, renaming a char, and transfeering to a zone all work still

* Address Feedback
2024-01-07 02:02:27 -06:00
David Markowitz
b683413a60 Remove deps from dNet (#1401) 2024-01-07 08:05:57 +01:00
David Markowitz
14c20fbd62 change timers to not use ptrs (#1399)
add comments as to why logic may seem confusing.
2024-01-06 03:45:23 -06:00
325598cd99 feat: fully reading auth packets and use stamps (#1398)
* feat: fully reading auth packets and use stamps

* fix the stupid define

* Address feedback

* no more Stamp(...)
2024-01-06 02:16:10 -06:00
David Markowitz
0c104e819d chore: Consolidate logger setup and add better handling of packets (#1389)
* Logger to Server class

Dont handle master packets from our clients

* move to namespace

Revert "remove extra headers"

This reverts commit ac7b901ece22165cdb0f38fca4be7d8fdf004de8.

remove extra headers

no changes otherwise.

* Merge branch 'main' into server_consolidation_of_work

* Update WorldServer.cpp

* fix submodule version

---------

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2024-01-05 23:32:09 -06:00
David Markowitz
0dc6763a3c chore: Remove pointer management in PlayerContainer (#1397)
* const

use cbegin, remove null checks

they are references now, they cant be null themselves

Change to reference instead of ptr

Remove getter

const

* allow duplicate request

without the reply you apparently need to re-log for some reason.

Update PlayerContainer.cpp
2024-01-05 22:42:30 -06:00
jadebenn
440dc8b88f replace macro with anonymous namespace function (#1394) 2024-01-05 18:55:42 -06:00
Demetri Van Sickle
3deec6499d Update Commands.md (#1396)
> Added `spawngroup` command
> Recorded some commands that were out of order from least to greatest GM level
> Filled in missing admin levels
> Added alt spellings of commands
> Fixed incorrect GM level of `fly` command from 4 to 6
2024-01-05 18:49:56 -06:00
David Markowitz
15a461d857 fix crash (#1395)
Update SlashCommandHandler.cpp
2024-01-05 18:49:21 -06:00
David Markowitz
2804dc3ec2 fix: make include paths consistent (#1390)
* fix: bad header includes

tests pass

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

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

* fix: formatting

* fix: newline

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

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

* fix crash

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

* Section off code

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

* Organize serialization

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

* Remove and simplify BlockDefinitions

Remove pointer usage for BlockDefinitions and move to optional.

* ControlBehaviors: Add addaction handling

* re-organization

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

* add strip ui position handling

* add split strip functionality

* fix issues

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

* update how you remove behaviors from models

* Add remove actions and rename

* migrate actions

* update action and rearrange strip

* merge strips

* add and move to inventory

* Remove dead code

* simplify code

* nits and move finish MoveToInventory

constify serialize

further include path fixes

use const, comments

fix amf message

Update ModelComponent.cpp

replace operator subscript with at

* Update ModelComponent.cpp

* Update MigrateActionsMessage.h

* const

* Move to separate translation units

* include amf3

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

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

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

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

* explicitly include mariadb libs

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

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

* coments to dockerfile

* Revert master server changes

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

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

* fix nullptr access for logger

fix no save on crash

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

FINALLY

dont ignore cmake module directory

move to separate file

very cool feature
tested that this still works

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

* fix warnings and errors

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

* fix: flush WorldServer logger before main loop

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

Don't return early if there are no party phrases

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

* Remove ambiguous include path

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

* Revert settings

* working

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

* fix: PascalCase
2023-12-30 07:07:49 -06:00
Daniel Seiler
98d2f25af2 feat: get & print std::current_exception (#1366) 2023-12-30 08:04:26 +01:00
Daniel Seiler
dd9d94f75f feat: allow env var override for game config (#1367) 2023-12-30 08:04:09 +01:00
Daniel Seiler
f08df25085 feat: split out system() calls from the rest of MasterServer (#1368) 2023-12-30 08:00:43 +01:00
1bdce8384f fix: LUP Exhibit component type and use delta compression on serialization (#1365)
* fix: component type and use delta compression on serialization

* sorry jett, emo made me do it
2023-12-28 22:24:45 -06:00
15954413ae chore: rename rebuild to quickbuild (#1364)
* rename rebuild to quickbuild

* fix includes
2023-12-28 22:24:30 -06:00
fddf99946f chore: make scripted activity derived from activity (#1363)
* chore: make scripted activity derived from activity
this paves the way for other component being properly derived from the activity component

* use a dirty flag instead

* address feedback
2023-12-28 19:11:19 -06:00
jadebenn
ef6f2f133e chore: Eradicate C-style casts and further clean up some code (#1361)
* cast and code cleanup

* cast cleanup

* bug fixes and improvements

* no getBoolField method exists

* fixes

* unbroke sg cannon scoring

* removing comments

* Remove the c-style cast warning I added from CMakeLists now that they're gone (it triggers on 3rd party dependencies and slows down compilation)

* (Hopefully) fix MacOS compilation error

* partially-implemented feedback

* more updates to account for feedback

* change bool default

---------

Co-authored-by: jadebenn <jonahebenn@yahoo.com>
2023-12-27 22:18:20 -06:00
David Markowitz
46ac039a3b fix: mission offering (#1359)
fixes an issue where NPCs would offer the incorrect missions which caused odd blocks.  Consolidated logic for mission offering and removed redundant code.
2023-12-26 15:45:10 -08:00
David Markowitz
ffa2f9986c AssetManager: fix deleting no file (#1358) 2023-12-25 08:48:57 -06:00
Jett
d2d67384a7 fix: update trigger loading code after typo (#1357) 2023-12-24 21:45:27 +01:00
David Markowitz
dbf37c2d2b fix: more than 1 zone control object causing issues with scripted minigames (#1355)
* fix: more than 1 zone control object

Fixes an issue when players cannot load into Avant Gardens Survival and other script based minigames with more than 1 zone control object in the level files.  (why are there 26 zone control objects in BoNS and AGS)

* Update Level.cpp
2023-12-24 11:17:04 -06:00
David Markowitz
81dc4e2216 fix: remove cancelOnLogout handling (#1354) 2023-12-24 08:01:47 -06:00
jadebenn
6de224a2fa chore: miscellaneous code, formatting, and syntax cleanup (#1351)
* miscellaneous code, formatting, and syntax cleanup

* update

* update again

* updated to account for feedback
2023-12-23 18:11:00 -06:00
David Markowitz
5e9355b1ff fix: shooting gallery bugs (#1347)
fix: reset some more vars

Co-authored-by: Aaron Kimbre <aronwk.aaron@gmail.com>
2023-12-23 17:50:14 +00:00
David Markowitz
6b9798595e fix: leavezone command (#1345)
Players do not need access to leave Return to the Venture Explorer (rttve) via a slash command.  They can leave through the usual zone method.

Tested that you cannot leavezone in rttve
2023-12-23 11:28:32 -06:00
David Markowitz
e58218cfbc chore: Speed up and cleanup level and zone loading; Add safer asset buffer reading (#1314)
* Remove std::couts littered throughout the base

* working

End of optimizations for now

going faster

* Remove extraneous compare function

std::less<LWOSCENEID> already does this in a map.

* gaming

* Update Zone.cpp

* dlu is moving to bitbucket again

* Update Level.cpp

---------

Co-authored-by: Jettford <mrjettbradford@gmail.com>
2023-12-23 11:24:16 -06:00
jadebenn
fcf4d6c6fa feat: Improve console output to show packet enum names (magic_enum) (#1344)
* add enum stringification functionality from third party source

* squashed commit

* Macros: Add test and improve speed

Space macros out
utilize cache locality
ensure no lost functionality

* moved stringify code to dCommon

* Rename #defines in stringify enum tests

* Revert "moved stringify code to dCommon"

This reverts commit 33fa5f8d2f.

* improve macro functionality

change function handle

formatting and function definition tweaks

* typo fixes

* moved code to dCommon/dEnums and tests to dCommonTests/dEnumsTests

* initial magic_enums alternate implementation of enum stringification

* deleted unused tests

* reverted compile flag oopsy and fixed output types

* fixed testing suite

* test formatting improvement

* formatting again :(

* added gm string to "aborting gm!" message

* Push my suggestion for CI tests.

* updated magic enum test

* fix test variable type

* added gm test

* making sure magic_enum is on a release branch

* tidying up console outputs

* re-implemented enum array access for performance

* now it is bugged :(

* nvm, working

* helping out the snowflake compilers

* changed return type too

* optimization too

* formatting too I guess because why not

* being even more painfully specific

* Update WorldServer.cpp to match emo's feedback

* Update MagicEnumTests.cpp to use srand(time(NULL))

* Update eGameMessageType.h - formatting

* Trying to fix the crash but can't actually compile the code to check on my own rn

* Update WorldServer.cpp - third try at this

* Update MagicEnumTests.cpp - use better macro definitions

* Update MagicEnumTests.cpp - c string comparison fix

* addressing all but the cmake feedback

* fixed cmake to the best of my very limited ability

* added tests to verify magic enum arrays are pre-sorted

* updated

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
Co-authored-by: Jettford <mrjettbradford@gmail.com>
2023-12-23 10:51:59 -06:00
David Markowitz
c1e8546d48 fix: add Nexus Tower missing scripts (#1349)
add final missing scripts for nt

also fix the turnin for the breadcrumb missions not showing the completion window.

Fix another missing script

Add another script

fix include guards

Fix dirt clouds not appearing on mission accept
2023-12-22 23:53:21 -08:00
jadebenn
c07c909a57 fix: Character access after deletion (#1348)
* assorted skill debug plus crash fix

* removed uncleaned debug
2023-12-21 22:12:52 -06:00
42ffd00478 feat: implement handling vendorStatusRequest (#1346)
this fixes the crafting menu on johny umami not being there on the initial internaction
2023-12-20 22:25:21 -06:00
jadebenn
799269c79e fix: Using 'leave-zone' in Return to the Venture Explorer allows players to return to the original Venture Explorer map (#1341)
* fixed VE zone bug and added config option to renable it

* add zero back to LWOMAPID initialization

* typo

* removed config option to enable bug
2023-12-20 15:12:01 -06:00
jadebenn
e2391665b9 imagination costs that equal your capacity no longer abort qbs (#1338) 2023-12-16 19:35:16 -06:00
David Markowitz
24c2361248 fix: achievements counting twice when accepted (#1337)
Tested that new achievements progress exactly once
Tested that already accepted achievements progress as expeected
Tested that tiered achievements only count the progress to the current tier and not the next one as well

Update MissionComponent.cpp
2023-12-16 12:27:14 -06:00
David Markowitz
511672c5cb fix: general crashes (#1336)
* Fix crashes

fix crash with chat filter
fix ldf_config being empty in database on windows debug

* WorldServer: Fix further crashes on windows

address multi threaded signal handling on worldservers
Remove iterator invalidation code in zone manager
2023-12-16 12:27:06 -06:00
Gie "Max" Vanommeslaeghe
c0b969e3f0 Merge pull request #1333 from DarkflameUniverse/chore-rename-zonecontrol-to-minigamecontrol
chore: rename zonecontrolcomp to minigamecontrolcomp
2023-12-06 14:39:21 +01:00
1f399a7277 chore: rename zonecontrolcomp to minigamecontrolcomp 2023-12-04 20:59:08 -06: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
Max
a19bead268 Fix edge case where leaderboard viewing would cause unhandled exception 2023-10-29 22:40:06 -04: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
David Markowitz
23d71340c9 Scripts: Fix possible nullptr access (#1232)
unsure how to reproduce the actual bug, however we can see that with the following crash dump
```
Entity::GetComponent(eReplicaComponentType) const(+0x4) [0x56095665e634]
BossSpiderQueenEnemyServer::OnDie(Entity*, Entity*)(+0x28d) [0x560956795d0d]
Entity::Kill(Entity*)(+0xf8) [0x5609566637a8]
ZoneAgProperty::BaseTimerDone(Entity*, std::string const&)(+0x89b) [0x56095683736b]
Entity::Update(float)(+0x2b6) [0x560956662676]
EntityManager::UpdateEntities(float)(+0x2e) [0x56095667305e]
```
that the actual crash issue starts at
```
Entity::Kill(Entity*)(+0xf8) [0x5609566637a8]
ZoneAgProperty::BaseTimerDone(Entity*, std::string const&)
```
BaseTimerDone calls Kill, and there is only 1 call to Kill in the function which calls Kill no arguments, meaning the killer is a nullptr.  This propogates its way to the BossSpiderQueenEnemyServer::OnDie wherein we blindly check the killer pointer without verifying that the pointer is actually valid.

This patch simply checks that killer is valid before access to address the hole.
2023-10-22 14:53:54 -07:00
David Markowitz
5942182486 feat: Abstract Logger and simplify code (#1207)
* Logger: Rename logger to Logger from dLogger

* Logger: Add compile time filename

Fix include issues
Add writers
Add macros
Add macro to force compilation

* Logger: Replace calls with macros

Allows for filename and line number to be logged

* Logger: Add comments

and remove extra define

Logger: Replace with unique_ptr

also flush console at exit. regular file writer should be flushed on file close.

Logger: Remove constexpr on variable

* Logger: Simplify code

* Update Logger.cpp
2023-10-21 16:31:55 -07:00
David Markowitz
131239538b Entity: Add missing destroyable case (#1228)
Adds a missing edge case on the client where you may have the is_smashable flag set, but dont actually have a destroyable component.  In this case you should create only a destroyable component and add it to the component and serialize it last after all other data.  We also needed to add the set_faction var reading and setting as this is how Oliver Sudden actually gets their faction since they have no actual information in the database, it is all stored in ldf keys.

Tested that Oliver Sudden no longer logs Unable to unserialize logs when serialized or constructed.
2023-10-19 12:42:43 -05:00
Gie "Max" Vanommeslaeghe
4c507e34a5 Merge pull request #1219 from DarkflameUniverse/lol
fix: Address race condition
2023-10-19 12:25:27 +02:00
David Markowitz
50921cce2d Loot: Fix rarity sorting (#1226)
Missed this step from the previous speedup.  rollloot numbers match within margin of error to numbers pre-cache improvement.
2023-10-18 09:18:47 -05:00
David Markowitz
3806891db0 Teams: Default team loot to ffa (#1224) 2023-10-18 09:18:22 -05:00
David Markowitz
ba91058736 Destroyable: Fix animated deaths (#1227)
Fixes #1222

addresses an issue where the death behavior of a destructible component was not being respected and enemies with destroyable components that had special death animations were not able to play the animation on death.  This pr adds in the hardcoded constant the client uses for the same metric of 12 seconds.

Tested that claiming Nimbus Rock and completing the property guards mission allows him to vacuum away and then network the destruction packet 12 seconds later.
2023-10-18 09:17:57 -05:00
David Markowitz
73e70badb7 Entity: fix bad ldf key serialization (#1225) 2023-10-17 08:45:46 -05:00
David Markowitz
e4cae35edb PhantomPhysics: Fix gravity (#1221)
Fix gravity not being adjusted when colliding with a phantom physics object

Tested that while on moonbase, the players gravity is no longer reset to 1 when they change their cheat info.
2023-10-16 07:55:57 -05:00
David Markowitz
2746683235 Master: Address race condition
Fix a race condition that would overwrite the port of zone 0 with the auth server port or a chat server port which would cause a domino effect of desynced server ports

Tested that a sleep(5) in auth still allows a player to connect and login without issues
2023-10-14 00:01:41 -07:00
David Markowitz
c6087ce77a perf: Loot memory savings (#1165)
* Move away from constructor queries

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

* Huge loot namespace rework

- Remove all excess memory usage
- do not cache components registry
- cache loot matrices on startup of the destroyable component
- convert loot singleton class to a namespace
- rework loot cdclient tables to operate closer to how someone would actually use them (basically doing the previous LootGenerator::LootGenerator caching but in those tables)
- Memory usage reduced by 10%+ across the board

* cache rebuild matrix

* Database: move reading to own function

Also change name of cache to PascalCase

* Database: Move common function rading
2023-10-09 15:33:22 -05:00
David Markowitz
e96fd56fbd Diagnostics: Fix demangling (#1215)
Rename to better names
More comments
Less branches
2023-10-09 15:31:43 -05:00
David Markowitz
500ae4d6e5 Scripts: Fix ambiguous base class (#1216) 2023-10-09 15:31:25 -05:00
David Markowitz
094797881b Cannon Cove: Fix incorrect sign (#1211)
Update CMakeVariables.txt

Cannon Cove: Fix incorrect sign
2023-10-09 15:29:11 -05:00
David Markowitz
3dd2791066 chore: Use TryParse for LDF parsing (#1206)
* LDF: Simplify parsing

* Update GeneralUtils.h
2023-10-09 15:22:40 -05:00
570c597148 WIP (#1203) 2023-10-09 15:20:56 -05:00
David Markowitz
ad003634f4 chore: Physics Component abstraction and addition of tests (#1159)
* Make serialize actually virtual

yep

* Abstract to PhysicsComponent

Move shared functionality of all physics related classes to a base class.

Tested that there were no failed to unserialize errors when in main gameplay in Gnarled Forest or in a race.

Tested that 2 players were able to see each other in the above scenarios just fine as well.

* Update PhantomPhysicsComponent.cpp

* Add SimplePhysicsTest

* Add construction test

* Update SimplePhysicsComponentTests.cpp

* remove flags and fix override

* Update VendorComponent.h
2023-10-09 15:19:38 -05:00
d8ac148cee refactor: re-write AOE, add FilterTargets, Update TacArc Reading (#1035)
* Re-write AOE behavior for new filter targets
Update Tacarc to use new filter targets
Added dev commands for skill and attack debugging

* Get all entities by detroyable
rather than controllable physics
Since destroyables are what can be hit

* Re-work filter targets to be 100% live accurate
reduce memory usage by only using one vector and removing invalid entries
get entities in the proximity rather than all entities with des comps in the instance, as was done in live

* remove debuging longs and remove oopsie

* address feedback

* make log more useful

* make filter more flat

* Add some more checks to filter targets
add pvp checks to isenemy

* fix typing

* Add filter target to TacArc and update filter target

* fix double declaration

* Some debugging logs

* Update TacArc reading

* make log clearer

* logs

* Update TacArcBehavior.cpp

* banana

* fix max targets

* remove extreanous parenthesesuuesdsds

* make behavior slot use a real type

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-10-09 15:18:51 -05:00
Gie "Max" Vanommeslaeghe
471d65707c Merge pull request #1217 from DarkflameUniverse/pre-req-fix
fix: Fix wisp lee mission
2023-10-09 15:11:23 +02:00
David Markowitz
94f8a99fba Revert: Fix wisp lee mission
This reverts commit d7e16ab589697fd1a0a270a02c622b8fa752638f.

Fixes an issue where the incorrect mission is marked as being offerable and this overwrote the original mission offered by wisp lee and forced him to offer a different, unacceptable mission.

Tested that completing the mission twice from a new daily state and a repeatable state both completed the chain correctly.  Unsure what the original bug was, but it does not appear to be present.
On top of that, there is no pre-requisite for mission 1883 anywhere in the cdclient, so at best, this check was always false, but the correct behavior is exact equivalence for the mission state.
2023-10-08 23:23:14 -07:00
TAHuntling
288991ef49 fix: Players are able to join a race without having a race car (#1149)
* Fixed Scrapped Racecar Stuck Issue

Changed RacingControlComponent to boot players back to the hub world when trying to race after dismantling a vehicle.

* Modified OnPlayerLoaded to fix Vehicle Inventory issue

* Change 3

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-10-08 19:38:48 -05:00
David Markowitz
74cc4176e1 Update RigidbodyPhantomPhysicsComponent.h (#1213) 2023-10-08 15:58:47 -05:00
b33e9935f5 fix: GM 1272 was labled as 1273 (#1210) 2023-10-05 23:20:20 -07:00
David Markowitz
258ee5c1ee CheatDetection: Move player access (#1209) 2023-10-05 15:31:05 -07:00
David Markowitz
a8820c14f2 AssetManager: Match allocators (#1205)
Currently on line 116 of AssetManager.cpp, we have the following
`*data = (char*)malloc(*len);`
however on line 45 of AssetManager.h we have `delete m_Base;` which is not the same allocator as we used to allocate the memory.
This PR matches the malloc and free to be the correct calls.
2023-09-30 18:48:12 -05:00
1ec8da8bf7 fix: remove foreign key on name in cheat_detection table (#1202) 2023-09-30 06:22:32 -05:00
David Markowitz
b24775f472 feat: Security improvements for spoofed packets (#1201)
* Add cheat detection for spoofed packets

* Add config option for ip loggin

* remove packet saving
2023-09-28 12:16:11 -05:00
David Markowitz
bd65fc6e33 Fix version number (#1199)
Accidentally reverted the changes in a previous PR.
2023-09-25 08:23:23 -05:00
David Markowitz
44f466ac72 fix: deleting last character still shows the character (#1123)
* Fix deleting last character

Fix an issue where deleting your last character caused the character to still show up on the character selection screen.

Tested that deleting my final character results in an empty character selection scene.

* Update UserManager.cpp
2023-09-24 02:55:54 -05:00
David Markowitz
51540568fb Strengthen checks for slash commands (#1198) 2023-09-22 17:48:35 -07:00
08020cd86d chore: cleanup LU(W)string writing (#1188)
* chore: cleanup LU(W)string writing
and add methods for reading
remove redunent "packet" from packet reading helpers
move write header to bitstreamutils since it's not packet related
add tests for reading/writing LU(W)Strings

* remove un-needed function defintions in header

* make reading and writing more efficient

* p

p

* quotes

* remove unneeded default

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2023-09-20 20:06:28 -05:00
ca78a166d9 fix #1194 (#1196) 2023-09-19 05:59:02 -05:00
2e386d29df fix: Limit the number of players that can join a team (#1187)
* Limit the number of players that can join a team
after being invited

* Notify player if they weren't added to the team

* check pointer
2023-08-18 18:58:27 -05:00
d893ecddeb Renamed RocketLaunchLUP to MultiZoneEntrance (#1185) 2023-08-18 14:14:40 -05:00
David Markowitz
f4f13e081a Update Commands.md (#1184)
If this fails to build im going to be nice to people
2023-08-13 17:05:04 -07:00
David Markowitz
c26086aff5 perf: Add path height correction on world load (#1178)
* Add path height correction on world load

* Increase height because Nexus Tower is tall

* Update height checker

- Only go up and down, do not deviate from the point you are on
- As a backup, use the nearestPoint on the nearestPoly, should detour be able to find one.
- Add a debug assert to fail the program should toReturn differ from nearestPoint[1].

Update dNavMesh.cpp

Update dNavMesh.cpp

* Fix if condition to actually return the value...

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-08-12 09:20:00 -05:00
Gie "Max" Vanommeslaeghe
0337449aa7 Merge pull request #1183 from DarkflameUniverse/fastcrashfixes
fix: dangling pointer in mission when saving character data
2023-08-12 12:21:14 +02:00
David Markowitz
598f4e1663 Fix dangling pointer 2023-08-12 02:11:25 -07:00
David Markowitz
5eca25e42a refactor: Move CDClient Database away from constructor queries (#1164)
* Move away from constructor queries

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

* Update CDBehaviorParameterTable.cpp
2023-08-10 23:27:40 -05:00
Gie "Max" Vanommeslaeghe
d233c7e2aa Merge pull request #1182 from DarkflameUniverse/fixRawReading
fix: RAW to OBJ parsing
2023-08-10 23:50:11 +02:00
beaceb947b fix: Properly read in scene metadata (#1170)
* fix: Properly read in scene metadata

* Fix reading in rental time and period from property path

* remove useless name in var
change hex to decimal so it's readable
fix scene transistion loop logic

* fix typoe and remove whitespace
2023-08-10 16:35:12 -05:00
David Markowitz
2cc13c6499 chore: Make serialize actually virtual (#1156)
* Make serialize actually virtual

* fix serialize and make update virutal

* Update VendorComponent.h

* Remove flag var

* Update SoundTriggerComponent.h

---------

Co-authored-by: Aaron Kimbrell <aronwk.aaron@gmail.com>
2023-08-10 14:33:15 -07:00
David Markowitz
ad81e341da add operator *= for vectors 2023-08-09 22:13:20 -07:00
David Markowitz
fe6be21008 fix raw reading 2023-08-09 22:08:32 -07:00
cefdfc696a fix: Implement proper Sound trigger component serialization (#1160)
* cleanup

* more cleanup and fully implement the sound trigger
and racing sound trigger

* more cleanup, and better defaults

* fixes and tested

* update initializor for guid and when to load sound guids

* make racing sound trigger it's own component

* fix type

* Remove global
move serializes

---------

Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-08-06 15:38:12 -05:00
7e2747a2d2 fix: load fdbchecksum to verify, even on character selection (world 0) (#1176) 2023-08-04 17:48:45 -05:00
a29253d2f0 feature: Donation Vendor Component (#1168)
* refactor: Vendor inventory loading
Implement proper delta compression
dynamically determine multicostitems and standard cost items
Quatantine max's custom code

* WIP

* progress missions

* address feedback

* fix newline

* Cleanup

* oops

* fix default for nexus tower jawbox
cleanup some logs

* remove log

* remove include that got added back
2023-08-03 21:44:03 -05:00
David Markowitz
e5b69745aa feat: Remove NET_VERSION define (#1153)
* Remove NET_VERSION define

I get to test this on 4 different platforms.  yay...

* move stuff around

finally fixed you docker

i love this

Update Dockerfile

Update Dockerfile

change type

Update AuthPackets.cpp

Update AuthServer.cpp

* Update CMakeVariables.txt

* Update sharedconfig.ini
2023-08-03 21:38:33 -05:00
David Markowitz
040a78c386 fix: Remove usage of rand() and seed randomness for chatserver (#1152)
* Remove usage of rand(), time(0) to time(NULL)

Replace it with the random engine mt19937.
convert time(0) to time(NULL)

* update

* revert a bunch of changes
2023-08-03 21:38:19 -05:00
David Markowitz
d8a5fd49a4 chore: Small movementAiComponent cleanup (#1145)
* rename and cleanup file

* more

* fix broken function

* Further naming fixes

t

Revert "Further naming fixes"

This reverts commit 057189982ba56788d48f9265d815e6c562ba6328.

* next step

* undo all testing changes

* minor tweaks
2023-08-03 21:38:04 -05:00
David Markowitz
208ed02158 Fix incorrect kick out of rebuilds (#1120) 2023-08-03 21:37:27 -05:00
952786c166 chore: cleanup some vanity npc logic (#1174)
* Dont setup talk for vanity npc's if they are no phrases
no ghosting ldfkey for canity npc's

* void, oops

* formatting
2023-08-03 21:37:09 -05:00
David Markowitz
9a07020a51 perf: Behavior Parameter memory savings (#1166)
* Memory Savings Behavior Parameter table

* Update CDBehaviorParameterTable.h
2023-08-03 19:31:02 -07:00
a22ecf385f feature: add full known GM list (#1171)
* feature: add full GM list

* Fix compiling

* add tests
Fix more issues
2023-08-03 21:27:40 -05:00
c2b4aa4026 refactor: Vendor inventory loading (#1163)
* refactor: Vendor inventory loading
Implement proper delta compression
dynamically determine multicostitems and standard cost items
Quatantine max's custom code

* address feedback

* fix newline

* oops

* remove header

* fix default and const for

* he said make it a reference too, not just const
2023-08-03 05:51:52 -05:00
David Markowitz
0d48cfe8c0 fix: Nipoint3 Operator+= (#1172)
* fix operator +=

* fix operator +=
2023-07-31 00:22:56 -07:00
e299bf9b62 fix: read and store the zone transition width properly (#1169) 2023-07-30 23:49:43 -07:00
Daniel Seiler
304af7922a Update MariaDB for windows (#792)
* Update CMakeMariaDBLists.txt

* Update CMakeMariaDBLists.txt

---------

Co-authored-by: EmosewaMC <39972741+EmosewaMC@users.noreply.github.com>
Co-authored-by: David Markowitz <EmosewaMC@gmail.com>
2023-07-23 20:51:13 -05:00
TAHuntling
0610fe99f8 fix: Nexus Tower Combat Challenge can be started twice if two players start it at the same time (#1147)
* Nexus Tower Dummy Challenge Dual Instance Fix

Fix for dual instance starting when two players hit start on the challenge at the same time.

* Changed GetVar to GetNetworkVar

Changed the if statement to GetNetworkVar boolean of bInUse rather than GetVar boolean of bInUse
2023-07-23 16:04:45 -07:00
Gie "Max" Vanommeslaeghe
d1316cfc9f Merge pull request #1107 from EmosewaMC/first-draft-leaderboard-re-write
feat: Leaderboards
2023-07-24 00:29:17 +02:00
bd5ead40f6 feat: upgrade session keys to use mersenne twister (#1155)
* upgrade session keys to use mersenne twister

* arithmetic type static assert and windows min/max macro undef
2023-07-23 14:59:43 -07:00
David Markowitz
c791d1a237 feature: Auto update of ini files (#1154)
* Add auto update of ini files

Tested that config options that currently exist are not modified.

Tested that if the exact variable name is not located in the destination ini, the config option is added along with all of its corresponding comments.

Comments in the build files are ignored to prevent any possible name collision with comments.

* Fix typos and empty file issue
2023-07-23 12:09:07 -07:00
TAHuntling
54454973a1 Distance for pet digs increased slightly to help with navmesh issues (#1151) 2023-07-22 02:49:23 -07:00
David Markowitz
b87537c637 bump minor version 2023-07-22 02:16:53 -07:00
David Markowitz
a625d2eae5 Update SGCannon.cpp 2023-07-21 23:42:50 -07:00
David Markowitz
887e2a25f0 Fix race score bugs
num wins serialized properly
scores are saved and num wins and times played are incremented properly
2023-07-21 23:18:51 -07:00
David Markowitz
34392932b3 Merge branch 'main' into pr/1107 2023-07-21 19:40:30 -07:00
David Markowitz
cc251d0986 Update ActivityManager.cpp 2023-07-21 19:39:44 -07:00
David Markowitz
0c0f7216a1 Merge branch 'main' into pr/1107 2023-07-21 19:38:59 -07:00
TAHuntling
342da56678 Added Player Rewards for Solo Racing (#1150)
* Added Player Rewards for Solo Racing

* Fixed Dual PlayersRating multiplication

* Checking for solo player fixed

* Another change to fix issues
2023-07-21 19:37:31 -05:00
TAHuntling
11b0097488 freemoney type update (#1146)
Changed freemoney command to parse 64 bit integers as opposed to 32 bit
2023-07-19 09:07:41 -07:00
David Markowitz
3e3148e910 Move dZoneManager to game namespace (#1143)
* convert zone manager to game namespace

* Destroy logger last
2023-07-17 17:55:33 -05:00
David Markowitz
080a833144 Convert BrickDatabase to namespace (#1142)
* Convert BrickDatabase to namespace

This did not need to be a class.

* Fix linker errors

* convert to anonymous namespace

so the cache is unmodifiable outside the brickcache

* Move to lower scope level and remove log
2023-07-17 17:55:25 -05:00
David Markowitz
455f9470a5 Move EntityManager to Game namespace (#1140)
* Move EntityManager to Game namespace

* move initialization to later

Need to wait for dZoneManager to be initialized.

* Fix bugs

- Cannot delete from a RandomAccessIterator while in a range based for loop.

Touchup zone manager initialize

replace magic numbers with better named constants
replace magic zonecontrol id with a more readable hex alternative
condense stack variables
move initializers closer to their use
initialize entity manager with zone control

change initialize timings

If zone is not zero we expect to initialize the entity manager during zone manager initialization

Add constexpr for zone control LOT

* Add proper error handling

* revert vanity changes

* Update WorldServer.cpp

* Update dZoneManager.cpp
2023-07-15 13:56:33 -07:00
9375c36c7b fix: remove hardcoded rotations now that vehicles orient correctly (#1132) 2023-07-03 11:58:49 -05:00
Raine
5cc7479f29 Update credits (#1133) 2023-07-01 15:40:34 -07:00
David Markowitz
c4135eac46 Revert playerflags functions to uint instead of int (#1130)
Prevents issue with negative numbers resulting in bugs.
2023-06-26 00:51:28 -07:00
41898bef86 foot race player flag fix (#1125)
and include fixes
2023-06-23 08:50:15 -05:00
David Markowitz
787dac7cd9 Update Dockerfile (#1126)
Fixes #1124
2023-06-23 08:49:58 -05:00
David Markowitz
e32cec6691 Merge branch 'first-draft-leaderboard-re-write' of https://github.com/EmosewaMC/DarkflameServer into first-draft-leaderboard-re-write 2023-06-21 21:53:23 -07:00
David Markowitz
85aa82b4b9 cleanup 2023-06-21 21:53:10 -07:00
David Markowitz
393733cc67 Update GameMessageHandler.cpp 2023-06-21 21:50:54 -07:00
David Markowitz
8156e5cc91 Update MigrationRunner.cpp 2023-06-21 21:50:16 -07:00
David Markowitz
d0e79d19fc Update Metrics.hpp 2023-06-21 21:48:58 -07:00
David Markowitz
45256a0338 Merge remote-tracking branch 'upstream/main' into first-draft-leaderboard-re-write 2023-06-21 21:48:22 -07:00
David Markowitz
238fc98ea5 Fix shooting gallery leaderboard bugs
- add weekly functionality for top scores
- Fix shooting gallery score saving
- remove extra leaderboard fetch
2023-06-21 21:46:11 -07:00
David Markowitz
d340874284 more bug fixing
- fix weekly leaderboards
- fix ag classic vs dlu scoring
- fix sorting for survival ns
- fix sorting for racing
2023-06-21 19:46:01 -07:00
David Markowitz
fe23c7c5f7 Allow default scripts (#1117)
Fix an issue where vanity script overwrote always
2023-06-20 10:40:16 -05:00
David Markowitz
132d31d3ab Fix vehicle serialization during races (#1122)
* Fix vehicle serialization during races

- Add missing frame stats reading
- correct the inversion of rotation
- correct serialization order
- use proper dirty flags

Tested that racers are no longer sideways on certain vertical slopes and stay in sync throughout the whole race.

* Update ClientPackets.cpp

* Update ClientPackets.cpp

* Update VehiclePhysicsComponent.h
2023-06-20 09:19:21 -05:00
David Markowitz
2d31b7e4bb Fix incorrect serialization of SendTeleport (#1121)
* Fix incorrect serialization of SendTeleport

- Fixes all incorrect teleports in the game
- remove hacks in mast teleport
- ...
- ......

Update GameMessages.cpp

* Remove stupid argument

there got it all out

* remove extra true
2023-06-18 00:00:36 -07:00
David Markowitz
f46bc33dd4 Fix prereq bug (#1118) 2023-06-17 19:20:05 -05:00
David Markowitz
0d6bd33f9e Remove unused problematic code (#1115) 2023-06-16 23:30:28 -05:00
1a74c028c2 fix: make vanity npc's use default equipment if none is specified (#1116) 2023-06-16 16:09:46 -05:00
David Markowitz
2a0f63c0a1 Fix all smashables not playing animations (#1112)
Fixes an issue where most smashables did not explode into bricks upon death.  This included anything that was spawned or didnt have the flag is_smashable set.
Tested that in races, all objects smash into bricks
Tested that the player properly explodes in their car if they crash
Tested that Shooting Gallery plays the special smash animation when a ship is smashed
Tested that all spawned objects play smash animations

* Fix warning, Fix modular assembly not smashing

* Rename variable to correct name
2023-06-14 15:44:22 -07:00
David Markowitz
12d7ab9034 Remove null check in GetPosition (#1109)
Get ready for null pointer errors
2023-06-06 22:48:41 -07:00
EmosewaMC
c99e2a372b Add weekly filter 2023-06-05 16:04:56 -07:00
EmosewaMC
dab075fc39 forgor about this one 2023-06-05 15:19:52 -07:00
EmosewaMC
b4aa5db305 Comment out tests
rest in pepperoni tests
2023-06-05 15:10:08 -07:00
David Markowitz
259f0c8371 Working in game again
hooray
2023-06-05 04:10:59 -07:00
David Markowitz
a5e63529dc const and compile save 2023-06-05 02:50:40 -07:00
David Markowitz
c572f2a58d better tabs and organization 2023-06-05 02:43:02 -07:00
David Markowitz
5bff441c01 Fix query crashing
Just select all columns since we need most of them anyways
2023-06-05 02:31:49 -07:00
David Markowitz
96fc6e81d8 Update sql to work
The old way was supposed to work but doesn't.  Oh well!
2023-06-05 02:24:00 -07:00
David Markowitz
59d7121978 Merge remote-tracking branch 'upstream/main' into first-draft-leaderboard-re-write 2023-06-05 02:04:14 -07:00
David Markowitz
b589755655 Fix out of bounds access in dpGrid (#1106)
Fixes an issue where we would try to access an array out of the physics bounds
2023-06-03 16:28:27 -07:00
David Markowitz
8ae1e1bc6b Fix: remove ability to buy items from a vendor if they don't sell said item (#1105) 2023-06-03 00:40:46 -07:00
David Markowitz
9fabff16e4 Update AMFDeserialize (#1096)
Per ISO C++ standard 9.7.1 5.3,
"Otherwise the type of the enumerator is the same as that of the preceding enumerator unless the
incremented value is not representable in that type, in which case the type is an unspecified integral
type sufficient to contain the incremented value. If no such type exists, the program is ill-formed."
it is not undefined behavior to set a scoped enum to a value outside of its constant range because all values of the underlying type can represent the scoped enum
2023-06-02 06:44:49 -05:00
EmosewaMC
8267823ca4 More simplification 2023-05-31 23:17:13 -07:00
EmosewaMC
b8878da61b Convert to using only floats
This will cover all of our bases for any type of score.  No need to do any conversions.
2023-05-31 23:05:19 -07:00
EmosewaMC
47deca6f4f Update migration
Better column names and maintainability
Better updating
Convert all data to floats
2023-05-31 23:04:33 -07:00
David Markowitz
8a1f275176 add a const 2023-05-31 03:10:28 -07:00
David Markowitz
e1a7b4993e look ma, more work 2023-05-30 18:21:10 -07:00
David Markowitz
0ecc5d83c3 Fix out of bounds access
Fixes an issue where we would try to access an array out of the physics bounds
2023-05-30 17:30:50 -07:00
EmosewaMC
a43e03255c It compiles at least now 2023-05-30 04:38:19 -07:00
EmosewaMC
83da45575e CONST 2023-05-30 04:28:50 -07:00
EmosewaMC
f5f599764d more const and views 2023-05-30 04:23:48 -07:00
EmosewaMC
7b1626901a Add some debug asserts 2023-05-30 04:18:32 -07:00
EmosewaMC
a595347211 Merge remote-tracking branch 'upstream/main' into first-draft-leaderboard-re-write 2023-05-30 04:11:53 -07:00
EmosewaMC
0107d05d55 draft 2023-05-30 04:11:37 -07:00
David Markowitz
f26e66da4d small updates to typs 2023-05-28 04:30:20 -07:00
David Markowitz
e47169fec5 Fix: Some platforms not using the same RNG for every roll (#1103)
* Test changes

* Update ObjectIDManager.h

* Revert "Update ObjectIDManager.h"

This reverts commit 3e4d169718.

* Revert "Test changes"

This reverts commit 8e16573f93.

* Use random engine
2023-05-26 23:22:31 -05:00
David Markowitz
238751f14e Remove extra cout (#1101) 2023-05-25 15:29:46 -05:00
59387e5fe3 fix: update type in am blue x script (#1095)
Fixes #1094
2023-05-17 13:17:34 -05:00
Gie "Max" Vanommeslaeghe
f9b52ad01c Merge pull request #1025 from EmosewaMC/more-cdclient-cleanup
Implement animation table
2023-05-14 15:08:18 +02:00
David Markowitz
1f3df08730 Update GameMessages.cpp 2023-05-13 16:22:13 -07:00
9708ea28dc refactor: removed hardcoded ag laser logic (#1079)
* Removed hardcoded laser logic

* Address feedback
2023-05-13 18:21:17 -05:00
David Markowitz
8a065ad074 Merge remote-tracking branch 'upstream/main' into more-cdclient-cleanup 2023-05-13 16:16:58 -07:00
Jett
2117a18d62 Enable artifact uploading and replace upload parameters (#1091) 2023-05-13 17:23:09 -05:00
David Markowitz
4fe335cc66 Refactor: Amf3 implementation (#998)
* Update AMFDeserializeTests.cpp

Redo Amf3 functionality

Overhaul the whole thing due to it being outdated and clunky to use

Sometimes you want to keep the value

Update AMFDeserializeTests.cpp

* Fix enum and constructors

Correct enum to a class and simplify names.
Add a proper default constructor

* Update MasterServer.cpp

* Fix bugs and add more tests

* Refactor: AMF with templates in mind

- Remove hard coded bodge
- Use templates and generics to allow for much looser typing and strengthened implementation
- Move code into header only implementation for portability

Refactor: Convert AMF implementation to templates

- Rip out previous implementation
- Remove all extraneous terminology
- Add proper overloads for all types of inserts
- Fix up tests and codebase

* Fix compiler errors

* Check for null first

* Add specialization for const char*

* Update tests for new template specialization

* Switch BitStream to use references

* Rename files

* Check enum bounds on deserialize

I did this on a phone
2023-05-13 17:22:00 -05:00
9d105a287d fix: not everything attached to a path is a moving platform (#1090) 2023-05-13 13:47:28 -07:00
1af70161eb fix: orient player correctly when using pirate mast in FV (#1087)
* fix: frient player correctly when using pirate mast in FV

* only get mast name once
2023-05-13 09:31:13 -05:00
739eae5244 feature: Implement FallSpeedBehavior (#1084)
* Hacky FallSpeedBehavior

* Fixup

* Make it more robust like speedboost
add check for default
Fix error in GetActiveSpeedboosts

* simplify and address feedback
2023-05-13 09:30:59 -05:00
Gie "Max" Vanommeslaeghe
07803a7ca2 Merge pull request #1085 from DarkflameUniverse/issue-436
fix: not exiting shooting gallery when clicking activity close button
2023-05-13 14:11:16 +02:00
Gie "Max" Vanommeslaeghe
61ae619886 Merge pull request #1032 from EmosewaMC/FixWingreaper
Fix Wingreaper birds not moving
2023-05-13 14:04:37 +02:00
Gie "Max" Vanommeslaeghe
8a8006bee5 Merge pull request #1088 from EmosewaMC/movingPlatformsFix
Fix deserialization errors for MovingPlatforms
2023-05-13 14:03:24 +02:00
David Markowitz
c5afd7d4a3 Fix deserialization errors for MovingPlatforms
- Fixes deserialization errors for MovingPlatforms that did not have an attached_path, but had a MovingPlatform component >= id 0.
2023-05-13 04:04:15 -07:00
a809f36548 Address feedback 2023-05-11 09:23:48 -05:00
0e01948414 Define comp 103 as Gate Rush Control comp (#1078)
Fix typo in 115
2023-05-11 06:54:41 -05:00
6e6a05fc1d fix: prevent negative imagination (#1083)
* fix: prevent negative imagination
And fail switch if we don't have enough imagination

* Make better
2023-05-11 06:37:02 -05:00
5af5b0f1c1 fix: not exiting shooting gallery when clicking activity close button
Fixes #436
Fixes crash when replaying as well
2023-05-10 19:26:04 -05:00
bf0ae6f181 fix: add check for arg nums on handlepushobject (#1081) 2023-05-10 07:44:21 -05:00
b1cd2776fa fix: make exiting the race work (#1082) 2023-05-10 04:05:56 -05:00
David Markowitz
af1abe9e74 Use only ints 2023-05-10 01:32:55 -07:00
David Markowitz
4dba8d9225 Correct column order 2023-05-09 22:21:41 -07:00
David Markowitz
3448426caf commit 2023-05-09 22:00:13 -07:00
David Markowitz
7a067e7b48 Working in game!
- Add score saving for races
- Fix some bugs
- Still work to do.
2023-05-09 01:42:11 -07:00
David Markowitz
411c9eaf1f Remove dead code 2023-05-09 00:06:43 -07:00
David Markowitz
fcf5c5ea8a Resolve compiler errors 2023-05-09 00:06:26 -07:00
EmosewaMC
6c2312fe87 Implement page fetching 2023-05-08 19:59:10 -07:00
EmosewaMC
d98ad4b94f Implement leaderboard page offsets 2023-05-08 19:35:19 -07:00
EmosewaMC
5c086909ed Ready for implementation 2023-05-08 18:36:28 -07:00
David Markowitz
1c7ac93d4b Fix various bugs and make code cleaner.
Still have work to go.
2023-05-08 03:55:10 -07:00
David Markowitz
0f307ac4be Fix bugs
- Reinforce Query formatting
- Guarantee 11 rows are selected at a time by ranking by id, should there be more than an 11 way tie.
2023-05-08 02:46:55 -07:00
David Markowitz
bc518be654 Bug fixes and cleanup
Fix co-pilot induced column bugs
Fix insert/update statements
Added saving functionality
Added update clause for column
2023-05-07 04:09:10 -07:00
EmosewaMC
820b375c50 push 2023-05-07 00:31:38 -07:00
David Markowitz
3b8f18d2be Fix new lines 2023-05-05 21:33:30 -07:00
EmosewaMC
0faef7d791 Finished saving 2023-05-04 16:53:36 -07:00
EmosewaMC
28a0492201 Fix bugs 2023-05-04 14:48:26 -07:00
EmosewaMC
2ab995b9c3 Simplify snprintfs
so much better to read
2023-05-04 14:28:53 -07:00
EmosewaMC
8de528e77c Remove old code 2023-05-04 13:58:48 -07:00
David Markowitz
a3626a3b53 The query is supposed to get SMALLER
Still better than 9 different queries all with 1 minor change i guess.
2023-05-03 00:38:38 -07:00
David Markowitz
b5e3bd22e3 Merge remote-tracking branch 'upstream/main' into first-draft-leaderboard-re-write 2023-05-02 15:49:23 -07:00
EmosewaMC
48264e2cf4 push 2023-04-30 21:30:41 -07:00
David Markowitz
10a2c24d5e Add base MyStandings query 2023-04-26 02:10:57 -07:00
EmosewaMC
89f427ace0 Further work on leaderboards 2023-04-18 01:26:35 -07:00
EmosewaMC
08220d6852 Remove extra includes 2023-04-17 15:22:44 -07:00
EmosewaMC
42870028e4 Remove complex migration
Only shooting gallery had a value put in the wrong column.  All others were either already correct or unimplemented.
2023-04-17 15:19:13 -07:00
David Markowitz
fcb088d263 Further work on leaderboards
- Modularize tests
- Add migrations
- Fix switch case so it actually breaks
- Add in missing writes
- Beginning work on custom migration to move the leaderboard to the final state
2023-04-17 01:16:23 -07:00
EmosewaMC
ed2639ce4e Use inline functions 2023-04-14 01:32:52 -07:00
David Markowitz
41355cea58 Add remaining enum types 2023-04-13 21:55:09 -07:00
David Markowitz
c91f0d16b3 Get it compiling, add a test and optimize heavily 2023-04-13 00:45:03 -07:00
EmosewaMC
c02963013b first draft 2023-04-12 21:57:58 -07:00
David Markowitz
308d56968a Use epsilon comparison 2023-04-11 22:25:02 -07:00
David Markowitz
47445ada54 Fix Wingreaper birds not moving
Fix an issue where the Wingreaper birds no longer moved.  The client seems to do the following:
Default speed set to 10.0f
Check the PhysicsComponent table for the column speed and if it exists set speed to that value and if the value was null set it to the default again.
2023-03-27 01:13:34 -07:00
David Markowitz
426bc963fe Add Animation Table logic 2023-03-26 05:18:45 -07:00
David Markowitz
1e4e1b914c Merge remote-tracking branch 'upstream/main' into more-cdclient-cleanup 2023-03-26 03:00:21 -07:00
David Markowitz
b432a3f5da Remove inlines
Clean up macros

more tomorrow

Cleanup and optimize CDActivities table

Remove unused include

Further work on CDActivityRewards

Update MasterServer.cpp

Further animations work

Activities still needs work for a better PK.

fix type

All of these replacements worked

Create internal interface for animations

Allows for user to just call GetAnimationTIme or PlayAnimation rather than passing in arbitrary true false statements
2023-03-26 02:59:46 -07:00
1057 changed files with 32328 additions and 25121 deletions

View File

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

View File

@@ -1,9 +1,7 @@
# Full path to the LEGO Universe client # Full path to the LEGO Universe client
CLIENT_PATH=/Users/someuser/LEGO Universe CLIENT_PATH=./client
# Can improve build time
BUILD_THREADS=1
# Updates NET_VERSION in CMakeVariables.txt # Updates NET_VERSION in CMakeVariables.txt
BUILD_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/ # grab a "SHA 256-bit Key" from here: https://keygen.io/
ACCOUNT_MANAGER_SECRET= ACCOUNT_MANAGER_SECRET=
@@ -12,6 +10,5 @@ EXTERNAL_IP=localhost
# 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=SECRET_VALUE_CHANGE_ME MARIADB_PASSWORD=
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME MARIADB_DATABASE=darkflame
MARIADB_DATABASE=darkflame

View File

@@ -0,0 +1,56 @@
name: CI
on:
push:
branches:
- "main"
tags:
- "v*.*.*"
pull_request:
branches:
- "main"
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push-image:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# generate Docker tags based on the following events/attributes
tags: |
type=ref,event=pr
type=raw,value=latest,enable=${{ github.ref == format('refs/heads/{0}', 'main') }}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@@ -13,7 +13,7 @@ jobs:
continue-on-error: true continue-on-error: true
strategy: strategy:
matrix: matrix:
os: [ windows-2022, ubuntu-20.04, macos-11 ] os: [ windows-2022, ubuntu-22.04, macos-13 ]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -25,9 +25,11 @@ jobs:
with: with:
vs-version: '[17,18)' vs-version: '[17,18)'
msbuild-architecture: x64 msbuild-architecture: x64
- name: Install libssl (Mac Only) - name: Install libssl and switch to XCode 15.2 (Mac Only)
if: ${{ matrix.os == 'macos-11' }} if: ${{ matrix.os == 'macos-13' }}
run: brew install openssl@3 run: |
brew install openssl@3
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
- name: cmake - name: cmake
uses: lukka/run-cmake@v10 uses: lukka/run-cmake@v10
with: with:
@@ -36,22 +38,16 @@ jobs:
testPreset: "ci-${{matrix.os}}" testPreset: "ci-${{matrix.os}}"
- name: artifacts - name: artifacts
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
if: ${{ github.ref == 'ref/head/main' }}
with: with:
name: build-${{matrix.os}} name: build-${{matrix.os}}
path: | path: |
build build/*Server*
!build/tests build/*.ini
!build/Testing build/*.so
!build/CMakeFiles build/*.dll
!build/DartConfiguration.tcl build/vanity/
!build/CTestTestfile.cmake build/navmeshes/
!build/CMakeCache.txt build/migrations/
!build/build.ninja build/*.dcf
!build/_deps !build/*.pdb
!build/cmake_install.cmake !build/d*/
!build/*.a
!build/*.lib
!build/*.dir
!build/*.vcxproj
!build/*.vcxproj.filters

1
.gitignore vendored
View File

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

6
.gitmodules vendored
View File

@@ -14,6 +14,6 @@
path = thirdparty/mariadb-connector-cpp path = thirdparty/mariadb-connector-cpp
url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git url = https://github.com/mariadb-corporation/mariadb-connector-cpp.git
ignore = dirty ignore = dirty
[submodule "thirdparty/AccountManager"] [submodule "thirdparty/magic_enum"]
path = thirdparty/AccountManager path = thirdparty/magic_enum
url = https://github.com/DarkflameUniverse/AccountManager url = https://github.com/Neargye/magic_enum.git

View File

@@ -1,8 +1,19 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.25)
project(Darkflame) project(Darkflame)
# check if the path to the source directory contains a space
if("${CMAKE_SOURCE_DIR}" MATCHES " ")
message(FATAL_ERROR "The server cannot build in the path (" ${CMAKE_SOURCE_DIR} ") because it contains a space. Please move the server to a path without spaces.")
endif()
include(CTest) include(CTest)
set (CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Export the compile commands for debugging
set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Set CMAKE visibility policy to NEW on project and subprojects
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON) # Set C and C++ symbol visibility to hide inlined functions
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Read variables from file # Read variables from file
FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables) FILE(READ "${CMAKE_SOURCE_DIR}/CMakeVariables.txt" variables)
@@ -14,30 +25,26 @@ string(REPLACE "\n" ";" variables ${variables})
foreach(variable ${variables}) foreach(variable ${variables})
# If the string contains a #, skip it # If the string contains a #, skip it
if(NOT "${variable}" MATCHES "#") if(NOT "${variable}" MATCHES "#")
# Split the variable into name and value # Split the variable into name and value
string(REPLACE "=" ";" variable ${variable}) string(REPLACE "=" ";" variable ${variable})
# Check that the length of the variable is 2 (name and value) # Check that the length of the variable is 2 (name and value)
list(LENGTH variable length) list(LENGTH variable length)
if(${length} EQUAL 2)
if(${length} EQUAL 2)
list(GET variable 0 variable_name) list(GET variable 0 variable_name)
list(GET variable 1 variable_value) list(GET variable 1 variable_value)
# Set the variable # Set the variable
set(${variable_name} ${variable_value}) set(${variable_name} ${variable_value})
# Add compiler definition
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D${variable_name}=${variable_value}")
message(STATUS "Variable: ${variable_name} = ${variable_value}") message(STATUS "Variable: ${variable_name} = ${variable_value}")
endif() endif()
endif() endif()
endforeach() endforeach()
# Set the version # Set the version
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") set(PROJECT_VERSION "\"${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}\"")
# Echo the version # Echo the version
message(STATUS "Version: ${PROJECT_VERSION}") message(STATUS "Version: ${PROJECT_VERSION}")
@@ -53,29 +60,34 @@ set(RECASTNAVIGATION_EXAMPLES OFF CACHE BOOL "" FORCE)
# Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent. # Disabled misleading indentation as DL_LinkedList from RakNet has a weird indent.
# 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.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPROJECT_VERSION=${PROJECT_VERSION}")
if(UNIX) if(UNIX)
if(APPLE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -Wuninitialized -fPIC")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -fPIC") add_compile_definitions(_GLIBCXX_USE_CXX11_ABI=0 _GLIBCXX_USE_CXX17_ABI=0)
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++17 -O2 -Wuninitialized -D_GLIBCXX_USE_CXX11_ABI=0 -D_GLIBCXX_USE_CXX17_ABI=0 -static-libgcc -fPIC -lstdc++fs") if(NOT APPLE)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -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") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -rdynamic")
endif() endif()
if (__ggdb)
if(${GGDB})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ggdb")
endif() endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -O2 -fPIC") 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
add_compile_options("/wd4267" "/utf-8") # Also disable non-portable MSVC volatile behavior
add_compile_options("/wd4267" "/utf-8" "/volatile:iso")
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_BINARY_DIR ${PROJECT_BINARY_DIR})
#set(CMAKE_INCLUDE_CURRENT_DIR_IN_INTERFACE ON) # unfortunately, forces all libraries to be built in series, which will slow down the build process
# TODO make this not have to override the build type directories # TODO make this not have to override the build type directories
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR})
@@ -89,6 +101,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
find_package(MariaDB)
# Create a /resServer directory # Create a /resServer directory
make_directory(${CMAKE_BINARY_DIR}/resServer) make_directory(${CMAKE_BINARY_DIR}/resServer)
@@ -96,30 +110,90 @@ make_directory(${CMAKE_BINARY_DIR}/resServer)
make_directory(${CMAKE_BINARY_DIR}/logs) make_directory(${CMAKE_BINARY_DIR}/logs)
# 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" "blacklist.dcf") set(RESOURCE_FILES "sharedconfig.ini" "authconfig.ini" "chatconfig.ini" "worldconfig.ini" "masterconfig.ini" "blocklist.dcf")
message(STATUS "Checking resource file integrity")
include(Utils)
UpdateConfigOption(${PROJECT_BINARY_DIR}/authconfig.ini "port" "auth_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/chatconfig.ini "port" "chat_server_port")
UpdateConfigOption(${PROJECT_BINARY_DIR}/masterconfig.ini "port" "master_server_port")
foreach(resource_file ${RESOURCE_FILES}) foreach(resource_file ${RESOURCE_FILES})
if (NOT EXISTS ${PROJECT_BINARY_DIR}/${resource_file}) set(file_size 0)
if(EXISTS ${PROJECT_BINARY_DIR}/${resource_file})
file(SIZE ${PROJECT_BINARY_DIR}/${resource_file} file_size)
endif()
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} ${PROJECT_BINARY_DIR}/${resource_file}
COPYONLY COPYONLY
) )
message("Moved ${resource_file} to project binary directory") message(STATUS "Moved " ${resource_file} " to project binary directory")
elseif(resource_file MATCHES ".ini")
message(STATUS "Checking " ${resource_file} " for missing config options")
file(READ ${PROJECT_BINARY_DIR}/${resource_file} current_file_contents)
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "")
# Remove comment lines so they do not interfere with the variable parsing
foreach(line ${current_file_contents})
string(FIND ${line} "#" is_comment)
if(NOT ${is_comment} EQUAL 0)
string(APPEND parsed_current_file_contents ${line})
endif()
endforeach()
file(READ ${CMAKE_SOURCE_DIR}/resources/${resource_file} depot_file_contents)
string(REPLACE "\\\n" "" depot_file_contents ${depot_file_contents})
string(REPLACE "\n" ";" depot_file_contents ${depot_file_contents})
set(line_to_add "")
foreach(line ${depot_file_contents})
string(FIND ${line} "#" is_comment)
if(NOT ${is_comment} EQUAL 0)
string(REPLACE "=" ";" line_split ${line})
list(GET line_split 0 variable_name)
if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
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})
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n" ${line_to_append})
endforeach()
file(APPEND ${PROJECT_BINARY_DIR}/${resource_file} "\n")
endif()
set(line_to_add "")
else()
set(line_to_add ${line_to_add} ${line})
endif()
endforeach()
endif() endif()
endforeach() endforeach()
# Copy navmesh data on first build and extract it message(STATUS "Resource file integrity check complete")
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) # if navmeshes directory does not exist, create it
file(REMOVE ${PROJECT_BINARY_DIR}/navmeshes.zip) if(NOT EXISTS ${PROJECT_BINARY_DIR}/navmeshes)
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/navmeshes)
endif() 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 # Copy vanity files on first build
set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "NPC.xml") set(VANITY_FILES "CREDITS.md" "INFO.md" "TESTAMENT.md" "root.xml" "dev-tribute.xml" "atm.xml" "demo.xml")
foreach(file ${VANITY_FILES}) foreach(file ${VANITY_FILES})
configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY) configure_file("${CMAKE_SOURCE_DIR}/vanity/${file}" "${CMAKE_BINARY_DIR}/vanity/${file}" COPYONLY)
endforeach() endforeach()
@@ -127,169 +201,61 @@ endforeach()
# Move our migrations for MasterServer to run # Move our migrations for MasterServer to run
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)
foreach(file ${SQL_FILES}) foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME) get_filename_component(file ${file} NAME)
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/dlu/${file}) configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file}
COPYONLY
)
endif()
endforeach() endforeach()
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/) file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql) file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)
foreach(file ${SQL_FILES}) foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME) get_filename_component(file ${file} NAME)
if (NOT EXISTS ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}) configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
configure_file(
${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file}
COPYONLY
)
endif()
endforeach() endforeach()
# Create our list of include directories
set(INCLUDED_DIRECTORIES
"dCommon"
"dCommon/dClient"
"dCommon/dEnums"
"dChatFilter"
"dGame"
"dGame/dBehaviors"
"dGame/dComponents"
"dGame/dGameMessages"
"dGame/dInventory"
"dGame/dMission"
"dGame/dEntity"
"dGame/dPropertyBehaviors"
"dGame/dPropertyBehaviors/ControlBehaviorMessages"
"dGame/dUtilities"
"dPhysics"
"dNavigation"
"dNavigation/dTerrain"
"dZoneManager"
"dDatabase"
"dDatabase/Tables"
"dNet"
"dScripts"
"dScripts/02_server"
"dScripts/ai"
"dScripts/client"
"dScripts/EquipmentScripts"
"dScripts/EquipmentTriggers"
"dScripts/zone"
"dScripts/02_server/DLU"
"dScripts/02_server/Enemy"
"dScripts/02_server/Equipment"
"dScripts/02_server/Map"
"dScripts/02_server/Minigame"
"dScripts/02_server/Objects"
"dScripts/02_server/Pets"
"dScripts/02_server/Enemy/AG"
"dScripts/02_server/Enemy/AM"
"dScripts/02_server/Enemy/FV"
"dScripts/02_server/Enemy/General"
"dScripts/02_server/Enemy/Survival"
"dScripts/02_server/Enemy/VE"
"dScripts/02_server/Enemy/Waves"
"dScripts/02_server/Map/AG"
"dScripts/02_server/Map/AG_Spider_Queen"
"dScripts/02_server/Map/AM"
"dScripts/02_server/Map/FV"
"dScripts/02_server/Map/General"
"dScripts/02_server/Map/GF"
"dScripts/02_server/Map/njhub"
"dScripts/02_server/Map/NS"
"dScripts/02_server/Map/NT"
"dScripts/02_server/Map/PR"
"dScripts/02_server/Map/Property"
"dScripts/02_server/Map/SS"
"dScripts/02_server/Map/VE"
"dScripts/02_server/Map/FV/Racing"
"dScripts/02_server/Map/General/Ninjago"
"dScripts/02_server/Map/njhub/boss_instance"
"dScripts/02_server/Map/NS/Waves"
"dScripts/02_server/Map/Property/AG_Med"
"dScripts/02_server/Map/Property/AG_Small"
"dScripts/02_server/Map/Property/NS_Med"
"dScripts/02_server/Minigame/General"
"dScripts/ai/ACT"
"dScripts/ai/AG"
"dScripts/ai/FV"
"dScripts/ai/GENERAL"
"dScripts/ai/GF"
"dScripts/ai/MINIGAME"
"dScripts/ai/NP"
"dScripts/ai/NS"
"dScripts/ai/PETS"
"dScripts/ai/PROPERTY"
"dScripts/ai/RACING"
"dScripts/ai/SPEC"
"dScripts/ai/WILD"
"dScripts/ai/ACT/FootRace"
"dScripts/ai/MINIGAME/SG_GF"
"dScripts/ai/MINIGAME/SG_GF/SERVER"
"dScripts/ai/NS/NS_PP_01"
"dScripts/ai/NS/WH"
"dScripts/ai/PROPERTY/AG"
"dScripts/ai/RACING/OBJECTS"
"dScripts/client/ai"
"dScripts/client/ai/PR"
"dScripts/zone/AG"
"dScripts/zone/LUPs"
"dScripts/zone/PROPERTY"
"dScripts/zone/PROPERTY/FV"
"dScripts/zone/PROPERTY/GF"
"dScripts/zone/PROPERTY/NS"
"thirdparty/raknet/Source"
"thirdparty/tinyxml2"
"thirdparty/recastnavigation"
"thirdparty/SQLite"
"thirdparty/cpplinq"
"tests"
"tests/dCommonTests"
"tests/dGameTests"
"tests/dGameTests/dComponentsTests"
)
# 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)
include_directories("/usr/local/include/") include_directories("/usr/local/include/")
endif() endif()
if (WIN32)
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include")
elseif (UNIX)
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt")
set(INCLUDED_DIRECTORIES ${INCLUDED_DIRECTORIES} "thirdparty/libbcrypt/include/bcrypt")
endif()
# Add binary directory as an include directory
include_directories(${PROJECT_BINARY_DIR})
# Actually include the directories from our list
foreach (dir ${INCLUDED_DIRECTORIES})
include_directories(${PROJECT_SOURCE_DIR}/${dir})
endforeach()
# Add linking directories:
link_directories(${PROJECT_BINARY_DIR})
# Load all of our third party directories # Load all of our third party directories
add_subdirectory(thirdparty) add_subdirectory(thirdparty SYSTEM)
# Glob together all headers that need to be precompiled # Create our list of include directories
file( include_directories(
GLOB HEADERS_DDATABASE "dPhysics"
LIST_DIRECTORIES false
${PROJECT_SOURCE_DIR}/dDatabase/*.h "dNavigation"
${PROJECT_SOURCE_DIR}/dDatabase/Tables/*.h
${PROJECT_SOURCE_DIR}/thirdparty/SQLite/*.h "dNet"
"tests"
"tests/dCommonTests"
"tests/dGameTests"
"tests/dGameTests/dComponentsTests"
SYSTEM "thirdparty/magic_enum/include/magic_enum"
SYSTEM "thirdparty/raknet/Source"
SYSTEM "thirdparty/tinyxml2"
SYSTEM "thirdparty/recastnavigation"
SYSTEM "thirdparty/SQLite"
SYSTEM "thirdparty/cpplinq"
SYSTEM "thirdparty/cpp-httplib"
SYSTEM "thirdparty/MD5"
) )
# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
# TODO: Should probably not do this.
if(APPLE)
include_directories("/usr/local/include/")
endif()
# Add linking directories:
if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
endif()
file( file(
GLOB HEADERS_DZONEMANAGER GLOB HEADERS_DZONEMANAGER
LIST_DIRECTORIES false LIST_DIRECTORIES false
@@ -321,15 +287,16 @@ add_subdirectory(dGame)
add_subdirectory(dZoneManager) add_subdirectory(dZoneManager)
add_subdirectory(dNavigation) add_subdirectory(dNavigation)
add_subdirectory(dPhysics) add_subdirectory(dPhysics)
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" "mariadbConnCpp") set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum")
# Add platform specific common libraries # Add platform specific common libraries
if (UNIX) if(UNIX)
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread") set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "dl" "pthread")
if (NOT APPLE AND __include_backtrace__) if(NOT APPLE AND ${INCLUDE_BACKTRACE})
set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace") set(COMMON_LIBRARIES ${COMMON_LIBRARIES} "backtrace")
endif() endif()
endif() endif()
@@ -340,23 +307,11 @@ add_subdirectory(dAuthServer)
add_subdirectory(dChatServer) add_subdirectory(dChatServer)
add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries add_subdirectory(dMasterServer) # Add MasterServer last so it can rely on the other binaries
# Add our precompiled headers
target_precompile_headers(
dGame PRIVATE
${HEADERS_DGAME}
)
target_precompile_headers( target_precompile_headers(
dZoneManager PRIVATE dZoneManager PRIVATE
${HEADERS_DZONEMANAGER} ${HEADERS_DZONEMANAGER}
) )
# Need to specify to use the CXX compiler language here or else we get errors including <string>.
target_precompile_headers(
dDatabase PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${HEADERS_DDATABASE}>"
)
target_precompile_headers( target_precompile_headers(
dCommon PRIVATE dCommon PRIVATE
${HEADERS_DCOMMON} ${HEADERS_DCOMMON}
@@ -367,6 +322,6 @@ target_precompile_headers(
"$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>" "$<$<COMPILE_LANGUAGE:CXX>:${PROJECT_SOURCE_DIR}/thirdparty/tinyxml2/tinyxml2.h>"
) )
if (${__enable_testing__} MATCHES "1") if(${ENABLE_TESTING})
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()

View File

@@ -14,13 +14,13 @@
"generator": "Unix Makefiles" "generator": "Unix Makefiles"
}, },
{ {
"name": "ci-ubuntu-20.04", "name": "ci-ubuntu-22.04",
"displayName": "CI configure step for Ubuntu", "displayName": "CI configure step for Ubuntu",
"description": "Same as default, Used in GitHub actions workflow", "description": "Same as default, Used in GitHub actions workflow",
"inherits": "default" "inherits": "default"
}, },
{ {
"name": "ci-macos-11", "name": "ci-macos-13",
"displayName": "CI configure step for MacOS", "displayName": "CI configure step for MacOS",
"description": "Same as default, Used in GitHub actions workflow", "description": "Same as default, Used in GitHub actions workflow",
"inherits": "default" "inherits": "default"
@@ -67,15 +67,15 @@
"jobs": 2 "jobs": 2
}, },
{ {
"name": "ci-ubuntu-20.04", "name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-20.04", "configurePreset": "ci-ubuntu-22.04",
"displayName": "Linux CI Build", "displayName": "Linux CI Build",
"description": "This preset is used by the CI build on linux", "description": "This preset is used by the CI build on linux",
"jobs": 2 "jobs": 2
}, },
{ {
"name": "ci-macos-11", "name": "ci-macos-13",
"configurePreset": "ci-macos-11", "configurePreset": "ci-macos-13",
"displayName": "MacOS CI Build", "displayName": "MacOS CI Build",
"description": "This preset is used by the CI build on MacOS", "description": "This preset is used by the CI build on MacOS",
"jobs": 2 "jobs": 2
@@ -83,8 +83,8 @@
], ],
"testPresets": [ "testPresets": [
{ {
"name": "ci-ubuntu-20.04", "name": "ci-ubuntu-22.04",
"configurePreset": "ci-ubuntu-20.04", "configurePreset": "ci-ubuntu-22.04",
"displayName": "CI Tests on Linux", "displayName": "CI Tests on Linux",
"description": "Runs all tests on a linux configuration", "description": "Runs all tests on a linux configuration",
"execution": { "execution": {
@@ -95,8 +95,8 @@
} }
}, },
{ {
"name": "ci-macos-11", "name": "ci-macos-13",
"configurePreset": "ci-macos-11", "configurePreset": "ci-macos-13",
"displayName": "CI Tests on MacOS", "displayName": "CI Tests on MacOS",
"description": "Runs all tests on a Mac configuration", "description": "Runs all tests on a Mac configuration",
"execution": { "execution": {

View File

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

View File

@@ -1,46 +0,0 @@
# Run the Darkflame Server inside Docker
## What you need
- [Docker](https://docs.docker.com/get-docker/) (Docker Desktop or on Linux normal Docker)
- [Docker Compose](https://docs.docker.com/compose/install/) (Included in Docker Desktop)
- LEGO® Universe Client. Check the main [README](./README.md) for details on this.
## Run server inside Docker
1. Copy `.env.example` and save it as `.env` inside the root directory of this repository
2. Edit the `.env` file and add your path to the root directory of your LEGO® Universe Client after `CLIENT_PATH=`
3. Update other values in the `.env` file as needed (be sure to update passwords!)
4. Run `docker compose up -d --build`
5. Run `docker compose exec darkflame /app/MasterServer -a` and setup your admin account
6. Follow the directions [here](https://github.com/DarkflameUniverse/AccountManager) to setup regular user accounts. The server will be accessible at: `http://<EXTERNAL_IP>:5000`
7. Now you can see the output of the server with `docker compose logs -f --tail 100` or `docker compose logs -f --tail 100`. This can help you understand issues and there you can also see when the server finishes it's startup.
8. You're ready to connect your client!
**NOTE #1**: If you're running an older version of Docker, you may need to use the command `docker-compose` instead of `docker compose`.
**NOTE #2**: To stop the server simply run `docker compose down` and to restart it just run `docker compose up -d` again. No need to run all the steps above every time.
**NOTE #3**: Docker buildkit needs to be enabled. https://docs.docker.com/develop/develop-images/build_enhancements/#to-enable-buildkit-builds
**NOTE #4**: Make sure to run the following in the repo root directory after cloning so submodules are also downloaded.
```
git submodule update --init --recursive
```
**NOTE #5**: If DarkflameSetup fails due to not having cdclient.fdb, rename CDClient.fdb (in the same folder) to cdclient.fdb
## Disable brickbuildfix
If you don't need the http server running on port 80 do this:
1. Create a file with the name `docker-compose.override.yml` in the root of the repository
2. Paste this content:
```yml
services:
brickbuildfix:
profiles:
- donotstart
```
3. Now run `docker compose up -d`

View File

@@ -1,58 +0,0 @@
# Installation under Windows
## First Run
1. Navigate to the [Docker download page](https://www.docker.com/products/docker-desktop) and download docker.
![Docker Download Page](docker/images/Docker_Download_Page.png)
2. Once the file has finished downloading, run it and proceed through the installation. Make sure, "Install required Windows components for WSL 2" is checked.
![Docker Desktop Installer Configuration](docker/images/Docker_Desktop_Installer_Configuration.png)
3. If necessary, restart your computer.
4. After the restart, Docker Desktop will automatically open. If it does not, simply start it like any other program.
5. If a window "WSL 2 Installation is incomplete." pops up, follow the link and click "WSL2 Linux kernel update package for x64 machines". Run the downloaded file and once that finishes, click "Restart" in the Docker Desktop window.
![WSL 2 download](docker/images/WSL_2_download.png)
6. Wait until Docker Desktop has started. You may skip the tutorial.
7. You may want to disable "Open Docker Dashboard at startup" in _Settings_ -> _General_
![Disable Dashboard Autostart](docker/images/DD_General_Settings.png)
8. Install [Git for Windows](https://git-scm.com/download/win). During the installation, simply confirming the defaults is sufficient.
9. In the folder you wish to save the Server, right click and select "Git Bash Here".
10. Type `git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer`
11. Once the command has completed (you can see you path again and can enter commands), close the window.
12. Inside the downloaded folder, copy `.env.example` and name the copy `.env`
13. Open `.env` with Notepad by right-clicking it and selecting _Open With_ -> _More apps_ -> _Notepad_.
14. Change the text after `CLIENT_PATH=` to the location of your client. This folder must contain either a folder `client` or `legouniverse.exe`.
> If you need the extra performance, place the client files in `\\wsl$\<your linux OS>\...` to avoid working across file systems, see [Docker Best Practices](https://docs.docker.com/desktop/windows/wsl/#best-practices) and [WSL documentation](https://docs.microsoft.com/en-us/windows/wsl/filesystems#file-storage-and-performance-across-file-systems).
15. Optionally, you can change the number after `BUILD_THREADS=` to the number of cores / threads your processor has. If your computer crashes while building, you can try to reduce this value.
16. After `ACCOUNT_MANAGER_SECRET=` paste a "SHA 256-bit Key" from https://keygen.io/
17. If you are not only hosting a local server, change the value after `EXTERNAL_IP=` to the external IP address of your computer.
18. Change the two values `SECRET_VALUE_CHANGE_ME` to passwords only you know. Save and close the file.
19. In the extracted folder hit Shift+Right Click and select "Open PowerShell window here".
![Open PowerShell](docker/images/Open_Powershell.png)
17. In the new window, paste (with right click) or type `docker compose up -d --build` and confirm with enter.
18. Once you see the blinking cursor and the path again, setup has finished and the server is already running.
![setup done](docker/images/setup_finished.png)
19. Create an admin account by pasting `docker compose exec darkflame /app/MasterServer -a` and following the prompts.
![admin account creation](docker/images/Account_Creation.png)
20. You can now login with these credentials at `http://your_ip:5000` (replace your_ip with your external IP). There you can create your account for playing as well as generate keys for other people to join; use these at `http://your_ip:5000/activate`
## Normal Use
1. In Docker Desktop you should now see an entry `darkflameserver-main` and when you click on it all containers but `DarkflameSetup` should eventually be green. That means the server is running.
![server running](docker/images/Docker_Compose_Finished.png)
2. For troubleshooting, you can check the logs of the various parts by clicking their entry.
3. You can start and stop the server with the corresponding buttons. Once all containers are grey, the server has shut down, and when all containers but `DarkflameSetup` are green, the server is running. Note that starting and stopping takes some time, please be patient.
![start stop buttons](docker/images/DD_Server_Startstop.png)

50
Dockerfile Normal file
View File

@@ -0,0 +1,50 @@
FROM gcc:12 as build
WORKDIR /app
RUN set -ex; \
apt-get update; \
apt-get install -y cmake
COPY . /app/
COPY --chmod=0500 ./build.sh /app/
RUN sed -i 's/MARIADB_CONNECTOR_COMPILE_JOBS__=.*/MARIADB_CONNECTOR_COMPILE_JOBS__=2/' /app/CMakeVariables.txt
RUN ./build.sh
FROM debian:12 as runtime
WORKDIR /app
RUN --mount=type=cache,id=build-apt-cache,target=/var/cache/apt \
apt update && \
apt install -y libssl3 libcurl4 && \
rm -rf /var/lib/apt/lists/*
# Grab libraries and load them
COPY --from=build /app/build/mariadbcpp/libmariadbcpp.so /usr/local/lib/
RUN ldconfig
# Server bins
COPY --from=build /app/build/*Server /app/
# Necessary suplimentary files
COPY --from=build /app/build/*.ini /app/configs/
COPY --from=build /app/build/vanity/*.* /app/vanity/*
COPY --from=build /app/build/navmeshes /app/navmeshes
COPY --from=build /app/build/migrations /app/migrations
COPY --from=build /app/build/*.dcf /app/
# backup of config and vanity files to copy to the host incase
# of a mount clobbering the copy from above
COPY --from=build /app/build/*.ini /app/default-configs/
COPY --from=build /app/build/vanity/*.* /app/default-vanity/*
# needed as the container runs with the root user
# and therefore sudo doesn't exist
ENV USE_SUDO_AUTH=0
ENV DLU_CONFIG_DIR=/app/configs/
COPY --chmod=0500 ./entrypoint.sh /app/
ENTRYPOINT [ "/app/entrypoint.sh" ]

111
README.md
View File

@@ -23,6 +23,9 @@ We do not recommend hosting public servers. Darkflame Universe is intended for s
### 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
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 ## Steps to setup server
* [Clone this repository](#clone-the-repository) * [Clone this repository](#clone-the-repository)
* [Install dependencies](#install-dependencies) * [Install dependencies](#install-dependencies)
@@ -34,6 +37,7 @@ Darkflame Universe is a server emulator and does not distribute any LEGO® Unive
* [Verify your setup](#verify-your-setup) * [Verify your setup](#verify-your-setup)
* [Running the server](#running-the-server) * [Running the server](#running-the-server)
* [User Guide](#user-guide) * [User Guide](#user-guide)
* [Docker](#docker)
## 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)
@@ -47,7 +51,7 @@ git clone --recursive https://github.com/DarkflameUniverse/DarkflameServer
### 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.18**</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!).
### MacOS packages ### MacOS packages
Ensure you have [brew](https://brew.sh) installed. Ensure you have [brew](https://brew.sh) installed.
@@ -69,7 +73,7 @@ sudo apt install build-essential gcc zlib1g-dev libssl-dev openssl mariadb-serve
``` ```
#### Required CMake version #### Required CMake version
This project uses <font size="4">**CMake version 3.18**</font> or higher and as such you will need to ensure you have this version installed. This project uses <font size="4">**CMake version 3.25**</font> or higher and as such you will need to ensure you have this version installed.
You can check your CMake version by using the following command in a terminal. You can check your CMake version by using the following command in a terminal.
```bash ```bash
cmake --version cmake --version
@@ -179,7 +183,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.
* If you are using a Darkflame Universe client, ensure NET_VERSION 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
This server has a few steps that need to be taken to configure the server for your use case. This server has a few steps that need to be taken to configure the server for your use case.
@@ -224,6 +228,44 @@ sudo setcap 'cap_net_bind_service=+ep' AuthServer
``` ```
and then go to `build/masterconfig.ini` and change `use_sudo_auth` to 0. 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 ### 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! 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 +327,7 @@ Below are known good SHA256 checksums of the client:
* `0d862f71eedcadc4494c4358261669721b40b2131101cbd6ef476c5a6ec6775b` (unpacked client, includes extra locales, rar compressed) * `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. 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 Client
Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there. Darkflame Universe clients identify themselves using a higher version number than the regular live clients out there.
@@ -305,6 +348,66 @@ certutil -hashfile <file> SHA1
Known good *SHA1* checksum of the Darkflame Universe client: Known good *SHA1* checksum of the Darkflame Universe client:
- `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed) - `91498e09b83ce69f46baf9e521d48f23fe502985` (packed client, zip compressed)
# Docker
The Darkflame Server is automatically built and published as a Docker Container / [OCI](https://opencontainers.org/) Image to the GitHub Container Registry at:
[`ghcr.io/darkflameuniverse/darkflameserver`](https://github.com/DarkflameUniverse/DarkflameServer/pkgs/container/darkflameserver).
## Compose
> [!WARNING]
> It seems that Docker Desktop on Windows with the WSL 2 backend has some issues with MariaDB (c.f. [mariadb-docker#331](https://github.com/MariaDB/mariadb-docker/issues/331)) triggered by NexusDashboard
> migrations, so this setup may not work for you. If that is the case, please tell us about your setup in [NexusDashboard#92](https://github.com/DarkflameUniverse/NexusDashboard/issues/92).
You can use the `docker-compose` tool to [setup a MariaDB database](#database-setup), run the Darkflame Server and manage it with [Nexus Dashboard](https://github.com/DarkflameUniverse/NexusDashboard) all
at once. For that:
- [Install Docker Desktop](https://docs.docker.com/get-docker/)
- Open the directory that contains your LU Client
- If the `legouniverse.exe` is in a subfolder called `client`, you're good to go. There may also be a folder `versions`.
- Otherwise, create a new `client` folder and move the exe and everything else (e.g. `res` and `locale`) in there. This is necessary to work around a bug in the client that will prevent that you to log back in after getting disconnected.
- Download the [docker-compose.yml](docker-compose.yml) file and place it next to `client`.
- 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).
- Update the `ACCOUNT_MANAGER_SECRET` and `MARIADB_PASSWORD` with strong random passwords.
- Use a password generator like <https://keygen.io>
- Avoid `:` and `@` characters
- 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
- Open a terminal in the folder with the `docker-compose.yml` and `client`
- Run `docker compose up -d`
- This might require `sudo` on Linux, and a recent version of [docker compose](https://docs.docker.com/compose/install/)
- Run `docker exec -it dlu-darkflameserver-1 /app/MasterServer -a` and follow the instructions to create the initial admin account
- Open <http://localhost:8000> to access Nexus Dashboard with the admin account to create normal users
- Set `AUTHSERVERIP=0:localhost` in `client/boot.cfg`
- Replace `localhost` with the value of `EXTERNAL_IP` if you changed that earlier.
- Also make sure `UGCUSE3DSERVICES=7:` is set to `0`
- Launch `legouniverse.exe`
## Standalone
This assumes that you have a database deployed to your host or in another docker container.
A basic deployment of this contianer would look like:
```sh
# example docker contianer deployment
docker run -it \
-v /path/to/configs/:/app/configs \
-v /path/to/logs/:/app/logs \
-v /path/to/dumps/:/app/dumps \
-v /path/to/res:/app/res:ro \
-v /path/to/resServer:/app/resServer \
-e DUMP_FOLDER=/app/dumps \
-p 1001:1001/udp \
-p 2005:2005/udp \
-p 3000-3300:3000-3300/udp \
ghcr.io/darkflameuniverse/darkflameserver:latest
```
You will need to replace the `/path/to/`'s to reflect the paths on your host.
Any config option in the `.ini`'s can be overridden with environmental variables: Ex: `log_to_console=1` from `shared_config.ini` would be overidden like `-e LOG_TO_CONSOLE=0`
# Development Documentation # Development Documentation
This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server This is a Work in Progress, but below are some quick links to documentaion for systems and structs in the server
[Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html) [Networked message structs](https://lcdruniverse.org/lu_packets/lu_packets/index.html)
@@ -338,7 +441,7 @@ This is a Work in Progress, but below are some quick links to documentaion for s
## Former Contributors ## Former Contributors
* TheMachine * TheMachine
* Matthew * Matthew
* [Raine](https://github.com/Rainebannister) * [Raine](https://github.com/uwainium)
* Bricknave * Bricknave
## Special Thanks ## Special Thanks

View File

@@ -0,0 +1,17 @@
include(FetchContent)
message(STATUS "Fetching gtest...")
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG release-1.12.1
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(GoogleTest)
message(STATUS "gtest fetched and is now ready.")
set(GoogleTest_FOUND TRUE)

146
cmake/FindMariaDB.cmake Normal file
View File

@@ -0,0 +1,146 @@
# mariadb connector cpp
# On Windows ClangCL can't compile the connector from source but can link to an msvc compiled one,
# so prefer the prebuilt binaries unless MARIADB_BUILD_SOURCE is specified
if(WIN32 AND NOT MARIADB_BUILD_SOURCE)
set(MARIADB_MSI_DIR "${PROJECT_BINARY_DIR}/msi")
set(MARIADB_CONNECTOR_DIR "${PROJECT_BINARY_DIR}/mariadbcpp")
set(MARIADB_C_CONNECTOR_DIR "${MARIADB_CONNECTOR_DIR}/MariaDB/MariaDB Connector C 64-bit")
set(MARIADB_CPP_CONNECTOR_DIR "${MARIADB_CONNECTOR_DIR}/MariaDB/MariaDB C++ Connector 64-bit")
file(MAKE_DIRECTORY "${MARIADB_MSI_DIR}")
file(MAKE_DIRECTORY "${MARIADB_CONNECTOR_DIR}")
# These values need to be updated whenever a new minor release replaces an old one
# Go to https://mariadb.com/downloads/connectors/ to find the up-to-date URL parts
set(MARIADB_CONNECTOR_C_VERSION "3.2.7")
set(MARIADB_CONNECTOR_C_BUCKET "2319651")
set(MARIADB_CONNECTOR_C_MD5 "f8636d733f1d093af9d4f22f3239f885")
set(MARIADB_CONNECTOR_CPP_VERSION "1.0.2")
set(MARIADB_CONNECTOR_CPP_BUCKET "2531525")
set(MARIADB_CONNECTOR_CPP_MD5 "3034bbd6ca00a0125345f9fd1a178401")
set(MARIADB_CONNECTOR_C_MSI "mariadb-connector-c-${MARIADB_CONNECTOR_C_VERSION}-win64.msi")
set(MARIADB_CONNECTOR_CPP_MSI "mariadb-connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}-win64.msi")
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" )
message(STATUS "Downloading mariadb connector/c")
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_C_BUCKET}/Connectors/c/connector-c-${MARIADB_CONNECTOR_C_VERSION}/${MARIADB_CONNECTOR_C_MSI}
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}"
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_C_MD5})
endif()
if(NOT EXISTS "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" )
message(STATUS "Downloading mariadb connector/c++")
file(DOWNLOAD https://dlm.mariadb.com/${MARIADB_CONNECTOR_CPP_BUCKET}/Connectors/cpp/connector-cpp-${MARIADB_CONNECTOR_CPP_VERSION}/${MARIADB_CONNECTOR_CPP_MSI}
"${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}"
EXPECTED_HASH MD5=${MARIADB_CONNECTOR_CPP_MD5})
endif()
file(TO_NATIVE_PATH "${MARIADB_CONNECTOR_DIR}" MSIEXEC_TARGETDIR)
# extract msi files without installing to users system
if(NOT EXISTS "${MARIADB_C_CONNECTOR_DIR}")
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_C_MSI}" MSI_DIR)
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
endif()
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADB_C_CONNECTOR_DIR}/lib/libmariadb.dll")
if(NOT EXISTS "${MARIADB_CPP_CONNECTOR_DIR}")
file(TO_NATIVE_PATH "${MARIADB_MSI_DIR}/${MARIADB_CONNECTOR_CPP_MSI}" MSI_DIR)
execute_process(COMMAND msiexec /a ${MSI_DIR} /qn TARGETDIR=${MSIEXEC_TARGETDIR})
endif()
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.dll")
set(MARIADB_IMPLIB_LOCATION "${MARIADB_CPP_CONNECTOR_DIR}/mariadbcpp.lib")
set(MARIADB_INCLUDE_DIR "${MARIADB_CPP_CONNECTOR_DIR}/include/mariadb")
add_custom_target(mariadb_connector_cpp)
add_custom_command(TARGET mariadb_connector_cpp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${MARIADBCPP_SHARED_LIBRARY_LOCATION}"
"${MARIADBC_SHARED_LIBRARY_LOCATION}"
"${PROJECT_BINARY_DIR}")
# MariaDB uses plugins that the database needs to load, the prebuilt binaries by default will try to find the libraries in system directories,
# so set this define and the servers will set the MARIADB_PLUGIN_DIR environment variable to the appropriate directory.
# Plugin directory is determined at dll load time (this will happen before main()) so we need to delay the dll load so that we can set the environment variable
add_link_options(/DELAYLOAD:${MARIADBCPP_SHARED_LIBRARY_LOCATION})
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADB_CPP_CONNECTOR_DIR}/plugin")
else() # Build from source
include(ExternalProject)
if(WIN32)
set(MARIADB_EXTRA_CMAKE_ARGS
-DCMAKE_C_FLAGS=/w # disable zlib warnings
-DCMAKE_CXX_FLAGS=/EHsc
-DWITH_MSI=OFF)
elseif(APPLE)
set(MARIADB_EXTRA_CMAKE_ARGS
-DWITH_EXTERNAL_ZLIB=ON
-DOPENSSL_ROOT_DIR=${OPENSSL_ROOT_DIR}
-DCMAKE_C_FLAGS=-w # disable zlib warnings
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
else()
set(MARIADB_EXTRA_CMAKE_ARGS
-DCMAKE_C_FLAGS=-w # disable zlib warnings
-DCMAKE_CXX_FLAGS=-D_GLIBCXX_USE_CXX11_ABI=0)
endif()
set(MARIADBCPP_INSTALL_DIR ${PROJECT_BINARY_DIR}/prefix)
set(MARIADBCPP_LIBRARY_DIR ${PROJECT_BINARY_DIR}/mariadbcpp)
set(MARIADBCPP_PLUGIN_DIR ${MARIADBCPP_LIBRARY_DIR}/plugin)
set(MARIADBCPP_SOURCE_DIR ${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp)
set(MARIADB_INCLUDE_DIR "${MARIADBCPP_SOURCE_DIR}/include")
ExternalProject_Add(mariadb_connector_cpp
PREFIX "${PROJECT_BINARY_DIR}/thirdparty/mariadb-connector-cpp"
SOURCE_DIR ${MARIADBCPP_SOURCE_DIR}
INSTALL_DIR ${MARIADBCPP_INSTALL_DIR}
CMAKE_ARGS -Wno-dev
-DWITH_UNIT_TESTS=OFF
-DMARIADB_LINK_DYNAMIC=OFF
-DCMAKE_BUILD_RPATH_USE_ORIGIN=${CMAKE_BUILD_RPATH_USE_ORIGIN}
-DCMAKE_INSTALL_PREFIX=<INSTALL_DIR>
-DINSTALL_LIBDIR=${MARIADBCPP_LIBRARY_DIR}
-DINSTALL_PLUGINDIR=${MARIADBCPP_PLUGIN_DIR}
${MARIADB_EXTRA_CMAKE_ARGS}
BUILD_ALWAYS true
)
if(WIN32)
set(MARIADB_SHARED_LIBRARY_NAME mariadbcpp.dll)
set(MARIADB_PLUGIN_SUFFIX .dll)
set(MARIADB_IMPLIB_LOCATION "${MARIADBCPP_LIBRARY_DIR}/mariadbcpp.lib")
# When built from source windows only seems to check same folder as exe instead specified folder, so use
# environment variable to force it
add_link_options(/DELAYLOAD:mariadbcpp.dll)
add_compile_definitions(MARIADB_PLUGIN_DIR_OVERRIDE="${MARIADBCPP_PLUGIN_DIR}")
else()
set(MARIADB_SHARED_LIBRARY_NAME libmariadbcpp${CMAKE_SHARED_LIBRARY_SUFFIX})
set(MARIADB_PLUGIN_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX})
endif()
set(MARIADBCPP_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/${MARIADB_SHARED_LIBRARY_NAME}")
if(WIN32)
set(MARIADBC_SHARED_LIBRARY_LOCATION "${MARIADBCPP_LIBRARY_DIR}/libmariadb.lib")
endif()
endif()
# Create mariadb connector library object
add_library(MariaDB::ConnCpp SHARED IMPORTED GLOBAL)
add_dependencies(MariaDB::ConnCpp mariadb_connector_cpp)
set_target_properties(MariaDB::ConnCpp PROPERTIES
IMPORTED_LOCATION "${MARIADBCPP_SHARED_LIBRARY_LOCATION}")
if(WIN32)
set_target_properties(MariaDB::ConnCpp PROPERTIES
IMPORTED_IMPLIB "${MARIADB_IMPLIB_LOCATION}")
elseif(APPLE)
set_target_properties(MariaDB::ConnCpp PROPERTIES
IMPORTED_SONAME "libmariadbcpp")
endif()
# Add directories to include lists
target_include_directories(MariaDB::ConnCpp SYSTEM INTERFACE ${MARIADB_INCLUDE_DIR})
set(MariaDB_FOUND TRUE)

51
cmake/Utils.cmake Normal file
View File

@@ -0,0 +1,51 @@
# Parses a config file for a specific option and appends the new option if it does not exist
# If the new option does exist, this function will do nothing.
# file_name: The name of the file to parse
# old_option_name: The name of the option to find
# new_option_name: The name of the option to add
function(UpdateConfigOption file_name old_option_name new_option_name)
string(APPEND old_option_name "=")
string(APPEND new_option_name "=")
message(STATUS "Checking " ${file_name} " for " ${old_option_name} " and adding " ${new_option_name} " if it does not exist")
if(NOT EXISTS ${file_name})
message(STATUS ${file_name} " does not exist. Doing nothing")
return()
endif()
file(READ ${file_name} current_file_contents)
string(REPLACE "\\\n" "" current_file_contents ${current_file_contents})
string(REPLACE "\n" ";" current_file_contents ${current_file_contents})
set(parsed_current_file_contents "")
# Remove comment lines so they do not interfere with the variable parsing
foreach(line ${current_file_contents})
string(FIND ${line} "#" is_comment)
if(NOT ${is_comment} EQUAL 0)
string(APPEND parsed_current_file_contents ${line})
endif()
endforeach()
set(found_new_option -1)
set(found_old_option -1)
set(current_value -1)
foreach(line ${current_file_contents})
string(FIND ${line} ${old_option_name} old_option_in_file)
if(${old_option_in_file} EQUAL 0)
set(found_old_option 1)
set(current_value ${line})
endif()
string(FIND ${line} ${new_option_name} found_new_option_in_file)
if(${found_new_option_in_file} EQUAL 0)
set(found_new_option 1)
endif()
endforeach(line ${current_file_contents})
if(${found_old_option} EQUAL 1 AND NOT ${found_new_option} EQUAL 1)
string(REPLACE ${old_option_name} ${new_option_name} current_value ${current_value})
file(APPEND ${file_name} "\n" ${current_value})
endif()
endfunction()

View File

@@ -1,13 +1,14 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <ctime> #include <ctime>
#include <csignal>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
//DLU Includes: //DLU Includes:
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dServer.h" #include "dServer.h"
#include "dLogger.h" #include "Logger.h"
#include "Database.h" #include "Database.h"
#include "dConfig.h" #include "dConfig.h"
#include "Diagnostics.h" #include "Diagnostics.h"
@@ -15,7 +16,7 @@
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
#include <MessageIdentifiers.h> #include "MessageIdentifiers.h"
//Auth includes: //Auth includes:
#include "AuthPackets.h" #include "AuthPackets.h"
@@ -24,14 +25,17 @@
#include "eAuthMessageType.h" #include "eAuthMessageType.h"
#include "Game.h" #include "Game.h"
#include "Server.h"
namespace Game { namespace Game {
dLogger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
std::mt19937 randomEngine;
} }
dLogger* SetupLogger();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
int main(int argc, char** argv) { int main(int argc, char** argv) {
@@ -41,29 +45,23 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
Game::config = new dConfig("authconfig.ini");
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Server::SetupLogger("AuthServer");
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: LOG("Starting Auth server...");
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "authconfig.ini").string()); LOG("Version: %s", PROJECT_VERSION);
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0"); LOG("Compiled on: %s", __TIMESTAMP__);
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
Game::logger->Log("AuthServer", "Starting Auth server...");
Game::logger->Log("AuthServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR);
Game::logger->Log("AuthServer", "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 { try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); Database::Connect();
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
Game::logger->Log("AuthServer", "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;
delete Game::logger; delete Game::logger;
@@ -73,23 +71,26 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1500; 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; auto masterInfo = Database::Get()->GetMasterInfo();
delete stmt; if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
LOG("Master is at %s:%d", masterIP.c_str(), masterPort);
Game::randomEngine = std::mt19937(time(0));
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 50; std::string ourIP = "localhost";
uint32_t ourPort = 1001; //LU client is hardcoded to use this for auth port, so I'm making it the default. const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients"));
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str());
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::shouldShutdown); //LU client is hardcoded to use this for auth port, so I'm making it the default.
const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("auth_server_port")).value_or(1001);
const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Auth, Game::config, &Game::lastSignal);
//Run it until server gets a kill message from Master: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();
@@ -100,13 +101,18 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
while (!Game::shouldShutdown) { AuthPackets::LoadClaimCodes();
Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;
if (framesSinceMasterDisconnect >= authFramerate) if (framesSinceMasterDisconnect >= authFramerate) {
LOG("No connection to master!");
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. //In world we'd update our other systems here.
@@ -131,16 +137,12 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason: //Find out the master's IP for absolutely no reason:
std::string masterIP; std::string masterIP;
uint32_t masterPort; uint32_t masterPort;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); auto masterInfo = Database::Get()->GetMasterInfo();
auto res = stmt->executeQuery(); if (masterInfo) {
while (res->next()) { masterIP = masterInfo->ip;
masterIP = res->getString(1).c_str(); masterPort = masterInfo->port;
masterPort = res->getInt(2);
} }
delete res;
delete stmt;
framesSinceLastSQLPing = 0; framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++; } else framesSinceLastSQLPing++;
@@ -149,6 +151,7 @@ int main(int argc, char** argv) {
std::this_thread::sleep_until(t); std::this_thread::sleep_until(t);
} }
LOG("Exited Main Loop! (signal %d)", Game::lastSignal);
//Delete our objects here: //Delete our objects here:
Database::Destroy("AuthServer"); Database::Destroy("AuthServer");
delete Game::server; delete Game::server;
@@ -158,18 +161,6 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
dLogger* SetupLogger() {
std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/AuthServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false;
bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
logDebugStatements = true;
#endif
return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 4) return; if (packet->length < 4) return;

View File

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

View File

@@ -8,7 +8,7 @@
#include <regex> #include <regex>
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dLogger.h" #include "Logger.h"
#include "dConfig.h" #include "dConfig.h"
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
@@ -27,20 +27,16 @@ dChatFilter::dChatFilter(const std::string& filepath, bool dontGenerateDCF) {
ExportWordlistToDCF(filepath + ".dcf", true); ExportWordlistToDCF(filepath + ".dcf", true);
} }
if (BinaryIO::DoesFileExist("blacklist.dcf")) { if (BinaryIO::DoesFileExist("blocklist.dcf")) {
ReadWordlistDCF("blacklist.dcf", false); ReadWordlistDCF("blocklist.dcf", false);
} }
//Read player names that are ok as well: //Read player names that are ok as well:
auto stmt = Database::CreatePreppedStmt("select name from charinfo;"); auto approvedNames = Database::Get()->GetApprovedCharacterNames();
auto res = stmt->executeQuery(); for (auto& name : approvedNames) {
while (res->next()) { std::transform(name.begin(), name.end(), name.begin(), ::tolower); //Transform to lowercase
std::string line = res->getString(1).c_str(); m_ApprovedWords.push_back(CalculateHash(name));
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
m_ApprovedWords.push_back(CalculateHash(line));
} }
delete res;
delete stmt;
} }
dChatFilter::~dChatFilter() { dChatFilter::~dChatFilter() {
@@ -48,20 +44,20 @@ dChatFilter::~dChatFilter() {
m_DeniedWords.clear(); m_DeniedWords.clear();
} }
void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool whiteList) { void dChatFilter::ReadWordlistPlaintext(const std::string& filepath, bool allowList) {
std::ifstream file(filepath); std::ifstream file(filepath);
if (file) { if (file) {
std::string line; std::string line;
while (std::getline(file, line)) { while (std::getline(file, line)) {
line.erase(std::remove(line.begin(), line.end(), '\r'), line.end()); line.erase(std::remove(line.begin(), line.end(), '\r'), line.end());
std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase std::transform(line.begin(), line.end(), line.begin(), ::tolower); //Transform to lowercase
if (whiteList) m_ApprovedWords.push_back(CalculateHash(line)); if (allowList) m_ApprovedWords.push_back(CalculateHash(line));
else m_DeniedWords.push_back(CalculateHash(line)); else m_DeniedWords.push_back(CalculateHash(line));
} }
} }
} }
bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) { bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool allowList) {
std::ifstream file(filepath, std::ios::binary); std::ifstream file(filepath, std::ios::binary);
if (file) { if (file) {
fileHeader hdr; fileHeader hdr;
@@ -74,13 +70,13 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
if (hdr.formatVersion == formatVersion) { if (hdr.formatVersion == formatVersion) {
size_t wordsToRead = 0; size_t wordsToRead = 0;
BinaryIO::BinaryRead(file, wordsToRead); BinaryIO::BinaryRead(file, wordsToRead);
if (whiteList) m_ApprovedWords.reserve(wordsToRead); if (allowList) m_ApprovedWords.reserve(wordsToRead);
else m_DeniedWords.reserve(wordsToRead); else m_DeniedWords.reserve(wordsToRead);
size_t word = 0; size_t word = 0;
for (size_t i = 0; i < wordsToRead; ++i) { for (size_t i = 0; i < wordsToRead; ++i) {
BinaryIO::BinaryRead(file, word); BinaryIO::BinaryRead(file, word);
if (whiteList) m_ApprovedWords.push_back(word); if (allowList) m_ApprovedWords.push_back(word);
else m_DeniedWords.push_back(word); else m_DeniedWords.push_back(word);
} }
@@ -94,14 +90,14 @@ bool dChatFilter::ReadWordlistDCF(const std::string& filepath, bool whiteList) {
return false; return false;
} }
void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteList) { void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowList) {
std::ofstream file(filepath, std::ios::binary | std::ios_base::out); std::ofstream file(filepath, std::ios::binary | std::ios_base::out);
if (file) { if (file) {
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header)); BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::header));
BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion)); BinaryIO::BinaryWrite(file, uint32_t(dChatFilterDCF::formatVersion));
BinaryIO::BinaryWrite(file, size_t(whiteList ? m_ApprovedWords.size() : m_DeniedWords.size())); BinaryIO::BinaryWrite(file, size_t(allowList ? m_ApprovedWords.size() : m_DeniedWords.size()));
for (size_t word : whiteList ? m_ApprovedWords : m_DeniedWords) { for (size_t word : allowList ? m_ApprovedWords : m_DeniedWords) {
BinaryIO::BinaryWrite(file, word); BinaryIO::BinaryWrite(file, word);
} }
@@ -109,10 +105,10 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool whiteLis
} }
} }
std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList) { std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) {
if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true. if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true.
if (message.empty()) return { }; if (message.empty()) return { };
if (!whiteList && m_DeniedWords.empty()) return { { 0, message.length() } }; if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } };
std::stringstream sMessage(message); std::stringstream sMessage(message);
std::string segment; std::string segment;
@@ -130,21 +126,21 @@ std::vector<std::pair<uint8_t, uint8_t>> dChatFilter::IsSentenceOkay(const std::
size_t hash = CalculateHash(segment); size_t hash = CalculateHash(segment);
if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && whiteList) { if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) {
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && whiteList) { if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !whiteList) { if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) {
m_UserUnapprovedWordCache.push_back(hash); m_UserUnapprovedWordCache.push_back(hash);
listOfBadSegments.emplace_back(position, originalSegment.length()); listOfBadSegments.emplace_back(position, originalSegment.length());
} }
position += segment.length() + 1; position += originalSegment.length() + 1;
} }
return listOfBadSegments; return listOfBadSegments;

View File

@@ -21,10 +21,10 @@ public:
dChatFilter(const std::string& filepath, bool dontGenerateDCF); dChatFilter(const std::string& filepath, bool dontGenerateDCF);
~dChatFilter(); ~dChatFilter();
void ReadWordlistPlaintext(const std::string& filepath, bool whiteList); void ReadWordlistPlaintext(const std::string& filepath, bool allowList);
bool ReadWordlistDCF(const std::string& filepath, bool whiteList); bool ReadWordlistDCF(const std::string& filepath, bool allowList);
void ExportWordlistToDCF(const std::string& filepath, bool whiteList); void ExportWordlistToDCF(const std::string& filepath, bool allowList);
std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool whiteList = true); std::vector<std::pair<uint8_t, uint8_t>> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true);
private: private:
bool m_DontGenerateDCF; bool m_DontGenerateDCF;

View File

@@ -1,10 +1,16 @@
set(DCHATSERVER_SOURCES set(DCHATSERVER_SOURCES
"ChatIgnoreList.cpp"
"ChatPacketHandler.cpp" "ChatPacketHandler.cpp"
"PlayerContainer.cpp" "PlayerContainer.cpp"
) )
add_executable(ChatServer "ChatServer.cpp") add_executable(ChatServer "ChatServer.cpp")
target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter")
add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"")
add_library(dChatServer ${DCHATSERVER_SOURCES}) add_library(dChatServer ${DCHATSERVER_SOURCES})
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) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer)

View File

@@ -0,0 +1,171 @@
#include "ChatIgnoreList.h"
#include "PlayerContainer.h"
#include "eChatMessageType.h"
#include "BitStreamUtils.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, eChatMessageType::WORLD_ROUTE_PACKET);
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.GetPlayerDataMutable(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, but is requesting it again.", playerId);
} else {
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.emplace_back(ignoredPlayer.name, ignoredPlayer.id);
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.GetPlayerDataMutable(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;
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
const 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.emplace_back(toIgnoreStr, ignoredPlayerId);
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.GetPlayerDataMutable(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;
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__

File diff suppressed because it is too large Load Diff

View File

@@ -4,16 +4,58 @@
#include "BitStream.h" #include "BitStream.h"
struct PlayerData; struct PlayerData;
enum class eAddFriendResponseType : uint8_t; enum class eAddFriendResponseType : uint8_t;
enum class eChatChannel : uint8_t {
SYSTEMNOTIFY = 0,
SYSTEMWARNING,
SYSTEMERROR,
BROADCAST,
LOCAL,
LOCALNOANIM,
EMOTE,
PRIVATE_CHAT,
TEAM,
TEAMLOCAL,
GUILD,
GUILDNOTIFY,
PROPERTY,
ADMIN,
COMBATDAMAGE,
COMBATHEALING,
COMBATLOOT,
COMBATEXP,
COMBATDEATH,
GENERAL,
TRADE,
LFG,
USER
};
enum class eChatMessageResponseCode : uint8_t {
SENT = 0,
NOTONLINE,
GENERALERROR,
RECEIVEDNEWWHISPER,
NOTFRIENDS,
SENDERFREETRIAL,
RECEIVERFREETRIAL,
};
namespace ChatPacketHandler { namespace ChatPacketHandler {
void HandleFriendlistRequest(Packet* packet); void HandleFriendlistRequest(Packet* packet);
void HandleFriendRequest(Packet* packet); void HandleFriendRequest(Packet* packet);
void HandleFriendResponse(Packet* packet); void HandleFriendResponse(Packet* packet);
void HandleRemoveFriend(Packet* packet); void HandleRemoveFriend(Packet* packet);
void HandleGMLevelUpdate(Packet* packet);
void HandleWho(Packet* packet);
void HandleShowAll(Packet* packet);
void HandleChatMessage(Packet* packet); void HandleChatMessage(Packet* packet);
void HandlePrivateChatMessage(Packet* packet); void HandlePrivateChatMessage(Packet* packet);
void SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode);
void HandleTeamInvite(Packet* packet); void HandleTeamInvite(Packet* packet);
void HandleTeamInviteResponse(Packet* packet); void HandleTeamInviteResponse(Packet* packet);
@@ -23,18 +65,18 @@ namespace ChatPacketHandler {
void HandleTeamLootOption(Packet* packet); void HandleTeamLootOption(Packet* packet);
void HandleTeamStatusRequest(Packet* packet); void HandleTeamStatusRequest(Packet* packet);
void SendTeamInvite(PlayerData* receiver, PlayerData* sender); void SendTeamInvite(const PlayerData& receiver, const PlayerData& sender);
void SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName); void SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName);
void SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName); void SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName);
void SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID); void SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID);
void SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID); void SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID);
void SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName); void SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName);
void SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID); void SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID);
//FriendData is the player we're SENDING this stuff to. Player is the friend that changed state. //FriendData is the player we're SENDING this stuff to. Player is the friend that changed state.
void SendFriendUpdate(PlayerData* friendData, PlayerData* playerData, uint8_t notifyType, uint8_t isBestFriend); void SendFriendUpdate(const PlayerData& friendData, const PlayerData& playerData, uint8_t notifyType, uint8_t isBestFriend);
void SendFriendRequest(PlayerData* receiver, PlayerData* sender); void SendFriendRequest(const PlayerData& receiver, const PlayerData& sender);
void SendFriendResponse(PlayerData* receiver, PlayerData* sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U); void SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready = 0U, uint8_t isBestFriendRequest = 0U);
void SendRemoveFriend(PlayerData* receiver, std::string& personToRemove, bool isSuccessful); void SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful);
}; };

View File

@@ -6,7 +6,7 @@
//DLU Includes: //DLU Includes:
#include "dCommonVars.h" #include "dCommonVars.h"
#include "dServer.h" #include "dServer.h"
#include "dLogger.h" #include "Logger.h"
#include "Database.h" #include "Database.h"
#include "dConfig.h" #include "dConfig.h"
#include "dChatFilter.h" #include "dChatFilter.h"
@@ -17,30 +17,30 @@
#include "PlayerContainer.h" #include "PlayerContainer.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eWorldMessageType.h" #include "eWorldMessageType.h"
#include "ChatIgnoreList.h"
#include "StringifiedEnum.h"
#include "Game.h" #include "Game.h"
#include "Server.h"
//RakNet includes: //RakNet includes:
#include "RakNetDefines.h" #include "RakNetDefines.h"
#include <MessageIdentifiers.h> #include "MessageIdentifiers.h"
namespace Game { namespace Game {
dLogger* logger = nullptr; Logger* logger = nullptr;
dServer* server = nullptr; dServer* server = nullptr;
dConfig* config = nullptr; dConfig* config = nullptr;
dChatFilter* chatFilter = nullptr; dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr; AssetManager* assetManager = nullptr;
bool shouldShutdown = false; Game::signal_t lastSignal = 0;
std::mt19937 randomEngine;
PlayerContainer playerContainer;
} }
dLogger* SetupLogger();
void HandlePacket(Packet* packet); void HandlePacket(Packet* packet);
PlayerContainer playerContainer;
int main(int argc, char** argv) { int main(int argc, char** argv) {
constexpr uint32_t chatFramerate = mediumFramerate; constexpr uint32_t chatFramerate = mediumFramerate;
constexpr uint32_t chatFrameDelta = mediumFrameDelta; constexpr uint32_t chatFrameDelta = mediumFrameDelta;
@@ -48,18 +48,20 @@ int main(int argc, char** argv) {
Diagnostics::SetProcessFileName(argv[0]); Diagnostics::SetProcessFileName(argv[0]);
Diagnostics::Initialize(); Diagnostics::Initialize();
std::signal(SIGINT, Game::OnSignal);
std::signal(SIGTERM, Game::OnSignal);
Game::config = new dConfig("chatconfig.ini");
//Create all the objects we need to run our service: //Create all the objects we need to run our service:
Game::logger = SetupLogger(); Server::SetupLogger("ChatServer");
if (!Game::logger) return EXIT_FAILURE; if (!Game::logger) return EXIT_FAILURE;
//Read our config: //Read our config:
Game::config = new dConfig((BinaryPathFinder::GetBinaryDir() / "chatconfig.ini").string());
Game::logger->SetLogToConsole(Game::config->GetValue("log_to_console") != "0");
Game::logger->SetLogDebugStatements(Game::config->GetValue("log_debug_statements") == "1");
Game::logger->Log("ChatServer", "Starting Chat server..."); LOG("Starting Chat server...");
Game::logger->Log("ChatServer", "Version: %i.%i", PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR); LOG("Version: %s", PROJECT_VERSION);
Game::logger->Log("ChatServer", "Compiled on: %s", __TIMESTAMP__); LOG("Compiled on: %s", __TIMESTAMP__);
try { try {
std::string clientPathStr = Game::config->GetValue("client_location"); std::string clientPathStr = Game::config->GetValue("client_location");
@@ -71,21 +73,16 @@ int main(int argc, char** argv) {
Game::assetManager = new AssetManager(clientPath); Game::assetManager = new AssetManager(clientPath);
} catch (std::runtime_error& ex) { } catch (std::runtime_error& ex) {
Game::logger->Log("ChatServer", "Got an error while setting up assets: %s", ex.what()); LOG("Got an error while setting up assets: %s", ex.what());
return EXIT_FAILURE; return EXIT_FAILURE;
} }
//Connect to the MySQL Database //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 { try {
Database::Connect(mysql_host, mysql_database, mysql_username, mysql_password); Database::Connect();
} catch (sql::SQLException& ex) { } catch (sql::SQLException& ex) {
Game::logger->Log("ChatServer", "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;
delete Game::logger; delete Game::logger;
@@ -95,25 +92,26 @@ int main(int argc, char** argv) {
//Find out the master's IP: //Find out the master's IP:
std::string masterIP; std::string masterIP;
uint32_t masterPort = 1000; uint32_t masterPort = 1000;
sql::PreparedStatement* stmt = Database::CreatePreppedStmt("SELECT ip, port FROM servers WHERE name='master';"); auto masterInfo = Database::Get()->GetMasterInfo();
auto res = stmt->executeQuery(); if (masterInfo) {
while (res->next()) { masterIP = masterInfo->ip;
masterIP = res->getString(1).c_str(); masterPort = masterInfo->port;
masterPort = res->getInt(2);
} }
delete res;
delete stmt;
//It's safe to pass 'localhost' here, as the IP is only used as the external IP. //It's safe to pass 'localhost' here, as the IP is only used as the external IP.
uint32_t maxClients = 50; std::string ourIP = "localhost";
uint32_t ourPort = 1501; const uint32_t maxClients = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_clients")).value_or(999);
if (Game::config->GetValue("max_clients") != "") maxClients = std::stoi(Game::config->GetValue("max_clients")); const uint32_t ourPort = GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("chat_server_port")).value_or(2005);
if (Game::config->GetValue("port") != "") ourPort = std::atoi(Game::config->GetValue("port").c_str()); const auto externalIPString = Game::config->GetValue("external_ip");
if (!externalIPString.empty()) ourIP = externalIPString;
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown); Game::server = new dServer(ourIP, ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::lastSignal);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf")))); const bool dontGenerateDCF = GeneralUtils::TryParse<bool>(Game::config->GetValue("dont_generate_dcf")).value_or(false);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", dontGenerateDCF);
Game::randomEngine = std::mt19937(time(0));
Game::playerContainer.Initialize();
//Run it until server gets a kill message from Master: //Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now(); auto t = std::chrono::high_resolution_clock::now();
@@ -124,7 +122,8 @@ int main(int argc, char** argv) {
uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceMasterDisconnect = 0;
uint32_t framesSinceLastSQLPing = 0; uint32_t framesSinceLastSQLPing = 0;
while (!Game::shouldShutdown) { Game::logger->Flush(); // once immediately before main loop
while (!Game::ShouldShutdown()) {
//Check if we're still connected to master: //Check if we're still connected to master:
if (!Game::server->GetIsConnectedToMaster()) { if (!Game::server->GetIsConnectedToMaster()) {
framesSinceMasterDisconnect++; framesSinceMasterDisconnect++;
@@ -155,15 +154,12 @@ int main(int argc, char** argv) {
//Find out the master's IP for absolutely no reason: //Find out the master's IP for absolutely no reason:
std::string masterIP; std::string masterIP;
uint32_t masterPort; 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; auto masterInfo = Database::Get()->GetMasterInfo();
delete stmt; if (masterInfo) {
masterIP = masterInfo->ip;
masterPort = masterInfo->port;
}
framesSinceLastSQLPing = 0; framesSinceLastSQLPing = 0;
} else framesSinceLastSQLPing++; } else framesSinceLastSQLPing++;
@@ -182,67 +178,46 @@ int main(int argc, char** argv) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
dLogger* SetupLogger() {
std::string logPath = (BinaryPathFinder::GetBinaryDir() / ("logs/ChatServer_" + std::to_string(time(nullptr)) + ".log")).string();
bool logToConsole = false;
bool logDebugStatements = false;
#ifdef _DEBUG
logToConsole = true;
logDebugStatements = true;
#endif
return new dLogger(logPath, logToConsole, logDebugStatements);
}
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) { if (packet->data[0] == ID_DISCONNECTION_NOTIFICATION || packet->data[0] == ID_CONNECTION_LOST) {
Game::logger->Log("ChatServer", "A server has disconnected, erasing their connected players from the list."); LOG("A server has disconnected, erasing their connected players from the list.");
} } else if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
LOG("A server is connecting, awaiting user list.");
} else if (packet->length < 4 || packet->data[0] != ID_USER_PACKET_ENUM) return; // Nothing left to process or not the right packet type
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) { CINSTREAM;
Game::logger->Log("ChatServer", "A server is connecting, awaiting user list."); inStream.SetReadOffset(BYTES_TO_BITS(1));
}
if (packet->length < 4) return; // Nothing left to process. Need 4 bytes to continue. eConnectionType connection;
eChatMessageType chatMessageID;
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { inStream.Read(connection);
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { if (connection != eConnectionType::CHAT) return;
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION: inStream.Read(chatMessageID);
playerContainer.InsertPlayer(packet);
switch (chatMessageID) {
case eChatMessageType::GM_MUTE:
Game::playerContainer.MuteUpdate(packet);
break; break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: case eChatMessageType::CREATE_TEAM:
playerContainer.RemovePlayer(packet); Game::playerContainer.CreateTeamServer(packet);
break; break;
case eChatInternalMessageType::MUTE_UPDATE:
playerContainer.MuteUpdate(packet);
break;
case eChatInternalMessageType::CREATE_TEAM:
playerContainer.CreateTeamServer(packet);
break;
case eChatInternalMessageType::ANNOUNCEMENT: {
//we just forward this packet to every connected server
CINSTREAM;
Game::server->Send(&inStream, packet->systemAddress, true); //send to everyone except origin
break;
}
default:
Game::logger->Log("ChatServer", "Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
switch (static_cast<eChatMessageType>(packet->data[3])) {
case eChatMessageType::GET_FRIENDS_LIST: case eChatMessageType::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet); ChatPacketHandler::HandleFriendlistRequest(packet);
break; break;
case eChatMessageType::GET_IGNORE_LIST: case eChatMessageType::GET_IGNORE_LIST:
Game::logger->Log("ChatServer", "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; break;
case eChatMessageType::TEAM_GET_STATUS: case eChatMessageType::TEAM_GET_STATUS:
@@ -298,21 +273,73 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::TEAM_SET_LOOT: case eChatMessageType::TEAM_SET_LOOT:
ChatPacketHandler::HandleTeamLootOption(packet); ChatPacketHandler::HandleTeamLootOption(packet);
break; break;
case eChatMessageType::GMLEVEL_UPDATE:
default: ChatPacketHandler::HandleGMLevelUpdate(packet);
Game::logger->Log("ChatServer", "Unknown CHAT id: %i", int(packet->data[3])); break;
} case eChatMessageType::LOGIN_SESSION_NOTIFY:
} Game::playerContainer.InsertPlayer(packet);
break;
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) { case eChatMessageType::GM_ANNOUNCE:{
switch (static_cast<eWorldMessageType>(packet->data[3])) { // we just forward this packet to every connected server
case eWorldMessageType::ROUTE_PACKET: { inStream.ResetReadPointer();
Game::logger->Log("ChatServer", "Routing packet from world"); Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
}
break;
case eChatMessageType::UNEXPECTED_DISCONNECT:
Game::playerContainer.RemovePlayer(packet);
break;
case eChatMessageType::WHO:
ChatPacketHandler::HandleWho(packet);
break;
case eChatMessageType::SHOW_ALL:
ChatPacketHandler::HandleShowAll(packet);
break;
case eChatMessageType::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
case eChatMessageType::WORLD_PARCEL_RESPONSE:
case eChatMessageType::TEAM_MISSED_INVITE_CHECK:
case eChatMessageType::GUILD_CREATE:
case eChatMessageType::GUILD_INVITE:
case eChatMessageType::GUILD_INVITE_RESPONSE:
case eChatMessageType::GUILD_LEAVE:
case eChatMessageType::GUILD_KICK:
case eChatMessageType::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_ALL:
case eChatMessageType::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODEL_READY:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
case eChatMessageType::PROPERTY_MODERATION_CHANGED:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED:
case eChatMessageType::PROPERTY_BUILDMODE_CHANGED_REPORT:
case eChatMessageType::MAIL:
case eChatMessageType::WORLD_INSTANCE_LOCATION_REQUEST:
case eChatMessageType::REPUTATION_UPDATE:
case eChatMessageType::SEND_CANNED_TEXT:
case eChatMessageType::CHARACTER_NAME_CHANGE_REQUEST:
case eChatMessageType::CSR_REQUEST:
case eChatMessageType::CSR_REPLY:
case eChatMessageType::GM_KICK:
case eChatMessageType::WORLD_ROUTE_PACKET:
case eChatMessageType::GET_ZONE_POPULATIONS:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
case eChatMessageType::MATCH_REQUEST:
case eChatMessageType::UGCMANIFEST_REPORT_MISSING_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCC_REQUEST:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::ACHIEVEMENT_NOTIFY:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::PLAYER_READY:
case eChatMessageType::GET_DONATION_TOTAL:
case eChatMessageType::UPDATE_DONATION:
case eChatMessageType::PRG_CSR_COMMAND:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break; break;
}
default: default:
Game::logger->Log("ChatServer", "Unknown World id: %i", int(packet->data[3])); LOG("Unknown CHAT Message id: %i", chatMessageID);
}
} }
} }

View File

@@ -3,51 +3,57 @@
#include <iostream> #include <iostream>
#include <algorithm> #include <algorithm>
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
#include "ChatPacketHandler.h" #include "ChatPacketHandler.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "PacketUtils.h" #include "BitStreamUtils.h"
#include "Database.h" #include "Database.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "ChatPackets.h"
#include "dConfig.h"
#include "eChatMessageType.h"
PlayerContainer::PlayerContainer() { void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends =
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_best_friends")).value_or(m_MaxNumberOfBestFriends);
m_MaxNumberOfFriends =
GeneralUtils::TryParse<uint32_t>(Game::config->GetValue("max_number_of_friends")).value_or(m_MaxNumberOfFriends);
} }
PlayerContainer::~PlayerContainer() { TeamData::TeamData() {
mPlayers.clear(); lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
} }
void PlayerContainer::InsertPlayer(Packet* packet) { void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER; CINSTREAM_SKIP_HEADER;
PlayerData* data = new PlayerData(); LWOOBJID playerId;
inStream.Read(data->playerID); if (!inStream.Read(playerId)) {
LOG("Failed to read player ID");
return;
}
auto& data = m_Players[playerId];
data.playerID = playerId;
uint32_t len; uint32_t len;
inStream.Read<uint32_t>(len); inStream.Read<uint32_t>(len);
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char character; inStream.Read<char>(character); char character; inStream.Read<char>(character);
data->playerName += character; data.playerName += character;
} }
inStream.Read(data->zoneID); inStream.Read(data.zoneID);
inStream.Read(data->muteExpire); inStream.Read(data.muteExpire);
data->sysAddr = packet->systemAddress; inStream.Read(data.gmLevel);
data.sysAddr = packet->systemAddress;
mNames[data->playerID] = GeneralUtils::UTF8ToUTF16(data->playerName); m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
m_PlayerCount++;
mPlayers.insert(std::make_pair(data->playerID, data)); LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
Game::logger->Log("PlayerContainer", "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 (?, ?, ?, ?);"); Database::Get()->UpdateActivityLog(data.playerID, eActivityType::PlayerLoggedIn, data.zoneID.GetMapID());
insertLog->setInt(1, data->playerID);
insertLog->setInt(2, 0);
insertLog->setUInt64(3, time(nullptr));
insertLog->setInt(4, data->zoneID.GetMapID());
insertLog->executeUpdate();
} }
void PlayerContainer::RemovePlayer(Packet* packet) { void PlayerContainer::RemovePlayer(Packet* packet) {
@@ -56,42 +62,37 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
inStream.Read(playerID); inStream.Read(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.
std::unique_ptr<PlayerData> player(this->GetPlayerData(playerID)); const auto& player = GetPlayerData(playerID);
if (player == nullptr) { if (!player) {
LOG("Failed to find user: %llu", playerID);
return; return;
} }
for (auto& fr : player->friends) { for (const auto& fr : player.friends) {
auto fd = this->GetPlayerData(fr.friendID); const auto& fd = this->GetPlayerData(fr.friendID);
if (fd) ChatPacketHandler::SendFriendUpdate(fd, player.get(), 0, fr.isBestFriend); if (fd) ChatPacketHandler::SendFriendUpdate(fd, player, 0, fr.isBestFriend);
} }
auto* team = GetTeam(playerID); auto* team = GetTeam(playerID);
if (team != nullptr) { if (team != nullptr) {
const auto memberName = GeneralUtils::UTF8ToUTF16(std::string(player->playerName.c_str())); const auto memberName = GeneralUtils::UTF8ToUTF16(player.playerName);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue; if (!otherMember) continue;
ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 }); ChatPacketHandler::SendTeamSetOffWorldFlag(otherMember, playerID, { 0, 0, 0 });
} }
} }
Game::logger->Log("PlayerContainer", "Removed user: %llu", playerID); m_PlayerCount--;
mPlayers.erase(playerID); LOG("Removed user: %llu", playerID);
m_Players.erase(playerID);
auto* insertLog = Database::CreatePreppedStmt("INSERT INTO activity_log (character_id, activity, time, map_id) VALUES (?, ?, ?, ?);"); Database::Get()->UpdateActivityLog(playerID, eActivityType::PlayerLoggedOut, player.zoneID.GetMapID());
insertLog->setInt(1, playerID);
insertLog->setInt(2, 1);
insertLog->setUInt64(3, time(nullptr));
insertLog->setInt(4, player->zoneID.GetMapID());
insertLog->executeUpdate();
} }
void PlayerContainer::MuteUpdate(Packet* packet) { void PlayerContainer::MuteUpdate(Packet* packet) {
@@ -101,15 +102,15 @@ void PlayerContainer::MuteUpdate(Packet* packet) {
time_t expire = 0; time_t expire = 0;
inStream.Read(expire); inStream.Read(expire);
auto* player = this->GetPlayerData(playerID); auto& player = this->GetPlayerDataMutable(playerID);
if (player == nullptr) { if (!player) {
Game::logger->Log("PlayerContainer", "Failed to find user: %llu", playerID); LOG("Failed to find user: %llu", playerID);
return; return;
} }
player->muteExpire = expire; player.muteExpire = expire;
BroadcastMuteUpdate(playerID, expire); BroadcastMuteUpdate(playerID, expire);
} }
@@ -146,12 +147,12 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::GM_MUTE);
bitStream.Write(player); bitStream.Write(player);
bitStream.Write(time); bitStream.Write(time);
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
} }
TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) { TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
@@ -185,7 +186,7 @@ TeamData* PlayerContainer::CreateLocalTeam(std::vector<LWOOBJID> members) {
TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) { TeamData* PlayerContainer::CreateTeam(LWOOBJID leader, bool local) {
auto* team = new TeamData(); auto* team = new TeamData();
team->teamID = ++mTeamIDCounter; team->teamID = ++m_TeamIDCounter;
team->leaderID = leader; team->leaderID = leader;
team->local = local; team->local = local;
@@ -207,24 +208,32 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
} }
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) { void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4) {
LOG("Tried to add player to team that already had 4 players");
const auto& player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player.sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID); const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return; if (index != team->memberIDs.end()) return;
team->memberIDs.push_back(playerID); team->memberIDs.push_back(playerID);
auto* leader = GetPlayerData(team->leaderID); const auto& leader = GetPlayerData(team->leaderID);
auto* member = GetPlayerData(playerID); const auto& member = GetPlayerData(playerID);
if (leader == nullptr || member == nullptr) return; if (!leader || !member) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
const auto memberName = GeneralUtils::UTF8ToUTF16(member->playerName); const auto memberName = GeneralUtils::UTF8ToUTF16(member.playerName);
ChatPacketHandler::SendTeamInviteConfirm(member, false, leader->playerID, leader->zoneID, team->lootFlag, 0, 0, leaderName); ChatPacketHandler::SendTeamInviteConfirm(member, false, leader.playerID, leader.zoneID, team->lootFlag, 0, 0, leaderName);
if (!team->local) { if (!team->local) {
ChatPacketHandler::SendTeamSetLeader(member, leader->playerID); ChatPacketHandler::SendTeamSetLeader(member, leader.playerID);
} else { } else {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
} }
@@ -232,16 +241,16 @@ void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
UpdateTeamsOnWorld(team, false); UpdateTeamsOnWorld(team, false);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == member) continue; if (otherMember == member) continue;
const auto otherMemberName = GetName(memberId); const auto otherMemberName = GetName(memberId);
ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember != nullptr ? otherMember->zoneID : LWOZONEID(0, 0, 0)); ChatPacketHandler::SendTeamAddPlayer(member, false, team->local, false, memberId, otherMemberName, otherMember ? otherMember.zoneID : LWOZONEID(0, 0, 0));
if (otherMember != nullptr) { if (otherMember) {
ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member->playerID, memberName, member->zoneID); ChatPacketHandler::SendTeamAddPlayer(otherMember, false, team->local, false, member.playerID, memberName, member.zoneID);
} }
} }
} }
@@ -251,9 +260,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
if (index == team->memberIDs.end()) return; if (index == team->memberIDs.end()) return;
auto* member = GetPlayerData(playerID); const auto& member = GetPlayerData(playerID);
if (member != nullptr && !silent) { if (member && !silent) {
ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(member, LWOOBJID_EMPTY);
} }
@@ -264,9 +273,9 @@ void PlayerContainer::RemoveMember(TeamData* team, LWOOBJID playerID, bool disba
continue; continue;
} }
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue; if (!otherMember) continue;
ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, disband, kicked, leaving, false, team->leaderID, playerID, memberName);
} }
@@ -288,9 +297,9 @@ void PlayerContainer::PromoteMember(TeamData* team, LWOOBJID newLeader) {
team->leaderID = newLeader; team->leaderID = newLeader;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue; if (!otherMember) continue;
ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader); ChatPacketHandler::SendTeamSetLeader(otherMember, newLeader);
} }
@@ -302,14 +311,14 @@ void PlayerContainer::DisbandTeam(TeamData* team) {
if (index == mTeams.end()) return; if (index == mTeams.end()) return;
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue; if (!otherMember) continue;
const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember->playerName); const auto memberName = GeneralUtils::UTF8ToUTF16(otherMember.playerName);
ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY); ChatPacketHandler::SendTeamSetLeader(otherMember, LWOOBJID_EMPTY);
ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember->playerID, memberName); ChatPacketHandler::SendTeamRemovePlayer(otherMember, true, false, false, team->local, team->leaderID, otherMember.playerID, memberName);
} }
UpdateTeamsOnWorld(team, true); UpdateTeamsOnWorld(team, true);
@@ -324,19 +333,19 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
if (index == mTeams.end()) return; if (index == mTeams.end()) return;
auto* leader = GetPlayerData(team->leaderID); const auto& leader = GetPlayerData(team->leaderID);
if (leader == nullptr) return; if (!leader) return;
const auto leaderName = GeneralUtils::UTF8ToUTF16(leader->playerName); const auto leaderName = GeneralUtils::UTF8ToUTF16(leader.playerName);
for (const auto memberId : team->memberIDs) { for (const auto memberId : team->memberIDs) {
auto* otherMember = GetPlayerData(memberId); const auto& otherMember = GetPlayerData(memberId);
if (otherMember == nullptr) continue; if (!otherMember) continue;
if (!team->local) { if (!team->local) {
ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader->zoneID, team->lootFlag, 0, leaderName); ChatPacketHandler::SendTeamStatus(otherMember, team->leaderID, leader.zoneID, team->lootFlag, 0, leaderName);
} }
} }
@@ -345,40 +354,59 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::TEAM_GET_STATUS);
bitStream.Write(team->teamID); bitStream.Write(team->teamID);
bitStream.Write(deleteTeam); bitStream.Write(deleteTeam);
if (!deleteTeam) { if (!deleteTeam) {
bitStream.Write(team->lootFlag); bitStream.Write(team->lootFlag);
bitStream.Write(static_cast<char>(team->memberIDs.size())); bitStream.Write<char>(team->memberIDs.size());
for (const auto memberID : team->memberIDs) { for (const auto memberID : team->memberIDs) {
bitStream.Write(memberID); bitStream.Write(memberID);
} }
} }
Game::server->Send(&bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true);
} }
std::u16string PlayerContainer::GetName(LWOOBJID playerID) { std::u16string PlayerContainer::GetName(LWOOBJID playerID) {
const auto& pair = mNames.find(playerID); const auto iter = m_Names.find(playerID);
if (pair == mNames.end()) return u""; if (iter == m_Names.end()) return u"";
return pair->second; return iter->second;
} }
LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) { LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
for (const auto& pair : mNames) { LWOOBJID toReturn = LWOOBJID_EMPTY;
if (pair.second == playerName) {
return pair.first; for (const auto& [id, name] : m_Names) {
if (name == playerName) {
toReturn = id;
break;
} }
} }
return LWOOBJID_EMPTY; return toReturn;
} }
bool PlayerContainer::GetIsMuted(PlayerData* data) { PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
return data->muteExpire == 1 || data->muteExpire > time(NULL); return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
}
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {
for (auto& [id, player] : m_Players) {
if (!player) continue;
if (player.playerName == playerName) return player;
}
return m_Players[LWOOBJID_EMPTY];
}
const PlayerData& PlayerContainer::GetPlayerData(const LWOOBJID& playerID) {
return GetPlayerDataMutable(playerID);
}
const PlayerData& PlayerContainer::GetPlayerData(const std::string& playerName) {
return GetPlayerDataMutable(playerName);
} }

View File

@@ -7,17 +7,49 @@
#include "dServer.h" #include "dServer.h"
#include <unordered_map> #include <unordered_map>
struct PlayerData { enum class eGameMasterLevel : uint8_t;
LWOOBJID playerID;
struct IgnoreData {
IgnoreData(const std::string& name, const LWOOBJID& id) : playerName{ name }, playerId{ id } {}
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 = LWOOBJID_EMPTY;
std::string playerName; std::string playerName;
SystemAddress sysAddr; };
LWOZONEID zoneID;
std::vector<FriendData> friends; struct PlayerData {
time_t muteExpire; operator bool() const noexcept {
return playerID != LWOOBJID_EMPTY;
}
bool operator==(const PlayerData& other) const noexcept {
return playerID == other.playerID;
}
bool GetIsMuted() const {
return muteExpire == 1 || muteExpire > time(NULL);
}
SystemAddress sysAddr{};
LWOZONEID zoneID{};
LWOOBJID playerID = LWOOBJID_EMPTY;
time_t muteExpire = 0;
uint8_t countOfBestFriends = 0; uint8_t countOfBestFriends = 0;
std::string playerName;
std::vector<FriendData> friends;
std::vector<IgnoreData> ignoredPlayers;
eGameMasterLevel gmLevel = static_cast<eGameMasterLevel>(0); // CIVILLIAN
bool isFTP = false;
}; };
struct TeamData { struct TeamData {
TeamData();
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
LWOOBJID leaderID = LWOOBJID_EMPTY; LWOOBJID leaderID = LWOOBJID_EMPTY;
std::vector<LWOOBJID> memberIDs{}; std::vector<LWOOBJID> memberIDs{};
@@ -28,31 +60,20 @@ struct TeamData {
class PlayerContainer { class PlayerContainer {
public: public:
PlayerContainer(); void Initialize();
~PlayerContainer();
void InsertPlayer(Packet* packet); void InsertPlayer(Packet* packet);
void RemovePlayer(Packet* packet); void RemovePlayer(Packet* packet);
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);
PlayerData* GetPlayerData(const LWOOBJID& playerID) { const PlayerData& GetPlayerData(const LWOOBJID& playerID);
auto it = mPlayers.find(playerID); const PlayerData& GetPlayerData(const std::string& playerName);
if (it != mPlayers.end()) return it->second; PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
return nullptr; PlayerData& GetPlayerDataMutable(const std::string& playerName);
} uint32_t GetPlayerCount() { return m_PlayerCount; };
uint32_t GetSimCount() { return m_SimCount; };
PlayerData* GetPlayerData(const std::string& playerName) { const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
for (auto player : mPlayers) {
if (player.second) {
std::string pn = player.second->playerName.c_str();
if (pn == playerName) return player.second;
}
}
return nullptr;
}
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);
@@ -65,14 +86,17 @@ public:
void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam); void UpdateTeamsOnWorld(TeamData* team, bool deleteTeam);
std::u16string GetName(LWOOBJID playerID); std::u16string GetName(LWOOBJID playerID);
LWOOBJID GetId(const std::u16string& playerName); 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; }
private: private:
LWOOBJID mTeamIDCounter = 0; LWOOBJID m_TeamIDCounter = 0;
std::map<LWOOBJID, PlayerData*> mPlayers; std::map<LWOOBJID, PlayerData> m_Players;
std::vector<TeamData*> mTeams; 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;
uint32_t m_PlayerCount = 0;
uint32_t m_SimCount = 0;
}; };

View File

@@ -1,89 +1,90 @@
#include "AMFDeserialize.h" #include "AMFDeserialize.h"
#include "AMFFormat.h" #include <stdexcept>
#include "Amf3.h"
/** /**
* AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf * AMF3 Reference document https://rtmp.veriskope.com/pdf/amf3-file-format-spec.pdf
* AMF3 Deserializer written by EmosewaMC * AMF3 Deserializer written by EmosewaMC
*/ */
AMFValue* AMFDeserialize::Read(RakNet::BitStream* inStream) { AMFBaseValue* AMFDeserialize::Read(RakNet::BitStream& inStream) {
if (!inStream) return nullptr; AMFBaseValue* returnValue = nullptr;
AMFValue* returnValue = nullptr;
// Read in the value type from the bitStream // Read in the value type from the bitStream
int8_t 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 AMFValueType::AMFUndefined: { case eAmf::Undefined: {
returnValue = new AMFUndefinedValue(); returnValue = new AMFBaseValue();
break; break;
} }
case AMFValueType::AMFNull: { case eAmf::Null: {
returnValue = new AMFNullValue(); returnValue = new AMFNullValue();
break; break;
} }
case AMFValueType::AMFFalse: { case eAmf::False: {
returnValue = new AMFFalseValue(); returnValue = new AMFBoolValue(false);
break; break;
} }
case AMFValueType::AMFTrue: { case eAmf::True: {
returnValue = new AMFTrueValue(); returnValue = new AMFBoolValue(true);
break; break;
} }
case AMFValueType::AMFInteger: { case eAmf::Integer: {
returnValue = ReadAmfInteger(inStream); returnValue = ReadAmfInteger(inStream);
break; break;
} }
case AMFValueType::AMFDouble: { case eAmf::Double: {
returnValue = ReadAmfDouble(inStream); returnValue = ReadAmfDouble(inStream);
break; break;
} }
case AMFValueType::AMFString: { case eAmf::String: {
returnValue = ReadAmfString(inStream); returnValue = ReadAmfString(inStream);
break; break;
} }
case AMFValueType::AMFArray: { case eAmf::Array: {
returnValue = ReadAmfArray(inStream); returnValue = ReadAmfArray(inStream);
break; break;
} }
// TODO We do not need these values, but if someone wants to implement them // These values are unimplemented in the live client and will remain unimplemented
// then please do so and add the corresponding unit tests. // unless someone modifies the client to allow serializing of these values.
case AMFValueType::AMFXMLDoc: case eAmf::XMLDoc:
case AMFValueType::AMFDate: case eAmf::Date:
case AMFValueType::AMFObject: case eAmf::Object:
case AMFValueType::AMFXML: case eAmf::XML:
case AMFValueType::AMFByteArray: case eAmf::ByteArray:
case AMFValueType::AMFVectorInt: case eAmf::VectorInt:
case AMFValueType::AMFVectorUInt: case eAmf::VectorUInt:
case AMFValueType::AMFVectorDouble: case eAmf::VectorDouble:
case AMFValueType::AMFVectorObject: case eAmf::VectorObject:
case AMFValueType::AMFDictionary: { case eAmf::Dictionary: {
throw static_cast<AMFValueType>(marker); throw marker;
break; break;
} }
default: default:
throw static_cast<AMFValueType>(marker); throw std::invalid_argument("Invalid AMF3 marker" + std::to_string(static_cast<int32_t>(marker)));
break; break;
} }
return returnValue; return returnValue;
} }
uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) { uint32_t AMFDeserialize::ReadU29(RakNet::BitStream& inStream) {
bool byteFlag = true; bool byteFlag = true;
uint32_t actualNumber{}; uint32_t actualNumber{};
uint8_t numberOfBytesRead{}; uint8_t numberOfBytesRead{};
while (byteFlag && numberOfBytesRead < 4) { while (byteFlag && numberOfBytesRead < 4) {
uint8_t byte{}; uint8_t byte{};
inStream->Read(byte); inStream.Read(byte);
// Parse the byte // Parse the byte
if (numberOfBytesRead < 3) { if (numberOfBytesRead < 3) {
byteFlag = byte & static_cast<uint8_t>(1 << 7); byteFlag = byte & static_cast<uint8_t>(1 << 7);
@@ -99,7 +100,7 @@ uint32_t AMFDeserialize::ReadU29(RakNet::BitStream* inStream) {
return actualNumber; return actualNumber;
} }
std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) { const std::string AMFDeserialize::ReadString(RakNet::BitStream& inStream) {
auto length = ReadU29(inStream); auto length = ReadU29(inStream);
// Check if this is a reference // Check if this is a reference
bool isReference = length % 2 == 1; bool isReference = length % 2 == 1;
@@ -107,54 +108,45 @@ std::string AMFDeserialize::ReadString(RakNet::BitStream* inStream) {
length = length >> 1; length = length >> 1;
if (isReference) { if (isReference) {
std::string value(length, 0); std::string value(length, 0);
inStream->Read(&value[0], length); inStream.Read(&value[0], length);
// Empty strings are never sent by reference // Empty strings are never sent by reference
if (!value.empty()) accessedElements.push_back(value); if (!value.empty()) accessedElements.push_back(value);
return value; return value;
} else { } else {
// Length is a reference to a previous index - use that as the read in value // Length is a reference to a previous index - use that as the read in value
return accessedElements[length]; return accessedElements.at(length);
} }
} }
AMFValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream* inStream) { AMFBaseValue* AMFDeserialize::ReadAmfDouble(RakNet::BitStream& inStream) {
auto doubleValue = new AMFDoubleValue();
double value; double value;
inStream->Read<double>(value); inStream.Read<double>(value);
doubleValue->SetDoubleValue(value); return new AMFDoubleValue(value);
return doubleValue;
} }
AMFValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream* inStream) { AMFBaseValue* AMFDeserialize::ReadAmfArray(RakNet::BitStream& inStream) {
auto arrayValue = new AMFArrayValue(); auto arrayValue = new AMFArrayValue();
// Read size of dense array // Read size of dense array
auto sizeOfDenseArray = (ReadU29(inStream) >> 1); const auto sizeOfDenseArray = (ReadU29(inStream) >> 1);
// Then read associative portion
// Then read Key'd portion
while (true) { while (true) {
auto key = ReadString(inStream); const auto key = ReadString(inStream);
// No more values when we encounter an empty string // No more associative values when we encounter an empty string key
if (key.size() == 0) break; if (key.size() == 0) break;
arrayValue->InsertValue(key, Read(inStream)); arrayValue->Insert(key, Read(inStream));
} }
// Finally read dense portion // Finally read dense portion
for (uint32_t i = 0; i < sizeOfDenseArray; i++) { for (uint32_t i = 0; i < sizeOfDenseArray; i++) {
arrayValue->PushBackValue(Read(inStream)); arrayValue->Insert(i, Read(inStream));
} }
return arrayValue; return arrayValue;
} }
AMFValue* AMFDeserialize::ReadAmfString(RakNet::BitStream* inStream) { AMFBaseValue* AMFDeserialize::ReadAmfString(RakNet::BitStream& inStream) {
auto stringValue = new AMFStringValue(); return new AMFStringValue(ReadString(inStream));
stringValue->SetStringValue(ReadString(inStream));
return stringValue;
} }
AMFValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream* inStream) { AMFBaseValue* AMFDeserialize::ReadAmfInteger(RakNet::BitStream& inStream) {
auto integerValue = new AMFIntegerValue(); return new AMFIntValue(ReadU29(inStream));
integerValue->SetIntegerValue(ReadU29(inStream));
return integerValue;
} }

View File

@@ -5,7 +5,8 @@
#include <vector> #include <vector>
#include <string> #include <string>
class AMFValue; class AMFBaseValue;
class AMFDeserialize { class AMFDeserialize {
public: public:
/** /**
@@ -14,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.
*/ */
AMFValue* Read(RakNet::BitStream* inStream); 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
@@ -22,7 +23,7 @@ private:
* @param inStream bitstream to read data from * @param inStream bitstream to read data from
* @return The number as an unsigned 29 bit integer * @return The number as an unsigned 29 bit integer
*/ */
uint32_t ReadU29(RakNet::BitStream* inStream); static uint32_t ReadU29(RakNet::BitStream& inStream);
/** /**
* @brief Reads a string from a bitstream * @brief Reads a string from a bitstream
@@ -30,7 +31,7 @@ private:
* @param inStream bitStream to read data from * @param inStream bitStream to read data from
* @return The read string * @return The read string
*/ */
std::string ReadString(RakNet::BitStream* inStream); const std::string ReadString(RakNet::BitStream& inStream);
/** /**
* @brief Read an AMFDouble value from a bitStream * @brief Read an AMFDouble value from a bitStream
@@ -38,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
*/ */
AMFValue* ReadAmfDouble(RakNet::BitStream* inStream); AMFBaseValue* ReadAmfDouble(RakNet::BitStream& inStream);
/** /**
* @brief Read an AMFArray from a bitStream * @brief Read an AMFArray from a bitStream
@@ -46,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
*/ */
AMFValue* ReadAmfArray(RakNet::BitStream* inStream); AMFBaseValue* ReadAmfArray(RakNet::BitStream& inStream);
/** /**
* @brief Read an AMFString from a bitStream * @brief Read an AMFString from a bitStream
@@ -54,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
*/ */
AMFValue* ReadAmfString(RakNet::BitStream* inStream); AMFBaseValue* ReadAmfString(RakNet::BitStream& inStream);
/** /**
* @brief Read an AMFInteger from a bitStream * @brief Read an AMFInteger from a bitStream
@@ -62,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
*/ */
AMFValue* ReadAmfInteger(RakNet::BitStream* inStream); AMFBaseValue* 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

@@ -1,156 +0,0 @@
#include "AMFFormat.h"
// AMFInteger
void AMFIntegerValue::SetIntegerValue(uint32_t value) {
this->value = value;
}
uint32_t AMFIntegerValue::GetIntegerValue() {
return this->value;
}
// AMFDouble
void AMFDoubleValue::SetDoubleValue(double value) {
this->value = value;
}
double AMFDoubleValue::GetDoubleValue() {
return this->value;
}
// AMFString
void AMFStringValue::SetStringValue(const std::string& value) {
this->value = value;
}
std::string AMFStringValue::GetStringValue() {
return this->value;
}
// AMFXMLDoc
void AMFXMLDocValue::SetXMLDocValue(const std::string& value) {
this->xmlData = value;
}
std::string AMFXMLDocValue::GetXMLDocValue() {
return this->xmlData;
}
// AMFDate
void AMFDateValue::SetDateValue(uint64_t value) {
this->millisecondTimestamp = value;
}
uint64_t AMFDateValue::GetDateValue() {
return this->millisecondTimestamp;
}
// AMFArray Insert Value
void AMFArrayValue::InsertValue(const std::string& key, AMFValue* value) {
this->associative.insert(std::make_pair(key, value));
}
// AMFArray Remove Value
void AMFArrayValue::RemoveValue(const std::string& key) {
_AMFArrayMap_::iterator it = this->associative.find(key);
if (it != this->associative.end()) {
this->associative.erase(it);
}
}
// AMFArray Get Associative Iterator Begin
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueBegin() {
return this->associative.begin();
}
// AMFArray Get Associative Iterator End
_AMFArrayMap_::iterator AMFArrayValue::GetAssociativeIteratorValueEnd() {
return this->associative.end();
}
// AMFArray Push Back Value
void AMFArrayValue::PushBackValue(AMFValue* value) {
this->dense.push_back(value);
}
// AMFArray Pop Back Value
void AMFArrayValue::PopBackValue() {
this->dense.pop_back();
}
// AMFArray Get Dense List Size
uint32_t AMFArrayValue::GetDenseValueSize() {
return (uint32_t)this->dense.size();
}
// AMFArray Get Dense Iterator Begin
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorBegin() {
return this->dense.begin();
}
// AMFArray Get Dense Iterator End
_AMFArrayList_::iterator AMFArrayValue::GetDenseIteratorEnd() {
return this->dense.end();
}
AMFArrayValue::~AMFArrayValue() {
for (auto valueToDelete : GetDenseArray()) {
if (valueToDelete) delete valueToDelete;
}
for (auto valueToDelete : GetAssociativeMap()) {
if (valueToDelete.second) delete valueToDelete.second;
}
}
// AMFObject Constructor
AMFObjectValue::AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits) {
this->traits.reserve(traits.size());
std::vector<std::pair<std::string, AMFValueType>>::iterator it = traits.begin();
while (it != traits.end()) {
this->traits.insert(std::make_pair(it->first, std::make_pair(it->second, new AMFNullValue())));
it++;
}
}
// AMFObject Set Value
void AMFObjectValue::SetTraitValue(const std::string& trait, AMFValue* value) {
if (value) {
_AMFObjectTraits_::iterator it = this->traits.find(trait);
if (it != this->traits.end()) {
if (it->second.first == value->GetValueType()) {
it->second.second = value;
}
}
}
}
// AMFObject Get Value
AMFValue* AMFObjectValue::GetTraitValue(const std::string& trait) {
_AMFObjectTraits_::iterator it = this->traits.find(trait);
if (it != this->traits.end()) {
return it->second.second;
}
return nullptr;
}
// AMFObject Get Trait Iterator Begin
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorBegin() {
return this->traits.begin();
}
// AMFObject Get Trait Iterator End
_AMFObjectTraits_::iterator AMFObjectValue::GetTraitsIteratorEnd() {
return this->traits.end();
}
// AMFObject Get Trait Size
uint32_t AMFObjectValue::GetTraitArrayCount() {
return (uint32_t)this->traits.size();
}
AMFObjectValue::~AMFObjectValue() {
for (auto valueToDelete = GetTraitsIteratorBegin(); valueToDelete != GetTraitsIteratorEnd(); valueToDelete++) {
if (valueToDelete->second.second) delete valueToDelete->second.second;
}
}

View File

@@ -1,413 +0,0 @@
#pragma once
// Custom Classes
#include "dCommonVars.h"
// C++
#include <unordered_map>
#include <vector>
/*!
\file AMFFormat.hpp
\brief A class for managing AMF values
*/
class AMFValue; // Forward declaration
// Definitions
#define _AMFArrayMap_ std::unordered_map<std::string, AMFValue*>
#define _AMFArrayList_ std::vector<AMFValue*>
#define _AMFObjectTraits_ std::unordered_map<std::string, std::pair<AMFValueType, AMFValue*>>
#define _AMFObjectDynamicTraits_ std::unordered_map<std::string, AMFValue*>
//! An enum for each AMF value type
enum AMFValueType : unsigned char {
AMFUndefined = 0x00, //!< An undefined AMF Value
AMFNull = 0x01, //!< A null AMF value
AMFFalse = 0x02, //!< A false AMF value
AMFTrue = 0x03, //!< A true AMF value
AMFInteger = 0x04, //!< An integer AMF value
AMFDouble = 0x05, //!< A double AMF value
AMFString = 0x06, //!< A string AMF value
AMFXMLDoc = 0x07, //!< An XML Doc AMF value
AMFDate = 0x08, //!< A date AMF value
AMFArray = 0x09, //!< An array AMF value
AMFObject = 0x0A, //!< An object AMF value
AMFXML = 0x0B, //!< An XML AMF value
AMFByteArray = 0x0C, //!< A byte array AMF value
AMFVectorInt = 0x0D, //!< An integer vector AMF value
AMFVectorUInt = 0x0E, //!< An unsigned integer AMF value
AMFVectorDouble = 0x0F, //!< A double vector AMF value
AMFVectorObject = 0x10, //!< An object vector AMF value
AMFDictionary = 0x11 //!< A dictionary AMF value
};
//! An enum for the object value types
enum AMFObjectValueType : unsigned char {
AMFObjectAnonymous = 0x01,
AMFObjectTyped = 0x02,
AMFObjectDynamic = 0x03,
AMFObjectExternalizable = 0x04
};
//! The base AMF value class
class AMFValue {
public:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
virtual AMFValueType GetValueType() = 0;
virtual ~AMFValue() {};
};
//! A typedef for a pointer to an AMF value
typedef AMFValue* NDGFxValue;
// The various AMF value types
//! The undefined value AMF type
class AMFUndefinedValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFUndefined;
};
//! The null value AMF type
class AMFNullValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFNull;
};
//! The false value AMF type
class AMFFalseValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFFalse;
};
//! The true value AMF type
class AMFTrueValue : public AMFValue {
private:
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFTrue;
};
//! The integer value AMF type
class AMFIntegerValue : public AMFValue {
private:
uint32_t value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFInteger;
//! Sets the integer value
/*!
\param value The value to set
*/
void SetIntegerValue(uint32_t value);
//! Gets the integer value
/*!
\return The integer value
*/
uint32_t GetIntegerValue();
};
//! The double value AMF type
class AMFDoubleValue : public AMFValue {
private:
double value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFDouble;
//! Sets the double value
/*!
\param value The value to set to
*/
void SetDoubleValue(double value);
//! Gets the double value
/*!
\return The double value
*/
double GetDoubleValue();
};
//! The string value AMF type
class AMFStringValue : public AMFValue {
private:
std::string value; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFString;
//! Sets the string value
/*!
\param value The string value to set to
*/
void SetStringValue(const std::string& value);
//! Gets the string value
/*!
\return The string value
*/
std::string GetStringValue();
};
//! The XML doc value AMF type
class AMFXMLDocValue : public AMFValue {
private:
std::string xmlData; //!< The value of the AMF type
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFXMLDoc;
//! Sets the XML Doc value
/*!
\param value The value to set to
*/
void SetXMLDocValue(const std::string& value);
//! Gets the XML Doc value
/*!
\return The XML Doc value
*/
std::string GetXMLDocValue();
};
//! The date value AMF type
class AMFDateValue : public AMFValue {
private:
uint64_t millisecondTimestamp; //!< The time in milliseconds since the ephoch
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() { return ValueType; }
public:
static const AMFValueType ValueType = AMFDate;
//! Sets the date time
/*!
\param value The value to set to
*/
void SetDateValue(uint64_t value);
//! Gets the date value
/*!
\return The date value in milliseconds since the epoch
*/
uint64_t GetDateValue();
};
//! The array value AMF type
// This object will manage it's own memory map and list. Do not delete its values.
class AMFArrayValue : public AMFValue {
private:
_AMFArrayMap_ associative; //!< The array map (associative part)
_AMFArrayList_ dense; //!< The array list (dense part)
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() override { return ValueType; }
public:
static const AMFValueType ValueType = AMFArray;
~AMFArrayValue() override;
//! Inserts an item into the array map for a specific key
/*!
\param key The key to set
\param value The value to add
*/
void InsertValue(const std::string& key, AMFValue* value);
//! Removes an item for a specific key
/*!
\param key The key to remove
*/
void RemoveValue(const std::string& key);
//! Finds an AMF value
/*!
\return The AMF value if found, nullptr otherwise
*/
template <typename T>
T* FindValue(const std::string& key) const {
_AMFArrayMap_::const_iterator it = this->associative.find(key);
if (it != this->associative.end() && T::ValueType == it->second->GetValueType()) {
return dynamic_cast<T*>(it->second);
}
return nullptr;
};
//! Returns where the associative iterator begins
/*!
\return Where the array map iterator begins
*/
_AMFArrayMap_::iterator GetAssociativeIteratorValueBegin();
//! Returns where the associative iterator ends
/*!
\return Where the array map iterator ends
*/
_AMFArrayMap_::iterator GetAssociativeIteratorValueEnd();
//! Pushes back a value into the array list
/*!
\param value The value to push back
*/
void PushBackValue(AMFValue* value);
//! Pops back the last value in the array list
void PopBackValue();
//! Gets the count of the dense list
/*!
\return The dense list size
*/
uint32_t GetDenseValueSize();
//! Gets a specific value from the list for the specified index
/*!
\param index The index to get
*/
template <typename T>
T* GetValueAt(uint32_t index) {
if (index >= this->dense.size()) return nullptr;
AMFValue* foundValue = this->dense.at(index);
return T::ValueType == foundValue->GetValueType() ? dynamic_cast<T*>(foundValue) : nullptr;
};
//! Returns where the dense iterator begins
/*!
\return Where the iterator begins
*/
_AMFArrayList_::iterator GetDenseIteratorBegin();
//! Returns where the dense iterator ends
/*!
\return Where the iterator ends
*/
_AMFArrayList_::iterator GetDenseIteratorEnd();
//! Returns the associative map
/*!
\return The associative map
*/
_AMFArrayMap_ GetAssociativeMap() { return this->associative; };
//! Returns the dense array
/*!
\return The dense array
*/
_AMFArrayList_ GetDenseArray() { return this->dense; };
};
//! The anonymous object value AMF type
class AMFObjectValue : public AMFValue {
private:
_AMFObjectTraits_ traits; //!< The object traits
//! Returns the AMF value type
/*!
\return The AMF value type
*/
AMFValueType GetValueType() override { return ValueType; }
~AMFObjectValue() override;
public:
static const AMFValueType ValueType = AMFObject;
//! Constructor
/*!
\param traits The traits to set
*/
AMFObjectValue(std::vector<std::pair<std::string, AMFValueType>> traits);
//! Gets the object value type
/*!
\return The object value type
*/
virtual AMFObjectValueType GetObjectValueType() { return AMFObjectAnonymous; }
//! Sets the value of a trait
/*!
\param trait The trait to set the value for
\param value The AMF value to set
*/
void SetTraitValue(const std::string& trait, AMFValue* value);
//! Gets a trait value
/*!
\param trait The trait to get the value for
\return The trait value
*/
AMFValue* GetTraitValue(const std::string& trait);
//! Gets the beginning of the object traits iterator
/*!
\return The AMF trait array iterator begin
*/
_AMFObjectTraits_::iterator GetTraitsIteratorBegin();
//! Gets the end of the object traits iterator
/*!
\return The AMF trait array iterator begin
*/
_AMFObjectTraits_::iterator GetTraitsIteratorEnd();
//! Gets the amount of traits
/*!
\return The amount of traits
*/
uint32_t GetTraitArrayCount();
};

View File

@@ -1,259 +0,0 @@
#include "AMFFormat_BitStream.h"
// Writes an AMFValue pointer to a RakNet::BitStream
template<>
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value) {
if (value != nullptr) {
AMFValueType type = value->GetValueType();
switch (type) {
case AMFUndefined: {
AMFUndefinedValue* v = (AMFUndefinedValue*)value;
this->Write(*v);
break;
}
case AMFNull: {
AMFNullValue* v = (AMFNullValue*)value;
this->Write(*v);
break;
}
case AMFFalse: {
AMFFalseValue* v = (AMFFalseValue*)value;
this->Write(*v);
break;
}
case AMFTrue: {
AMFTrueValue* v = (AMFTrueValue*)value;
this->Write(*v);
break;
}
case AMFInteger: {
AMFIntegerValue* v = (AMFIntegerValue*)value;
this->Write(*v);
break;
}
case AMFDouble: {
AMFDoubleValue* v = (AMFDoubleValue*)value;
this->Write(*v);
break;
}
case AMFString: {
AMFStringValue* v = (AMFStringValue*)value;
this->Write(*v);
break;
}
case AMFXMLDoc: {
AMFXMLDocValue* v = (AMFXMLDocValue*)value;
this->Write(*v);
break;
}
case AMFDate: {
AMFDateValue* v = (AMFDateValue*)value;
this->Write(*v);
break;
}
case AMFArray: {
this->Write((AMFArrayValue*)value);
break;
}
case AMFObject:
case AMFXML:
case AMFByteArray:
case AMFVectorInt:
case AMFVectorUInt:
case AMFVectorDouble:
case AMFVectorObject:
case AMFDictionary:
break;
}
}
}
/**
* A private function to write an value to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteUInt29(RakNet::BitStream* bs, uint32_t v) {
unsigned char b4 = (unsigned char)v;
if (v < 0x00200000) {
b4 = b4 & 0x7F;
if (v > 0x7F) {
unsigned char b3;
v = v >> 7;
b3 = ((unsigned char)(v)) | 0x80;
if (v > 0x7F) {
unsigned char b2;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
bs->Write(b2);
}
bs->Write(b3);
}
} else {
unsigned char b1;
unsigned char b2;
unsigned char b3;
v = v >> 8;
b3 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b2 = ((unsigned char)(v)) | 0x80;
v = v >> 7;
b1 = ((unsigned char)(v)) | 0x80;
bs->Write(b1);
bs->Write(b2);
bs->Write(b3);
}
bs->Write(b4);
}
/**
* Writes a flag number to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteFlagNumber(RakNet::BitStream* bs, uint32_t v) {
v = (v << 1) | 0x01;
WriteUInt29(bs, v);
}
/**
* Writes an AMFString to a RakNet::BitStream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFString(RakNet::BitStream* bs, const std::string& str) {
WriteFlagNumber(bs, (uint32_t)str.size());
bs->Write(str.c_str(), (uint32_t)str.size());
}
/**
* Writes an U16 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU16(RakNet::BitStream* bs, uint16_t value) {
bs->Write(value);
}
/**
* Writes an U32 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU32(RakNet::BitStream* bs, uint32_t value) {
bs->Write(value);
}
/**
* Writes an U64 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU64(RakNet::BitStream* bs, uint64_t value) {
bs->Write(value);
}
// Writes an AMFUndefinedValue to BitStream
template<>
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value) {
this->Write(AMFUndefined);
}
// Writes an AMFNullValue to BitStream
template<>
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value) {
this->Write(AMFNull);
}
// Writes an AMFFalseValue to BitStream
template<>
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value) {
this->Write(AMFFalse);
}
// Writes an AMFTrueValue to BitStream
template<>
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value) {
this->Write(AMFTrue);
}
// Writes an AMFIntegerValue to BitStream
template<>
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value) {
this->Write(AMFInteger);
WriteUInt29(this, value.GetIntegerValue());
}
// Writes an AMFDoubleValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value) {
this->Write(AMFDouble);
double d = value.GetDoubleValue();
WriteAMFU64(this, *((unsigned long long*) & d));
}
// Writes an AMFStringValue to BitStream
template<>
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value) {
this->Write(AMFString);
std::string v = value.GetStringValue();
WriteAMFString(this, v);
}
// Writes an AMFXMLDocValue to BitStream
template<>
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value) {
this->Write(AMFXMLDoc);
std::string v = value.GetXMLDocValue();
WriteAMFString(this, v);
}
// Writes an AMFDateValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value) {
this->Write(AMFDate);
uint64_t date = value.GetDateValue();
WriteAMFU64(this, date);
}
// Writes an AMFArrayValue to BitStream
template<>
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value) {
this->Write(AMFArray);
uint32_t denseSize = value->GetDenseValueSize();
WriteFlagNumber(this, denseSize);
_AMFArrayMap_::iterator it = value->GetAssociativeIteratorValueBegin();
_AMFArrayMap_::iterator end = value->GetAssociativeIteratorValueEnd();
while (it != end) {
WriteAMFString(this, it->first);
this->Write(it->second);
it++;
}
this->Write(AMFNull);
if (denseSize > 0) {
_AMFArrayList_::iterator it2 = value->GetDenseIteratorBegin();
_AMFArrayList_::iterator end2 = value->GetDenseIteratorEnd();
while (it2 != end2) {
this->Write(*it2);
it2++;
}
}
}

View File

@@ -1,92 +0,0 @@
#pragma once
// Custom Classes
#include "AMFFormat.h"
// RakNet
#include <BitStream.h>
/*!
\file AMFFormat_BitStream.h
\brief A class that implements native writing of AMF values to RakNet::BitStream
*/
// We are using the RakNet namespace
namespace RakNet {
//! Writes an AMFValue pointer to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFValue*>(AMFValue* value);
//! Writes an AMFUndefinedValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFUndefinedValue>(AMFUndefinedValue value);
//! Writes an AMFNullValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFNullValue>(AMFNullValue value);
//! Writes an AMFFalseValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFFalseValue>(AMFFalseValue value);
//! Writes an AMFTrueValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFTrueValue>(AMFTrueValue value);
//! Writes an AMFIntegerValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFIntegerValue>(AMFIntegerValue value);
//! Writes an AMFDoubleValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDoubleValue>(AMFDoubleValue value);
//! Writes an AMFStringValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFStringValue>(AMFStringValue value);
//! Writes an AMFXMLDocValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFXMLDocValue>(AMFXMLDocValue value);
//! Writes an AMFDateValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDateValue>(AMFDateValue value);
//! Writes an AMFArrayValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFArrayValue*>(AMFArrayValue* value);
} // namespace RakNet

380
dCommon/Amf3.h Normal file
View File

@@ -0,0 +1,380 @@
#ifndef __AMF3__H__
#define __AMF3__H__
#include "dCommonVars.h"
#include "Logger.h"
#include "Game.h"
#include <unordered_map>
#include <vector>
enum class eAmf : uint8_t {
Undefined = 0x00, // An undefined AMF Value
Null = 0x01, // A null AMF value
False = 0x02, // A false AMF value
True = 0x03, // A true AMF value
Integer = 0x04, // An integer AMF value
Double = 0x05, // A double AMF value
String = 0x06, // A string AMF value
XMLDoc = 0x07, // Unused in the live client and cannot be serialized without modification. An XML Doc AMF value
Date = 0x08, // Unused in the live client and cannot be serialized without modification. A date AMF value
Array = 0x09, // An array AMF value
Object = 0x0A, // Unused in the live client and cannot be serialized without modification. An object AMF value
XML = 0x0B, // Unused in the live client and cannot be serialized without modification. An XML AMF value
ByteArray = 0x0C, // Unused in the live client and cannot be serialized without modification. A byte array AMF value
VectorInt = 0x0D, // Unused in the live client and cannot be serialized without modification. An integer vector AMF value
VectorUInt = 0x0E, // Unused in the live client and cannot be serialized without modification. An unsigned integer AMF value
VectorDouble = 0x0F, // Unused in the live client and cannot be serialized without modification. A double vector AMF value
VectorObject = 0x10, // Unused in the live client and cannot be serialized without modification. An object vector AMF value
Dictionary = 0x11 // Unused in the live client and cannot be serialized without modification. A dictionary AMF value
};
class AMFBaseValue {
public:
[[nodiscard]] constexpr virtual eAmf GetValueType() const noexcept { return eAmf::Undefined; }
constexpr AMFBaseValue() noexcept = default;
constexpr virtual ~AMFBaseValue() noexcept = default;
};
// AMFValue template class instantiations
template <typename ValueType>
class AMFValue : public AMFBaseValue {
public:
AMFValue() = default;
AMFValue(const ValueType value) : m_Data{ value } {}
virtual ~AMFValue() override = default;
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override;
[[nodiscard]] const ValueType& GetValue() const { return m_Data; }
void SetValue(const ValueType value) { m_Data = value; }
protected:
ValueType m_Data;
};
// Explicit template class instantiations
template class AMFValue<std::nullptr_t>;
template class AMFValue<bool>;
template class AMFValue<int32_t>;
template class AMFValue<uint32_t>;
template class AMFValue<std::string>;
template class AMFValue<double>;
// AMFValue template class member function instantiations
template <> [[nodiscard]] constexpr eAmf AMFValue<std::nullptr_t>::GetValueType() const noexcept { return eAmf::Null; }
template <> [[nodiscard]] constexpr eAmf AMFValue<bool>::GetValueType() const noexcept { return m_Data ? eAmf::True : eAmf::False; }
template <> [[nodiscard]] constexpr eAmf AMFValue<int32_t>::GetValueType() const noexcept { return eAmf::Integer; }
template <> [[nodiscard]] constexpr eAmf AMFValue<uint32_t>::GetValueType() const noexcept { return eAmf::Integer; }
template <> [[nodiscard]] constexpr eAmf AMFValue<std::string>::GetValueType() const noexcept { return eAmf::String; }
template <> [[nodiscard]] constexpr eAmf AMFValue<double>::GetValueType() const noexcept { return eAmf::Double; }
template <typename ValueType>
[[nodiscard]] constexpr eAmf AMFValue<ValueType>::GetValueType() const noexcept { return eAmf::Undefined; }
// As a string this is much easier to write and read from a BitStream.
template <>
class AMFValue<const char*> : public AMFBaseValue {
public:
AMFValue() = default;
AMFValue(const char* value) { m_Data = value; }
virtual ~AMFValue() override = default;
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::String; }
[[nodiscard]] const std::string& GetValue() const { return m_Data; }
void SetValue(const std::string& value) { m_Data = value; }
protected:
std::string m_Data;
};
using AMFNullValue = AMFValue<std::nullptr_t>;
using AMFBoolValue = AMFValue<bool>;
using AMFIntValue = AMFValue<int32_t>;
using AMFStringValue = AMFValue<std::string>;
using AMFDoubleValue = AMFValue<double>;
/**
* The AMFArrayValue object holds 2 types of lists:
* An associative list where a key maps to a value
* A Dense list where elements are stored back to back
*
* Objects that are Registered are owned by this object
* and are not to be deleted by a caller.
*/
class AMFArrayValue : public AMFBaseValue {
using AMFAssociative = std::unordered_map<std::string, AMFBaseValue*>;
using AMFDense = std::vector<AMFBaseValue*>;
public:
[[nodiscard]] constexpr eAmf GetValueType() const noexcept override { return eAmf::Array; }
~AMFArrayValue() override {
for (const auto* valueToDelete : GetDense()) {
if (valueToDelete) {
delete valueToDelete;
valueToDelete = nullptr;
}
}
for (auto valueToDelete : GetAssociative()) {
if (valueToDelete.second) {
delete valueToDelete.second;
valueToDelete.second = nullptr;
}
}
}
/**
* Returns the Associative portion of the object
*/
[[nodiscard]] inline const AMFAssociative& GetAssociative() const noexcept { return m_Associative; }
/**
* Returns the dense portion of the object
*/
[[nodiscard]] inline const AMFDense& GetDense() const noexcept { return m_Dense; }
/**
* Inserts an AMFValue into the associative portion with the given key.
* If a duplicate is attempted to be inserted, it is ignored and the
* first value with that key is kept in the map.
*
* These objects are not to be deleted by the caller as they are owned by
* the AMFArray object which manages its own memory.
*
* @param key The key to associate with the value
* @param value The value to insert
*
* @return The inserted element if the type matched,
* or nullptr if a key existed and was not the same type
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const std::string& key, const ValueType value) {
const auto element = m_Associative.find(key);
AMFValue<ValueType>* val = nullptr;
bool found = true;
if (element == m_Associative.cend()) {
val = new AMFValue<ValueType>(value);
m_Associative.emplace(key, val);
} else {
val = dynamic_cast<AMFValue<ValueType>*>(element->second);
found = false;
}
return std::make_pair(val, found);
}
// Associates an array with a string key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const std::string& key) {
const auto element = m_Associative.find(key);
AMFArrayValue* val = nullptr;
bool found = true;
if (element == m_Associative.cend()) {
val = new AMFArrayValue();
m_Associative.emplace(key, val);
} else {
val = dynamic_cast<AMFArrayValue*>(element->second);
found = false;
}
return std::make_pair(val, found);
}
// Associates an array with an integer key
[[maybe_unused]] std::pair<AMFBaseValue*, bool> Insert(const size_t index) {
AMFArrayValue* val = nullptr;
bool inserted = false;
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
val = new AMFArrayValue();
m_Dense.at(index) = val;
inserted = true;
}
return std::make_pair(dynamic_cast<AMFArrayValue*>(m_Dense.at(index)), inserted);
}
/**
* @brief Inserts an AMFValue into the AMFArray key'd by index.
* Attempting to insert the same key to the same value twice overwrites
* the previous value with the new one.
*
* @param index The index to associate with the value
* @param value The value to insert
* @return The inserted element, or nullptr if the type did not match
* what was at the index.
*/
template <typename ValueType>
[[maybe_unused]] std::pair<AMFValue<ValueType>*, bool> Insert(const size_t index, const ValueType value) {
AMFValue<ValueType>* val = nullptr;
bool inserted = false;
if (index >= m_Dense.size()) {
m_Dense.resize(index + 1);
val = new AMFValue<ValueType>(value);
m_Dense.at(index) = val;
inserted = true;
}
return std::make_pair(dynamic_cast<AMFValue<ValueType>*>(m_Dense.at(index)), inserted);
}
/**
* Inserts an AMFValue into the associative portion with the given key.
* If a duplicate is attempted to be inserted, it replaces the original
*
* The inserted element is now owned by this object and is not to be deleted
*
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const std::string& key, AMFBaseValue* const value) {
const auto element = m_Associative.find(key);
if (element != m_Associative.cend() && element->second) {
delete element->second;
element->second = value;
} else {
m_Associative.emplace(key, value);
}
}
/**
* Inserts an AMFValue into the associative portion with the given index.
* If a duplicate is attempted to be inserted, it replaces the original
*
* The inserted element is now owned by this object and is not to be deleted
*
* @param key The key to associate with the value
* @param value The value to insert
*/
void Insert(const size_t index, AMFBaseValue* const value) {
if (index < m_Dense.size()) {
const AMFDense::const_iterator itr = m_Dense.cbegin() + index;
if (*itr) delete m_Dense.at(index);
} else {
m_Dense.resize(index + 1);
}
m_Dense.at(index) = value;
}
/**
* Pushes an AMFValue into the back of the dense portion.
*
* These objects are not to be deleted by the caller as they are owned by
* the AMFArray object which manages its own memory.
*
* @param value The value to insert
*
* @return The inserted pointer, or nullptr should the key already be in use.
*/
template <typename ValueType>
[[maybe_unused]] inline AMFValue<ValueType>* Push(const ValueType value) {
return Insert(m_Dense.size(), value).first;
}
/**
* Removes the key from the associative portion
*
* The pointer removed is now no longer managed by this container
*
* @param key The key to remove from the associative portion
*/
void Remove(const std::string& key, const bool deleteValue = true) {
const AMFAssociative::const_iterator it = m_Associative.find(key);
if (it != m_Associative.cend()) {
if (deleteValue) delete it->second;
m_Associative.erase(it);
}
}
/**
* Pops the last element in the dense portion, deleting it in the process.
*/
void Remove(const size_t index) {
if (!m_Dense.empty() && index < m_Dense.size()) {
const auto itr = m_Dense.cbegin() + index;
if (*itr) delete (*itr);
m_Dense.erase(itr);
}
}
void Pop() {
if (!m_Dense.empty()) Remove(m_Dense.size() - 1);
}
[[nodiscard]] AMFArrayValue* GetArray(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ? dynamic_cast<AMFArrayValue*>(it->second) : nullptr;
}
[[nodiscard]] AMFArrayValue* GetArray(const size_t index) const {
return index < m_Dense.size() ? dynamic_cast<AMFArrayValue*>(m_Dense.at(index)) : nullptr;
}
[[maybe_unused]] inline AMFArrayValue* InsertArray(const std::string& key) {
return static_cast<AMFArrayValue*>(Insert(key).first);
}
[[maybe_unused]] inline AMFArrayValue* InsertArray(const size_t index) {
return static_cast<AMFArrayValue*>(Insert(index).first);
}
[[maybe_unused]] inline AMFArrayValue* PushArray() {
return static_cast<AMFArrayValue*>(Insert(m_Dense.size()).first);
}
/**
* Gets an AMFValue by the key from the associative portion and converts it
* to the AmfValue template type. If the key did not exist, it is inserted.
*
* @tparam The target object type
* @param key The key to lookup
*
* @return The AMFValue
*/
template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ?
dynamic_cast<AMFValue<AmfType>*>(it->second) :
nullptr;
}
// Get from the array but dont cast it
[[nodiscard]] AMFBaseValue* Get(const std::string& key) const {
const AMFAssociative::const_iterator it = m_Associative.find(key);
return it != m_Associative.cend() ? it->second : nullptr;
}
/**
* @brief Get an AMFValue object at a position in the dense portion.
* Gets an AMFValue by the index from the dense portion and converts it
* to the AmfValue template type. If the index did not exist, it is inserted.
*
* @tparam The target object type
* @param index The index to get
* @return The casted object, or nullptr.
*/
template <typename AmfType>
[[nodiscard]] AMFValue<AmfType>* Get(const size_t index) const {
return index < m_Dense.size() ?
dynamic_cast<AMFValue<AmfType>*>(m_Dense.at(index)) :
nullptr;
}
// Get from the dense but dont cast it
[[nodiscard]] AMFBaseValue* Get(const size_t index) const {
return index < m_Dense.size() ? m_Dense.at(index) : nullptr;
}
private:
/**
* The associative portion. These values are key'd with strings to an AMFValue.
*/
AMFAssociative m_Associative;
/**
* The dense portion. These AMFValue's are stored one after
* another with the most recent addition being at the back.
*/
AMFDense m_Dense;
};
#endif //!__AMF3__H__

184
dCommon/AmfSerialize.cpp Normal file
View File

@@ -0,0 +1,184 @@
#include "AmfSerialize.h"
#include "Game.h"
#include "Logger.h"
// Writes an AMFValue pointer to a RakNet::BitStream
template<>
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value) {
eAmf type = value.GetValueType();
this->Write(type);
switch (type) {
case eAmf::Integer: {
this->Write<AMFIntValue&>(*static_cast<AMFIntValue*>(&value));
break;
}
case eAmf::Double: {
this->Write<AMFDoubleValue&>(*static_cast<AMFDoubleValue*>(&value));
break;
}
case eAmf::String: {
this->Write<AMFStringValue&>(*static_cast<AMFStringValue*>(&value));
break;
}
case eAmf::Array: {
this->Write<AMFArrayValue&>(*static_cast<AMFArrayValue*>(&value));
break;
}
default: {
LOG("Encountered unwritable AMFType %i!", type);
}
case eAmf::Undefined:
case eAmf::Null:
case eAmf::False:
case eAmf::True:
case eAmf::Date:
case eAmf::Object:
case eAmf::XML:
case eAmf::XMLDoc:
case eAmf::ByteArray:
case eAmf::VectorInt:
case eAmf::VectorUInt:
case eAmf::VectorDouble:
case eAmf::VectorObject:
case eAmf::Dictionary:
break;
}
}
/**
* A private function to write an value to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteUInt29(RakNet::BitStream& bs, uint32_t v) {
unsigned char b4 = static_cast<unsigned char>(v);
if (v < 0x00200000) {
b4 = b4 & 0x7F;
if (v > 0x7F) {
unsigned char b3;
v = v >> 7;
b3 = static_cast<unsigned char>(v) | 0x80;
if (v > 0x7F) {
unsigned char b2;
v = v >> 7;
b2 = static_cast<unsigned char>(v) | 0x80;
bs.Write(b2);
}
bs.Write(b3);
}
} else {
unsigned char b1;
unsigned char b2;
unsigned char b3;
v = v >> 8;
b3 = static_cast<unsigned char>(v) | 0x80;
v = v >> 7;
b2 = static_cast<unsigned char>(v) | 0x80;
v = v >> 7;
b1 = static_cast<unsigned char>(v) | 0x80;
bs.Write(b1);
bs.Write(b2);
bs.Write(b3);
}
bs.Write(b4);
}
/**
* Writes a flag number to a RakNet::BitStream
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteFlagNumber(RakNet::BitStream& bs, uint32_t v) {
v = (v << 1) | 0x01;
WriteUInt29(bs, v);
}
/**
* Writes an AMFString to a RakNet::BitStream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFString(RakNet::BitStream& bs, const std::string& str) {
WriteFlagNumber(bs, static_cast<uint32_t>(str.size()));
bs.Write(str.c_str(), static_cast<uint32_t>(str.size()));
}
/**
* Writes an U16 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU16(RakNet::BitStream& bs, uint16_t value) {
bs.Write(value);
}
/**
* Writes an U32 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU32(RakNet::BitStream& bs, uint32_t value) {
bs.Write(value);
}
/**
* Writes an U64 to a bitstream
*
* RakNet writes in the correct byte order - do not reverse this.
*/
void WriteAMFU64(RakNet::BitStream& bs, uint64_t value) {
bs.Write(value);
}
// Writes an AMFIntegerValue to BitStream
template<>
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value) {
WriteUInt29(*this, value.GetValue());
}
// Writes an AMFDoubleValue to BitStream
template<>
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value) {
double d = value.GetValue();
WriteAMFU64(*this, *reinterpret_cast<uint64_t*>(&d));
}
// Writes an AMFStringValue to BitStream
template<>
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value) {
WriteAMFString(*this, value.GetValue());
}
// Writes an AMFArrayValue to BitStream
template<>
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value) {
uint32_t denseSize = value.GetDense().size();
WriteFlagNumber(*this, denseSize);
auto it = value.GetAssociative().begin();
auto end = value.GetAssociative().end();
while (it != end) {
WriteAMFString(*this, it->first);
this->Write<AMFBaseValue&>(*it->second);
it++;
}
this->Write(eAmf::Null);
if (denseSize > 0) {
auto it2 = value.GetDense().begin();
auto end2 = value.GetDense().end();
while (it2 != end2) {
this->Write<AMFBaseValue&>(**it2);
it2++;
}
}
}

50
dCommon/AmfSerialize.h Normal file
View File

@@ -0,0 +1,50 @@
#pragma once
// Custom Classes
#include "Amf3.h"
// RakNet
#include "BitStream.h"
/*!
\file AmfSerialize.h
\brief A class that implements native writing of AMF values to RakNet::BitStream
*/
// We are using the RakNet namespace
namespace RakNet {
//! Writes an AMFValue pointer to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFBaseValue&>(AMFBaseValue& value);
//! Writes an AMFIntegerValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFIntValue&>(AMFIntValue& value);
//! Writes an AMFDoubleValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFDoubleValue&>(AMFDoubleValue& value);
//! Writes an AMFStringValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFStringValue&>(AMFStringValue& value);
//! Writes an AMFArrayValue to a RakNet::BitStream
/*!
\param value The value to write
*/
template <>
void RakNet::BitStream::Write<AMFArrayValue&>(AMFArrayValue& value);
} // namespace RakNet

View File

@@ -1,14 +1,6 @@
#include "BinaryIO.h" #include "BinaryIO.h"
#include <string> #include <string>
void BinaryIO::WriteString(const std::string& stringToWrite, std::ofstream& outstream) {
//BinaryWrite(outstream, uint32_t(stringToWrite.length()));
for (size_t i = 0; i < size_t(stringToWrite.length()); ++i) {
BinaryIO::BinaryWrite(outstream, stringToWrite[i]);
}
}
//For reading null-terminated strings //For reading null-terminated strings
std::string BinaryIO::ReadString(std::istream& instream) { std::string BinaryIO::ReadString(std::istream& instream) {
std::string toReturn; std::string toReturn;
@@ -23,36 +15,3 @@ std::string BinaryIO::ReadString(std::istream& instream) {
return toReturn; return toReturn;
} }
//For reading strings of a specific size
std::string BinaryIO::ReadString(std::istream& instream, size_t size) {
std::string toReturn;
char buffer;
for (size_t i = 0; i < size; ++i) {
BinaryIO::BinaryRead(instream, buffer);
toReturn += buffer;
}
return toReturn;
}
std::string BinaryIO::ReadWString(std::istream& instream) {
size_t size;
BinaryRead(instream, size);
//toReturn.resize(size);
std::string test;
unsigned char buf;
for (size_t i = 0; i < size; ++i) {
//instream.ignore(1);
BinaryRead(instream, buf);
test += buf;
}
//printf("%s\n", test.c_str());
//instream.read((char*)&toReturn[0], size * 2);
//std::string str(toReturn.begin(), toReturn.end());
return test;
}

View File

@@ -1,8 +1,17 @@
#pragma once #pragma once
#ifndef __BINARYIO__H__
#define __BINARYIO__H__
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <string>
#include "Game.h"
#include "Logger.h"
namespace BinaryIO { namespace BinaryIO {
template<typename T> template<typename T>
std::ostream& BinaryWrite(std::ostream& stream, const T& value) { std::ostream& BinaryWrite(std::ostream& stream, const T& value) {
return stream.write(reinterpret_cast<const char*>(&value), sizeof(T)); return stream.write(reinterpret_cast<const char*>(&value), sizeof(T));
@@ -15,13 +24,51 @@ namespace BinaryIO {
return stream.read(reinterpret_cast<char*>(&value), sizeof(T)); return stream.read(reinterpret_cast<char*>(&value), sizeof(T));
} }
void WriteString(const std::string& stringToWrite, std::ofstream& outstream); enum class ReadType : int8_t {
WideString = 0,
String = 1,
};
template<typename SizeType>
inline void ReadString(std::istream& stream, std::u16string& value) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
stream.read(reinterpret_cast<char*>(value.data()), size * sizeof(uint16_t));
}
template<typename SizeType>
inline void ReadString(std::istream& stream, std::string& value, ReadType readType) {
static_assert(std::is_integral<SizeType>::value, "SizeType must be an integral type.");
SizeType size;
BinaryRead(stream, size);
if (!stream.good()) throw std::runtime_error("Failed to read from istream.");
value.resize(size);
if (readType == ReadType::WideString) {
uint16_t wideChar;
// Faster to do this than to read a u16string and convert it to a string since we only go through allocator once
for (SizeType i = 0; i < size; ++i) {
BinaryRead(stream, wideChar);
value[i] = static_cast<char>(wideChar);
}
} else {
stream.read(value.data(), size);
}
}
std::string ReadString(std::istream& instream); std::string ReadString(std::istream& instream);
std::string ReadString(std::istream& instream, size_t size);
std::string ReadWString(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();
} }
} }
#endif //!__BINARYIO__H__

View File

@@ -9,13 +9,12 @@
#include "Database.h" #include "Database.h"
#include "Game.h" #include "Game.h"
#include "ZCompression.h" #include "ZCompression.h"
#include "dLogger.h" #include "Logger.h"
//! Forward declarations //! Forward declarations
std::unique_ptr<sql::ResultSet> GetModelsFromDatabase();
void WriteSd0Magic(char* input, uint32_t chunkSize); 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. * @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 BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
uint32_t modelsTruncated{}; uint32_t modelsTruncated{};
auto modelsToTruncate = GetModelsFromDatabase(); auto modelsToTruncate = Database::Get()->GetAllUgcModels();
bool previousCommitValue = Database::GetAutoCommit(); bool previousCommitValue = Database::Get()->GetAutoCommit();
Database::SetAutoCommit(false); Database::Get()->SetAutoCommit(false);
while (modelsToTruncate->next()) { for (auto& model : modelsToTruncate) {
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 = ?;"));
std::string completeUncompressedModel{}; std::string completeUncompressedModel{};
uint32_t chunkCount{}; 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. // Check that header is sd0 by checking for the sd0 magic.
if (CheckSd0Magic(modelAsSd0.get())) { if (CheckSd0Magic(model.lxfmlData)) {
while (true) { while (true) {
uint32_t chunkSize{}; 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. // 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]); std::unique_ptr<uint8_t[]> compressedChunk(new uint8_t[chunkSize]);
for (uint32_t i = 0; i < chunkSize; i++) { 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. // Ignore the valgrind warning about uninitialized values. These are discarded later when we know the actual uncompressed size.
@@ -56,17 +51,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
if (actualUncompressedSize != -1) { if (actualUncompressedSize != -1) {
uint32_t previousSize = completeUncompressedModel.size(); uint32_t previousSize = completeUncompressedModel.size();
completeUncompressedModel.append((char*)uncompressedChunk.get()); completeUncompressedModel.append(reinterpret_cast<char*>(uncompressedChunk.get()));
completeUncompressedModel.resize(previousSize + actualUncompressedSize); completeUncompressedModel.resize(previousSize + actualUncompressedSize);
} else { } else {
Game::logger->Log("BrickByBrickFix", "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; break;
} }
chunkCount++; chunkCount++;
} }
std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>(); std::unique_ptr<tinyxml2::XMLDocument> document = std::make_unique<tinyxml2::XMLDocument>();
if (!document) { if (!document) {
Game::logger->Log("BrickByBrickFix", "Failed to initialize tinyxml document. Aborting."); LOG("Failed to initialize tinyxml document. Aborting.");
return 0; return 0;
} }
@@ -75,28 +70,20 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
"</LXFML>", "</LXFML>",
completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos completeUncompressedModel.length() >= 15 ? completeUncompressedModel.length() - 15 : 0) == std::string::npos
) { ) {
Game::logger->Log("BrickByBrickFix", LOG("Brick-by-brick model %llu will be deleted!", model.id);
"Brick-by-brick model %llu will be deleted!", modelId); Database::Get()->DeleteUgcModelData(model.id);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
modelsTruncated++; modelsTruncated++;
} }
} }
} else { } else {
Game::logger->Log("BrickByBrickFix", LOG("Brick-by-brick model %llu will be deleted!", model.id);
"Brick-by-brick model %llu will be deleted!", modelId); Database::Get()->DeleteUgcModelData(model.id);
ugcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
pcModelToDelete->setInt64(1, modelsToTruncate->getInt64(1));
ugcModelToDelete->execute();
pcModelToDelete->execute();
modelsTruncated++; modelsTruncated++;
} }
} }
Database::Commit(); Database::Get()->Commit();
Database::SetAutoCommit(previousCommitValue); Database::Get()->SetAutoCommit(previousCommitValue);
return modelsTruncated; return modelsTruncated;
} }
@@ -108,21 +95,17 @@ uint32_t BrickByBrickFix::TruncateBrokenBrickByBrickXml() {
*/ */
uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() { uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
uint32_t updatedModels = 0; uint32_t updatedModels = 0;
auto modelsToUpdate = GetModelsFromDatabase(); auto modelsToUpdate = Database::Get()->GetAllUgcModels();
auto previousAutoCommitState = Database::GetAutoCommit(); auto previousAutoCommitState = Database::Get()->GetAutoCommit();
Database::SetAutoCommit(false); Database::Get()->SetAutoCommit(false);
std::unique_ptr<sql::PreparedStatement> insertionStatement(Database::CreatePreppedStmt("UPDATE ugc SET lxfml = ? WHERE id = ?;")); for (auto& model : modelsToUpdate) {
while (modelsToUpdate->next()) {
int64_t modelId = modelsToUpdate->getInt64(1);
std::unique_ptr<sql::Blob> oldLxfml(modelsToUpdate->getBlob(2));
// Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib) // Check if the stored blob starts with zlib magic (0x78 0xDA - best compression of zlib)
// If it does, convert it to sd0. // 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. // Get and save size of zlib compressed chunk.
oldLxfml->seekg(0, std::ios::end); model.lxfmlData.seekg(0, std::ios::end);
uint32_t oldLxfmlSize = static_cast<uint32_t>(oldLxfml->tellg()); uint32_t oldLxfmlSize = static_cast<uint32_t>(model.lxfmlData.tellg());
oldLxfml->seekg(0); model.lxfmlData.seekg(0);
// Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size. // Allocate 9 extra bytes. 5 for sd0 magic, 4 for the only zlib compressed size.
uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9; uint32_t oldLxfmlSizeWithHeader = oldLxfmlSize + 9;
@@ -130,36 +113,27 @@ uint32_t BrickByBrickFix::UpdateBrickByBrickModelsToSd0() {
WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize); WriteSd0Magic(sd0ConvertedModel.get(), oldLxfmlSize);
for (uint32_t i = 9; i < oldLxfmlSizeWithHeader; i++) { 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::string outputString(sd0ConvertedModel.get(), oldLxfmlSizeWithHeader);
std::istringstream outputStringStream(outputString); std::istringstream outputStringStream(outputString);
insertionStatement->setBlob(1, static_cast<std::istream*>(&outputStringStream));
insertionStatement->setInt64(2, modelId);
try { try {
insertionStatement->executeUpdate(); Database::Get()->UpdateUgcModelData(model.id, outputStringStream);
Game::logger->Log("BrickByBrickFix", "Updated model %i to sd0", modelId); LOG("Updated model %i to sd0", model.id);
updatedModels++; updatedModels++;
} catch (sql::SQLException exception) { } catch (sql::SQLException exception) {
Game::logger->Log( LOG("Failed to update model %i. This model should be inspected manually to see why."
"BrickByBrickFix", "The database error is %s", model.id, exception.what());
"Failed to update model %i. This model should be inspected manually to see why."
"The database error is %s", modelId, exception.what());
} }
} }
} }
Database::Commit(); Database::Get()->Commit();
Database::SetAutoCommit(previousAutoCommitState); Database::Get()->SetAutoCommit(previousAutoCommitState);
return updatedModels; 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* * @brief Writes sd0 magic at the front of a char*
* *
@@ -175,6 +149,6 @@ void WriteSd0Magic(char* input, uint32_t chunkSize) {
*reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array *reinterpret_cast<uint32_t*>(input + 5) = chunkSize; // Write the integer to the character array
} }
bool CheckSd0Magic(sql::Blob* streamToCheck) { bool CheckSd0Magic(std::istream& streamToCheck) {
return streamToCheck->get() == 's' && streamToCheck->get() == 'd' && streamToCheck->get() == '0' && streamToCheck->get() == 0x01 && streamToCheck->get() == 0xFF; return streamToCheck.get() == 's' && streamToCheck.get() == 'd' && streamToCheck.get() == '0' && streamToCheck.get() == 0x01 && streamToCheck.get() == 0xFF;
} }

View File

@@ -1,35 +1,44 @@
set(DCOMMON_SOURCES "AMFFormat.cpp" set(DCOMMON_SOURCES
"AMFDeserialize.cpp" "AMFDeserialize.cpp"
"AMFFormat_BitStream.cpp" "AmfSerialize.cpp"
"BinaryIO.cpp" "BinaryIO.cpp"
"dConfig.cpp" "dConfig.cpp"
"Diagnostics.cpp" "Diagnostics.cpp"
"dLogger.cpp" "Logger.cpp"
"Game.cpp"
"GeneralUtils.cpp" "GeneralUtils.cpp"
"LDFFormat.cpp" "LDFFormat.cpp"
"MD5.cpp"
"Metrics.cpp" "Metrics.cpp"
"NiPoint3.cpp" "NiPoint3.cpp"
"NiQuaternion.cpp" "NiQuaternion.cpp"
"SHA512.cpp" "Demangler.cpp"
"Type.cpp"
"ZCompression.cpp" "ZCompression.cpp"
"BrickByBrickFix.cpp" "BrickByBrickFix.cpp"
"BinaryPathFinder.cpp" "BinaryPathFinder.cpp"
"FdbToSqlite.cpp" "FdbToSqlite.cpp"
) )
# Workaround for compiler bug where the optimized code could result in a memcpy of 0 bytes, even though that isnt possible.
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97185
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
set_source_files_properties("FdbToSqlite.cpp" PROPERTIES COMPILE_FLAGS "-Wno-stringop-overflow")
endif()
add_subdirectory(dClient) add_subdirectory(dClient)
foreach(file ${DCOMMON_DCLIENT_SOURCES}) foreach(file ${DCOMMON_DCLIENT_SOURCES})
set(DCOMMON_SOURCES ${DCOMMON_SOURCES} "dClient/${file}") set(DCOMMON_SOURCES ${DCOMMON_SOURCES} "dClient/${file}")
endforeach() endforeach()
include_directories(${PROJECT_SOURCE_DIR}/dCommon/)
add_library(dCommon STATIC ${DCOMMON_SOURCES}) add_library(dCommon STATIC ${DCOMMON_SOURCES})
target_include_directories(dCommon
target_link_libraries(dCommon bcrypt dDatabase tinyxml2) PUBLIC "." "dClient" "dEnums"
PRIVATE
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
)
if (UNIX) if (UNIX)
find_package(ZLIB REQUIRED) find_package(ZLIB REQUIRED)
@@ -60,4 +69,6 @@ else ()
) )
endif () endif ()
target_link_libraries(dCommon ZLIB::ZLIB) target_link_libraries(dCommon
PRIVATE ZLIB::ZLIB bcrypt tinyxml2
INTERFACE dDatabase)

29
dCommon/Demangler.cpp Normal file
View File

@@ -0,0 +1,29 @@
#include "Demangler.h"
#ifdef __GNUG__
#include <cstdlib>
#include <cxxabi.h>
#include <memory>
#include <typeinfo>
std::string Demangler::Demangle(const char* name) {
// some arbitrary value to eliminate the compiler warning
// -4 is not a valid return value for __cxa_demangle so we'll use that.
int status = -4;
// __cxa_demangle requires that we free the returned char*
std::unique_ptr<char, void (*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : "";
}
#else // __GNUG__
// does nothing if not g++
std::string Demangler::Demangle(const char* name) {
return name;
}
#endif // __GNUG__

9
dCommon/Demangler.h Normal file
View File

@@ -0,0 +1,9 @@
#pragma once
#include <string>
namespace Demangler {
// Given a char* containing a mangled name, return a std::string containing the demangled name.
// If the function fails for any reason, it returns an empty string.
std::string Demangle(const char* name);
}

View File

@@ -1,6 +1,6 @@
#include "Diagnostics.h" #include "Diagnostics.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
// If we're on Win32, we'll include our minidump writer // If we're on Win32, we'll include our minidump writer
#ifdef _WIN32 #ifdef _WIN32
@@ -9,7 +9,7 @@
#include <Dbghelp.h> #include <Dbghelp.h>
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
void make_minidump(EXCEPTION_POINTERS* e) { void make_minidump(EXCEPTION_POINTERS* e) {
auto hDbgHelp = LoadLibraryA("dbghelp"); auto hDbgHelp = LoadLibraryA("dbghelp");
@@ -28,7 +28,7 @@ void make_minidump(EXCEPTION_POINTERS* e) {
"_%4d%02d%02d_%02d%02d%02d.dmp", "_%4d%02d%02d_%02d%02d%02d.dmp",
t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond); t.wYear, t.wMonth, t.wDay, t.wHour, t.wMinute, t.wSecond);
} }
Game::logger->Log("Diagnostics", "Creating crash dump %s", name); LOG("Creating crash dump %s", name);
auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); auto hFile = CreateFileA(name, GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile == INVALID_HANDLE_VALUE) if (hFile == INVALID_HANDLE_VALUE)
return; return;
@@ -71,7 +71,7 @@ LONG CALLBACK unhandled_handler(EXCEPTION_POINTERS* e) {
#include <cstring> #include <cstring>
#include <exception> #include <exception>
#if defined(__include_backtrace__) #if defined(INCLUDE_BACKTRACE)
#include <backtrace.h> #include <backtrace.h>
#include <backtrace-supported.h> #include <backtrace-supported.h>
@@ -83,7 +83,7 @@ struct bt_ctx {
static inline void Bt(struct backtrace_state* state) { static inline void Bt(struct backtrace_state* state) {
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
Game::logger->Log("Diagnostics", "backtrace is enabled, crash dump located at %s", fileName.c_str()); LOG("backtrace is enabled, crash dump located at %s", fileName.c_str());
FILE* file = fopen(fileName.c_str(), "w+"); FILE* file = fopen(fileName.c_str(), "w+");
if (file != nullptr) { if (file != nullptr) {
backtrace_print(state, 2, file); backtrace_print(state, 2, file);
@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
} }
#endif #endif
#include "Type.h" #include "Demangler.h"
void GenerateDump() { void GenerateDump() {
std::string cmd = "sudo gcore " + std::to_string(getpid()); std::string cmd = "sudo gcore " + std::to_string(getpid());
@@ -115,58 +115,68 @@ void GenerateDump() {
} }
void CatchUnhandled(int sig) { void CatchUnhandled(int sig) {
#ifndef __include_backtrace__ std::exception_ptr eptr = std::current_exception();
try {
if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what());
} catch (...) {
LOG("Caught unknown exception.");
}
#ifndef INCLUDE_BACKTRACE
std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log"; std::string fileName = Diagnostics::GetOutDirectory() + "crash_" + Diagnostics::GetProcessName() + "_" + std::to_string(getpid()) + ".log";
Game::logger->Log("Diagnostics", "Encountered signal %i, creating crash dump %s", sig, fileName.c_str()); LOG("Encountered signal %i, creating crash dump %s", sig, fileName.c_str());
if (Diagnostics::GetProduceMemoryDump()) { if (Diagnostics::GetProduceMemoryDump()) {
GenerateDump(); GenerateDump();
} }
constexpr uint8_t MaxStackTrace = 32;
void* array[10]; void* array[MaxStackTrace];
size_t size; size_t size;
// get void*'s for all entries on the stack // get void*'s for all entries on the stack
size = backtrace(array, 10); size = backtrace(array, MaxStackTrace);
#if defined(__GNUG__) and defined(__dynamic) # if defined(__GNUG__)
// Loop through the returned addresses, and get the symbols to be demangled // Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size); 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 // Print the stack trace
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name // Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
std::string functionName = strings[i]; std::string functionName = strings[i];
std::string::size_type start = functionName.find('('); std::string::size_type start = functionName.find('(');
std::string::size_type end = functionName.find('+'); std::string::size_type end = functionName.find('+');
if (start != std::string::npos && end != std::string::npos) { if (start != std::string::npos && end != std::string::npos) {
std::string demangled = functionName.substr(start + 1, end - start - 1); std::string demangled = functionName.substr(start + 1, end - start - 1);
demangled = demangle(functionName.c_str()); demangled = Demangler::Demangle(demangled.c_str());
if (demangled.empty()) { // If the demangled string is not empty, then we can replace the mangled string with the demangled one
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str()); if (!demangled.empty()) {
} else { demangled.push_back('(');
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str()); demangled += functionName.substr(end);
functionName = demangled;
} }
} else { }
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
LOG("[%02zu] %s", i, functionName.c_str());
if (file != NULL) {
fprintf(file, "[%02zu] %s\n", i, functionName.c_str());
} }
} }
#else # else // defined(__GNUG__)
backtrace_symbols_fd(array, size, STDOUT_FILENO); backtrace_symbols_fd(array, size, STDOUT_FILENO);
#endif # endif // defined(__GNUG__)
FILE* file = fopen(fileName.c_str(), "w+"); #else // INCLUDE_BACKTRACE
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
struct backtrace_state* state = backtrace_create_state( struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(), Diagnostics::GetProcessFileName().c_str(),
@@ -177,7 +187,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 }; struct bt_ctx ctx = { state, 0 };
Bt(state); Bt(state);
#endif #endif // INCLUDE_BACKTRACE
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -196,10 +206,10 @@ void MakeBacktrace() {
sigact.sa_sigaction = CritErrHdlr; sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO; sigact.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigaction(SIGSEGV, &sigact, (struct sigaction*)nullptr) != 0 || if (sigaction(SIGSEGV, &sigact, nullptr) != 0 ||
sigaction(SIGFPE, &sigact, (struct sigaction*)nullptr) != 0 || sigaction(SIGFPE, &sigact, nullptr) != 0 ||
sigaction(SIGABRT, &sigact, (struct sigaction*)nullptr) != 0 || sigaction(SIGABRT, &sigact, nullptr) != 0 ||
sigaction(SIGILL, &sigact, (struct sigaction*)nullptr) != 0) { sigaction(SIGILL, &sigact, nullptr) != 0) {
fprintf(stderr, "error setting signal handler for %d (%s)\n", fprintf(stderr, "error setting signal handler for %d (%s)\n",
SIGSEGV, SIGSEGV,
strsignal(SIGSEGV)); strsignal(SIGSEGV));

12
dCommon/DluAssert.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __DLUASSERT__H__
#define __DLUASSERT__H__
#include <assert.h>
#ifdef _DEBUG
# define DluAssert(expression) assert(expression)
#else
# define DluAssert(expression)
#endif
#endif //!__DLUASSERT__H__

View File

@@ -9,7 +9,7 @@
#include "CDClientDatabase.h" #include "CDClientDatabase.h"
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
#include "AssetManager.h" #include "AssetManager.h"
#include "eSqliteDataType.h" #include "eSqliteDataType.h"
@@ -28,23 +28,21 @@ FdbToSqlite::Convert::Convert(std::string binaryOutPath) {
this->m_BinaryOutPath = binaryOutPath; this->m_BinaryOutPath = binaryOutPath;
} }
bool FdbToSqlite::Convert::ConvertDatabase(AssetMemoryBuffer& buffer) { bool FdbToSqlite::Convert::ConvertDatabase(AssetStream& buffer) {
if (m_ConversionStarted) return false; if (m_ConversionStarted) return false;
std::istream cdClientBuffer(&buffer);
this->m_ConversionStarted = true; this->m_ConversionStarted = true;
try { try {
CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite"); CDClientDatabase::Connect(m_BinaryOutPath + "/CDServer.sqlite");
CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;"); CDClientDatabase::ExecuteQuery("BEGIN TRANSACTION;");
int32_t numberOfTables = ReadInt32(cdClientBuffer); int32_t numberOfTables = ReadInt32(buffer);
ReadTables(numberOfTables, cdClientBuffer); ReadTables(numberOfTables, buffer);
CDClientDatabase::ExecuteQuery("COMMIT;"); CDClientDatabase::ExecuteQuery("COMMIT;");
} catch (CppSQLite3Exception& e) { } catch (CppSQLite3Exception& e) {
Game::logger->Log("FdbToSqlite", "Encountered error %s converting FDB to SQLite", e.errorMessage()); LOG("Encountered error %s converting FDB to SQLite", e.errorMessage());
return false; return false;
} }

View File

@@ -7,7 +7,7 @@
#include <iosfwd> #include <iosfwd>
#include <map> #include <map>
class AssetMemoryBuffer; #include "AssetManager.h"
enum class eSqliteDataType : int32_t; enum class eSqliteDataType : int32_t;
@@ -27,7 +27,7 @@ namespace FdbToSqlite {
* *
* @return true if the database was converted properly, false otherwise. * @return true if the database was converted properly, false otherwise.
*/ */
bool ConvertDatabase(AssetMemoryBuffer& buffer); bool ConvertDatabase(AssetStream& buffer);
/** /**
* @brief Reads a 32 bit int from the fdb file. * @brief Reads a 32 bit int from the fdb file.

7
dCommon/Game.cpp Normal file
View File

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

View File

@@ -1,18 +1,24 @@
#pragma once #pragma once
#include <string>
#include <random> #include <random>
#include <csignal>
class dServer; class dServer;
class dLogger; class Logger;
class InstanceManager; class InstanceManager;
class dChatFilter; class dChatFilter;
class dConfig; class dConfig;
class RakPeerInterface; class RakPeerInterface;
class AssetManager; class AssetManager;
struct SystemAddress; struct SystemAddress;
class EntityManager;
class dZoneManager;
class PlayerContainer;
namespace Game { namespace Game {
extern dLogger* logger; using signal_t = volatile std::sig_atomic_t;
extern Logger* logger;
extern dServer* server; extern dServer* server;
extern InstanceManager* im; extern InstanceManager* im;
extern dChatFilter* chatFilter; extern dChatFilter* chatFilter;
@@ -21,5 +27,14 @@ namespace Game {
extern RakPeerInterface* chatServer; extern RakPeerInterface* chatServer;
extern AssetManager* assetManager; extern AssetManager* assetManager;
extern SystemAddress chatSysAddr; extern SystemAddress chatSysAddr;
extern bool shouldShutdown; extern signal_t lastSignal;
extern EntityManager* entityManager;
extern dZoneManager* zoneManager;
extern PlayerContainer playerContainer;
extern std::string projectVersion;
inline bool ShouldShutdown() {
return lastSignal != 0;
}
void OnSignal(int signal);
} }

View File

@@ -8,23 +8,23 @@
#include <map> #include <map>
template <typename T> template <typename T>
inline size_t MinSize(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_t(-1) || size > string.size()) { if (size == SIZE_MAX || size > string.size()) {
return string.size(); return string.size();
} else { } else {
return size; return size;
} }
} }
inline bool IsLeadSurrogate(char16_t c) { inline bool IsLeadSurrogate(const char16_t c) {
return (0xD800 <= c) && (c <= 0xDBFF); return (0xD800 <= c) && (c <= 0xDBFF);
} }
inline bool IsTrailSurrogate(char16_t c) { inline bool IsTrailSurrogate(const char16_t c) {
return (0xDC00 <= c) && (c <= 0xDFFF); return (0xDC00 <= c) && (c <= 0xDFFF);
} }
inline void PushUTF8CodePoint(std::string& ret, char32_t cp) { inline void PushUTF8CodePoint(std::string& ret, const char32_t cp) {
if (cp <= 0x007F) { if (cp <= 0x007F) {
ret.push_back(static_cast<uint8_t>(cp)); ret.push_back(static_cast<uint8_t>(cp));
} else if (cp <= 0x07FF) { } else if (cp <= 0x07FF) {
@@ -46,16 +46,16 @@ inline void PushUTF8CodePoint(std::string& ret, char32_t cp) {
constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD; constexpr const char16_t REPLACEMENT_CHARACTER = 0xFFFD;
bool _IsSuffixChar(uint8_t c) { bool static _IsSuffixChar(const uint8_t c) {
return (c & 0xC0) == 0x80; return (c & 0xC0) == 0x80;
} }
bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) { bool GeneralUtils::details::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
size_t rem = slice.length(); const size_t rem = slice.length();
if (slice.empty()) return false; if (slice.empty()) return false;
const uint8_t* bytes = (const uint8_t*)&slice.front(); const uint8_t* bytes = reinterpret_cast<const uint8_t*>(&slice.front());
if (rem > 0) { if (rem > 0) {
uint8_t first = bytes[0]; const uint8_t first = bytes[0];
if (first < 0x80) { // 1 byte character if (first < 0x80) { // 1 byte character
out = static_cast<uint32_t>(first & 0x7F); out = static_cast<uint32_t>(first & 0x7F);
slice.remove_prefix(1); slice.remove_prefix(1);
@@ -64,7 +64,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
// middle byte, not valid at start, fall through // middle byte, not valid at start, fall through
} else if (first < 0xE0) { // two byte character } else if (first < 0xE0) { // two byte character
if (rem > 1) { if (rem > 1) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
if (_IsSuffixChar(second)) { if (_IsSuffixChar(second)) {
out = (static_cast<uint32_t>(first & 0x1F) << 6) out = (static_cast<uint32_t>(first & 0x1F) << 6)
+ static_cast<uint32_t>(second & 0x3F); + static_cast<uint32_t>(second & 0x3F);
@@ -74,8 +74,8 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
} else if (first < 0xF0) { // three byte character } else if (first < 0xF0) { // three byte character
if (rem > 2) { if (rem > 2) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
uint8_t third = bytes[2]; const uint8_t third = bytes[2];
if (_IsSuffixChar(second) && _IsSuffixChar(third)) { if (_IsSuffixChar(second) && _IsSuffixChar(third)) {
out = (static_cast<uint32_t>(first & 0x0F) << 12) out = (static_cast<uint32_t>(first & 0x0F) << 12)
+ (static_cast<uint32_t>(second & 0x3F) << 6) + (static_cast<uint32_t>(second & 0x3F) << 6)
@@ -86,9 +86,9 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
} else if (first < 0xF8) { // four byte character } else if (first < 0xF8) { // four byte character
if (rem > 3) { if (rem > 3) {
uint8_t second = bytes[1]; const uint8_t second = bytes[1];
uint8_t third = bytes[2]; const uint8_t third = bytes[2];
uint8_t fourth = bytes[3]; const uint8_t fourth = bytes[3];
if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) { if (_IsSuffixChar(second) && _IsSuffixChar(third) && _IsSuffixChar(fourth)) {
out = (static_cast<uint32_t>(first & 0x07) << 18) out = (static_cast<uint32_t>(first & 0x07) << 18)
+ (static_cast<uint32_t>(second & 0x3F) << 12) + (static_cast<uint32_t>(second & 0x3F) << 12)
@@ -107,7 +107,7 @@ bool GeneralUtils::_NextUTF8Char(std::string_view& slice, uint32_t& out) {
} }
/// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1> /// See <https://www.ietf.org/rfc/rfc2781.html#section-2.1>
bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) { bool PushUTF16CodePoint(std::u16string& output, const uint32_t U, const size_t size) {
if (output.length() >= size) return false; if (output.length() >= size) return false;
if (U < 0x10000) { if (U < 0x10000) {
// If U < 0x10000, encode U as a 16-bit unsigned integer and terminate. // If U < 0x10000, encode U as a 16-bit unsigned integer and terminate.
@@ -120,7 +120,7 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
// Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF, // Let U' = U - 0x10000. Because U is less than or equal to 0x10FFFF,
// U' must be less than or equal to 0xFFFFF. That is, U' can be // U' must be less than or equal to 0xFFFFF. That is, U' can be
// represented in 20 bits. // represented in 20 bits.
uint32_t Ut = U - 0x10000; const uint32_t Ut = U - 0x10000;
// Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and // Initialize two 16-bit unsigned integers, W1 and W2, to 0xD800 and
// 0xDC00, respectively. These integers each have 10 bits free to // 0xDC00, respectively. These integers each have 10 bits free to
@@ -141,25 +141,25 @@ bool PushUTF16CodePoint(std::u16string& output, uint32_t U, size_t size) {
} else return false; } else return false;
} }
std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view& string, size_t size) { std::u16string GeneralUtils::UTF8ToUTF16(const std::string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::u16string output; std::u16string output;
output.reserve(newSize); output.reserve(newSize);
std::string_view iterator = string; std::string_view iterator = string;
uint32_t c; uint32_t c;
while (_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {} while (details::_NextUTF8Char(iterator, c) && PushUTF16CodePoint(output, c, size)) {}
return output; return output;
} }
//! Converts an std::string (ASCII) to UCS-2 / UTF-16 //! Converts an std::string (ASCII) to UCS-2 / UTF-16
std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t size) { std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::u16string ret; std::u16string ret;
ret.reserve(newSize); ret.reserve(newSize);
for (size_t i = 0; i < newSize; i++) { for (size_t i = 0; i < newSize; ++i) {
char c = string[i]; const char c = string[i];
// Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t // Note: both 7-bit ascii characters and REPLACEMENT_CHARACTER fit in one char16_t
ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER); ret.push_back((c > 0 && c <= 127) ? static_cast<char16_t>(c) : REPLACEMENT_CHARACTER);
} }
@@ -169,18 +169,18 @@ std::u16string GeneralUtils::ASCIIToUTF16(const std::string_view& string, size_t
//! 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, size_t size) { std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view string, const size_t size) {
size_t newSize = MinSize(size, string); const size_t newSize = MinSize(size, string);
std::string ret; std::string ret;
ret.reserve(newSize); ret.reserve(newSize);
for (size_t i = 0; i < newSize; i++) { for (size_t i = 0; i < newSize; ++i) {
char16_t u = string[i]; const char16_t u = string[i];
if (IsLeadSurrogate(u) && (i + 1) < newSize) { if (IsLeadSurrogate(u) && (i + 1) < newSize) {
char16_t next = string[i + 1]; const char16_t next = string[i + 1];
if (IsTrailSurrogate(next)) { if (IsTrailSurrogate(next)) {
i += 1; i += 1;
char32_t cp = 0x10000 const char32_t cp = 0x10000
+ ((static_cast<char32_t>(u) - 0xD800) << 10) + ((static_cast<char32_t>(u) - 0xD800) << 10)
+ (static_cast<char32_t>(next) - 0xDC00); + (static_cast<char32_t>(next) - 0xDC00);
PushUTF8CodePoint(ret, cp); PushUTF8CodePoint(ret, cp);
@@ -195,40 +195,40 @@ std::string GeneralUtils::UTF16ToWTF8(const std::u16string_view& string, size_t
return ret; return ret;
} }
bool GeneralUtils::CaseInsensitiveStringCompare(const std::string& a, const std::string& b) { bool GeneralUtils::CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b) {
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); }); return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](char a, char b) { return tolower(a) == tolower(b); });
} }
// MARK: Bits // MARK: Bits
//! Sets a specific bit in a signed 64-bit integer //! Sets a specific bit in a signed 64-bit integer
int64_t GeneralUtils::SetBit(int64_t value, uint32_t index) { int64_t GeneralUtils::SetBit(int64_t value, const uint32_t index) {
return value |= 1ULL << index; return value |= 1ULL << index;
} }
//! Clears a specific bit in a signed 64-bit integer //! Clears a specific bit in a signed 64-bit integer
int64_t GeneralUtils::ClearBit(int64_t value, uint32_t index) { int64_t GeneralUtils::ClearBit(int64_t value, const uint32_t index) {
return value &= ~(1ULL << index); return value &= ~(1ULL << index);
} }
//! Checks a specific bit in a signed 64-bit integer //! Checks a specific bit in a signed 64-bit integer
bool GeneralUtils::CheckBit(int64_t value, uint32_t index) { bool GeneralUtils::CheckBit(int64_t value, const uint32_t index) {
return value & (1ULL << index); return value & (1ULL << index);
} }
bool GeneralUtils::ReplaceInString(std::string& str, const std::string& from, const std::string& to) { bool GeneralUtils::ReplaceInString(std::string& str, const std::string_view from, const std::string_view to) {
size_t start_pos = str.find(from); const size_t start_pos = str.find(from);
if (start_pos == std::string::npos) if (start_pos == std::string::npos)
return false; return false;
str.replace(start_pos, from.length(), to); str.replace(start_pos, from.length(), to);
return true; return true;
} }
std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t delimiter) { std::vector<std::wstring> GeneralUtils::SplitString(const std::wstring_view str, const wchar_t delimiter) {
std::vector<std::wstring> vector = std::vector<std::wstring>(); std::vector<std::wstring> vector = std::vector<std::wstring>();
std::wstring current; std::wstring current;
for (const auto& c : str) { for (const wchar_t c : str) {
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = L""; current = L"";
@@ -237,15 +237,15 @@ std::vector<std::wstring> GeneralUtils::SplitString(std::wstring& str, wchar_t d
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str, char16_t delimiter) { std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string_view str, const char16_t delimiter) {
std::vector<std::u16string> vector = std::vector<std::u16string>(); std::vector<std::u16string> vector = std::vector<std::u16string>();
std::u16string current; std::u16string current;
for (const auto& c : str) { for (const char16_t c : str) {
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = u""; current = u"";
@@ -254,17 +254,15 @@ std::vector<std::u16string> GeneralUtils::SplitString(const std::u16string& str,
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char delimiter) { std::vector<std::string> GeneralUtils::SplitString(const std::string_view str, const char delimiter) {
std::vector<std::string> vector = std::vector<std::string>(); std::vector<std::string> vector = std::vector<std::string>();
std::string current = ""; std::string current = "";
for (size_t i = 0; i < str.length(); i++) { for (const char c : str) {
char c = str[i];
if (c == delimiter) { if (c == delimiter) {
vector.push_back(current); vector.push_back(current);
current = ""; current = "";
@@ -273,53 +271,70 @@ std::vector<std::string> GeneralUtils::SplitString(const std::string& str, char
} }
} }
vector.push_back(current); vector.push_back(std::move(current));
return vector; return vector;
} }
std::u16string GeneralUtils::ReadWString(RakNet::BitStream* inStream) { std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
uint32_t length; uint32_t length;
inStream->Read<uint32_t>(length); inStream.Read<uint32_t>(length);
std::u16string string; std::u16string string;
for (auto i = 0; i < length; i++) { for (uint32_t i = 0; i < length; ++i) {
uint16_t c; uint16_t c;
inStream->Read(c); inStream.Read(c);
string.push_back(c); string.push_back(c);
} }
return string; return string;
} }
std::vector<std::string> GeneralUtils::GetSqlFileNamesFromFolder(const std::string& 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 (auto& t : std::filesystem::directory_iterator(folder)) { for (const auto& t : std::filesystem::directory_iterator(folder)) {
auto filename = t.path().filename().string(); auto filename = t.path().filename().string();
auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0)); const auto index = std::stoi(GeneralUtils::SplitString(filename, '_').at(0));
filenames.insert(std::make_pair(index, filename)); filenames.emplace(index, std::move(filename));
} }
// Now sort the map by the oldest migration. // Now sort the map by the oldest migration.
std::vector<std::string> sortedFiles{}; std::vector<std::string> sortedFiles{};
auto fileIterator = filenames.begin(); auto fileIterator = filenames.cbegin();
std::map<uint32_t, std::string>::iterator oldest = filenames.begin(); auto oldest = filenames.cbegin();
while (!filenames.empty()) { while (!filenames.empty()) {
if (fileIterator == filenames.end()) { if (fileIterator == filenames.cend()) {
sortedFiles.push_back(oldest->second); sortedFiles.push_back(oldest->second);
filenames.erase(oldest); filenames.erase(oldest);
fileIterator = filenames.begin(); fileIterator = filenames.cbegin();
oldest = filenames.begin(); oldest = filenames.cbegin();
continue; continue;
} }
if (oldest->first > fileIterator->first) oldest = fileIterator; if (oldest->first > fileIterator->first) oldest = fileIterator;
fileIterator++; ++fileIterator;
} }
return sortedFiles; return sortedFiles;
} }
bool GeneralUtils::TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst) { #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
return TryParse<float>(x.c_str(), dst.x) && TryParse<float>(y.c_str(), dst.y) && TryParse<float>(z.c_str(), dst.z);
// MacOS floating-point parse function specializations
namespace GeneralUtils::details {
template <>
[[nodiscard]] float _parse<float>(const std::string_view str, size_t& parseNum) {
return std::stof(std::string{ str }, &parseNum);
}
template <>
[[nodiscard]] double _parse<double>(const std::string_view str, size_t& parseNum) {
return std::stod(std::string{ str }, &parseNum);
}
template <>
[[nodiscard]] long double _parse<long double>(const std::string_view str, size_t& parseNum) {
return std::stold(std::string{ str }, &parseNum);
}
} }
#endif

View File

@@ -1,19 +1,23 @@
#pragma once #pragma once
// C++ // C++
#include <stdint.h> #include <charconv>
#include <random> #include <cstdint>
#include <time.h> #include <ctime>
#include <string>
#include <type_traits>
#include <functional> #include <functional>
#include <type_traits> #include <optional>
#include <random>
#include <span>
#include <stdexcept> #include <stdexcept>
#include <BitStream.h> #include <string>
#include "NiPoint3.h" #include <string_view>
#include <type_traits>
#include "BitStream.h"
#include "NiPoint3.h"
#include "dPlatforms.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
enum eInventoryType : uint32_t; enum eInventoryType : uint32_t;
enum class eObjectBits : size_t; enum class eObjectBits : size_t;
@@ -29,29 +33,31 @@ namespace GeneralUtils {
//! Converts a plain ASCII string to a UTF-16 string //! Converts a plain ASCII string to a UTF-16 string
/*! /*!
\param string The string to convert \param string The string to convert
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-16 representation of the string \return An UTF-16 representation of the string
*/ */
std::u16string ASCIIToUTF16(const std::string_view& string, size_t size = -1); std::u16string ASCIIToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
//! Converts a UTF-8 String to a UTF-16 string //! Converts a UTF-8 String to a UTF-16 string
/*! /*!
\param string The string to convert \param string The string to convert
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-16 representation of the string \return An UTF-16 representation of the string
*/ */
std::u16string UTF8ToUTF16(const std::string_view& string, size_t size = -1); std::u16string UTF8ToUTF16(const std::string_view string, const size_t size = SIZE_MAX);
//! Internal, do not use namespace details {
bool _NextUTF8Char(std::string_view& slice, uint32_t& out); //! Internal, do not use
bool _NextUTF8Char(std::string_view& slice, uint32_t& out);
}
//! 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
\param size A size to trim the string to. Default is -1 (No trimming) \param size A size to trim the string to. Default is SIZE_MAX (No trimming)
\return An UTF-8 representation of the string \return An UTF-8 representation of the string
*/ */
std::string UTF16ToWTF8(const std::u16string_view& string, size_t size = -1); std::string UTF16ToWTF8(const std::u16string_view string, const size_t size = SIZE_MAX);
/** /**
* Compares two basic strings but does so ignoring case sensitivity * Compares two basic strings but does so ignoring case sensitivity
@@ -59,7 +65,7 @@ namespace GeneralUtils {
* \param b the second string to compare against the first string * \param b the second string to compare against the first string
* @return if the two strings are equal * @return if the two strings are equal
*/ */
bool CaseInsensitiveStringCompare(const std::string& a, const std::string& b); bool CaseInsensitiveStringCompare(const std::string_view a, const std::string_view b);
// MARK: Bits // MARK: Bits
@@ -67,9 +73,9 @@ namespace GeneralUtils {
//! Sets a bit on a numerical value //! Sets a bit on a numerical value
template <typename T> template <typename T>
inline void SetBit(T& value, eObjectBits bits) { inline void SetBit(T& value, const eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
auto index = static_cast<size_t>(bits); const auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8) - 1) { if (index > (sizeof(T) * 8) - 1) {
return; return;
} }
@@ -79,9 +85,9 @@ namespace GeneralUtils {
//! Clears a bit on a numerical value //! Clears a bit on a numerical value
template <typename T> template <typename T>
inline void ClearBit(T& value, eObjectBits bits) { inline void ClearBit(T& value, const eObjectBits bits) {
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
auto index = static_cast<size_t>(bits); const auto index = static_cast<size_t>(bits);
if (index > (sizeof(T) * 8 - 1)) { if (index > (sizeof(T) * 8 - 1)) {
return; return;
} }
@@ -94,14 +100,14 @@ namespace GeneralUtils {
\param value The value to set the bit for \param value The value to set the bit for
\param index The index of the bit \param index The index of the bit
*/ */
int64_t SetBit(int64_t value, uint32_t index); int64_t SetBit(int64_t value, const uint32_t index);
//! Clears a specific bit in a signed 64-bit integer //! Clears a specific bit in a signed 64-bit integer
/*! /*!
\param value The value to clear the bit from \param value The value to clear the bit from
\param index The index of the bit \param index The index of the bit
*/ */
int64_t ClearBit(int64_t value, uint32_t index); int64_t ClearBit(int64_t value, const uint32_t index);
//! Checks a specific bit in a signed 64-bit integer //! Checks a specific bit in a signed 64-bit integer
/*! /*!
@@ -109,7 +115,131 @@ namespace GeneralUtils {
\param index The index of the bit \param index The index of the bit
\return Whether or not the bit is set \return Whether or not the bit is set
*/ */
bool CheckBit(int64_t value, uint32_t index); bool CheckBit(int64_t value, const uint32_t index);
bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
std::u16string ReadWString(RakNet::BitStream& inStream);
std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
// Concept constraining to enum types
template <typename T>
concept Enum = std::is_enum_v<T>;
// Concept constraining to numeric types
template <typename T>
concept Numeric = std::integral<T> || Enum<T> || std::floating_point<T>;
// Concept trickery to enable parsing underlying numeric types
template <Numeric T>
struct numeric_parse { using type = T; };
// If an enum, present an alias to its underlying type for parsing
template <Numeric T> requires Enum<T>
struct numeric_parse<T> { using type = std::underlying_type_t<T>; };
// If a boolean, present an alias to an intermediate integral type for parsing
template <Numeric T> requires std::same_as<T, bool>
struct numeric_parse<T> { using type = uint8_t; };
// Shorthand type alias
template <Numeric T>
using numeric_parse_t = numeric_parse<T>::type;
/**
* For numeric values: Parses a string_view and returns an optional variable depending on the result.
* @param str The string_view to be evaluated
* @returns An std::optional containing the desired value if it is equivalent to the string
*/
template <Numeric T>
[[nodiscard]] std::optional<T> TryParse(std::string_view str) {
numeric_parse_t<T> result;
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
const char* const strEnd = str.data() + str.size();
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
const bool isParsed = parseEnd == strEnd && ec == std::errc{};
return isParsed ? static_cast<T>(result) : std::optional<T>{};
}
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse helper function specializations
namespace details {
template <std::floating_point T>
[[nodiscard]] T _parse(const std::string_view str, size_t& parseNum);
}
/**
* For floating-point values: Parses a string_view and returns an optional variable depending on the result.
* Note that this function overload is only included for MacOS, as from_chars will fulfill its purpose otherwise.
* @param str The string_view to be evaluated
* @returns An std::optional containing the desired value if it is equivalent to the string
*/
template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
try {
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
size_t parseNum;
const T result = details::_parse<T>(str, parseNum);
const bool isParsed = str.length() == parseNum;
return isParsed ? result : std::optional<T>{};
} catch (...) {
return std::nullopt;
}
#endif
/**
* The TryParse overload for handling NiPoint3 by passing 3 seperate string references
* @param strX The string representing the X coordinate
* @param strY The string representing the Y coordinate
* @param strZ The string representing the Z coordinate
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/
template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string_view strX, const std::string_view strY, const std::string_view strZ) {
const auto x = TryParse<float>(strX);
if (!x) return std::nullopt;
const auto y = TryParse<float>(strY);
if (!y) return std::nullopt;
const auto z = TryParse<float>(strZ);
return z ? std::make_optional<NiPoint3>(x.value(), y.value(), z.value()) : std::nullopt;
}
/**
* The TryParse overload for handling NiPoint3 by passing a span of three strings
* @param str The string vector representing the X, Y, and Z coordinates
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/
template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::span<const std::string> str) {
return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
}
template <typename T>
std::u16string to_u16string(const T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
}
// From boost::hash_combine
template <class T>
constexpr void hash_combine(std::size_t& s, const T& v) {
std::hash<T> h;
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
}
// MARK: Random Number Generation // MARK: Random Number Generation
@@ -119,7 +249,7 @@ namespace GeneralUtils {
\param max The maximum to generate to \param max The maximum to generate to
*/ */
template <typename T> template <typename T>
inline T GenerateRandomNumber(std::size_t min, std::size_t max) { inline T GenerateRandomNumber(const std::size_t min, const std::size_t max) {
// Make sure it is a numeric type // Make sure it is a numeric type
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type"); static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
@@ -134,93 +264,28 @@ namespace GeneralUtils {
return T(); return T();
} }
bool ReplaceInString(std::string& str, const std::string& from, const std::string& to); /**
* Casts the value of an enum entry to its underlying type
* @param entry Enum entry to cast
* @returns The enum entry's value in its underlying type
*/
template <Enum eType>
constexpr std::underlying_type_t<eType> ToUnderlying(const eType entry) noexcept {
return static_cast<std::underlying_type_t<eType>>(entry);
}
std::u16string ReadWString(RakNet::BitStream* inStream); // on Windows we need to undef these or else they conflict with our numeric limits calls
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter); #ifdef _WIN32
#undef min
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter); #undef max
#endif
std::vector<std::string> SplitString(const std::string& str, char delimiter);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder);
template <typename T> template <typename T>
T Parse(const char* value); inline T GenerateRandomNumber() {
// Make sure it is a numeric type
static_assert(std::is_arithmetic<T>::value, "Not an arithmetic type");
template <> return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
inline int32_t Parse(const char* value) {
return std::stoi(value);
}
template <>
inline int64_t Parse(const char* value) {
return std::stoll(value);
}
template <>
inline float Parse(const char* value) {
return std::stof(value);
}
template <>
inline double Parse(const char* value) {
return std::stod(value);
}
template <>
inline uint32_t Parse(const char* value) {
return std::stoul(value);
}
template <>
inline uint64_t Parse(const char* value) {
return std::stoull(value);
}
template <>
inline eInventoryType Parse(const char* value) {
return static_cast<eInventoryType>(std::stoul(value));
}
template <>
inline eReplicaComponentType Parse(const char* value) {
return static_cast<eReplicaComponentType>(std::stoul(value));
}
template <typename T>
bool TryParse(const char* value, T& dst) {
try {
dst = Parse<T>(value);
return true;
} catch (...) {
return false;
}
}
template <typename T>
T Parse(const std::string& value) {
return Parse<T>(value.c_str());
}
template <typename T>
bool TryParse(const std::string& value, T& dst) {
return TryParse<T>(value.c_str(), dst);
}
bool TryParse(const std::string& x, const std::string& y, const std::string& z, NiPoint3& dst);
template<typename T>
std::u16string to_u16string(T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value));
}
// From boost::hash_combine
template <class T>
void hash_combine(std::size_t& s, const T& v) {
std::hash<T> h;
s ^= h(v) + 0x9e3779b9 + (s << 6) + (s >> 2);
} }
} }

View File

@@ -4,7 +4,7 @@
#include "GeneralUtils.h" #include "GeneralUtils.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
// C++ // C++
#include <string_view> #include <string_view>
@@ -48,7 +48,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
try { try {
type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10)); type = static_cast<eLDFType>(strtol(ldfTypeAndValue.first.data(), &storage, 10));
} catch (std::exception) { } catch (std::exception) {
Game::logger->Log("LDFFormat", "Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data()); LOG("Attempted to process invalid ldf type (%s) from string (%s)", ldfTypeAndValue.first.data(), format.data());
return nullptr; return nullptr;
} }
@@ -61,35 +61,33 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_S32: { case LDF_TYPE_S32: {
try { const auto data = GeneralUtils::TryParse<int32_t>(ldfTypeAndValue.second);
int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10)); if (!data) {
returnValue = new LDFData<int32_t>(key, data); LOG("Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<int32_t>(key, data.value());
break; break;
} }
case LDF_TYPE_FLOAT: { case LDF_TYPE_FLOAT: {
try { const auto data = GeneralUtils::TryParse<float>(ldfTypeAndValue.second);
float data = strtof(ldfTypeAndValue.second.data(), &storage); if (!data) {
returnValue = new LDFData<float>(key, data); LOG("Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<float>(key, data.value());
break; break;
} }
case LDF_TYPE_DOUBLE: { case LDF_TYPE_DOUBLE: {
try { const auto data = GeneralUtils::TryParse<double>(ldfTypeAndValue.second);
double data = strtod(ldfTypeAndValue.second.data(), &storage); if (!data) {
returnValue = new LDFData<double>(key, data); LOG("Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<double>(key, data.value());
break; break;
} }
@@ -102,12 +100,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = 0; data = 0;
} else { } else {
try { const auto dataOptional = GeneralUtils::TryParse<uint32_t>(ldfTypeAndValue.second);
data = static_cast<uint32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10)); if (!dataOptional) {
} catch (std::exception) { LOG("Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
data = dataOptional.value();
} }
returnValue = new LDFData<uint32_t>(key, data); returnValue = new LDFData<uint32_t>(key, data);
@@ -122,12 +120,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") { } else if (ldfTypeAndValue.second == "false") {
data = false; data = false;
} else { } else {
try { const auto dataOptional = GeneralUtils::TryParse<bool>(ldfTypeAndValue.second);
data = static_cast<bool>(strtol(ldfTypeAndValue.second.data(), &storage, 10)); if (!dataOptional) {
} catch (std::exception) { LOG("Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
data = dataOptional.value();
} }
returnValue = new LDFData<bool>(key, data); returnValue = new LDFData<bool>(key, data);
@@ -135,24 +133,22 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_U64: { case LDF_TYPE_U64: {
try { const auto data = GeneralUtils::TryParse<uint64_t>(ldfTypeAndValue.second);
uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10)); if (!data) {
returnValue = new LDFData<uint64_t>(key, data); LOG("Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<uint64_t>(key, data.value());
break; break;
} }
case LDF_TYPE_OBJID: { case LDF_TYPE_OBJID: {
try { const auto data = GeneralUtils::TryParse<LWOOBJID>(ldfTypeAndValue.second);
LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10)); if (!data) {
returnValue = new LDFData<LWOOBJID>(key, data); LOG("Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
} catch (std::exception) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr; return nullptr;
} }
returnValue = new LDFData<LWOOBJID>(key, data.value());
break; break;
} }
@@ -163,12 +159,12 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} }
case LDF_TYPE_UNKNOWN: { case LDF_TYPE_UNKNOWN: {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data()); LOG("Warning: Attempted to process invalid unknown value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
break; break;
} }
default: { default: {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data()); LOG("Warning: Attempted to process invalid LDF type (%d) from string (%s)", type, format.data());
break; break;
} }
} }

View File

@@ -31,22 +31,22 @@ public:
virtual ~LDFBaseData() {} virtual ~LDFBaseData() {}
virtual void WriteToPacket(RakNet::BitStream* packet) = 0; virtual void WriteToPacket(RakNet::BitStream& packet) const = 0;
virtual const std::u16string& GetKey() = 0; virtual const std::u16string& GetKey() const = 0;
virtual eLDFType GetValueType() = 0; virtual eLDFType GetValueType() const = 0;
/** Gets a string from the key/value pair /** Gets a string from the key/value pair
* @param includeKey Whether or not to include the key in the data * @param includeKey Whether or not to include the key in the data
* @param includeTypeId Whether or not to include the type id in the data * @param includeTypeId Whether or not to include the type id in the data
* @return The string representation of the data * @return The string representation of the data
*/ */
virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) = 0; virtual std::string GetString(bool includeKey = true, bool includeTypeId = true) const = 0;
virtual std::string GetValueAsString() = 0; virtual std::string GetValueAsString() const = 0;
virtual LDFBaseData* Copy() = 0; virtual LDFBaseData* Copy() const = 0;
/** /**
* Given an input string, return the data as a LDF key. * Given an input string, return the data as a LDF key.
@@ -62,17 +62,17 @@ private:
T value; T value;
//! Writes the key to the packet //! Writes the key to the packet
void WriteKey(RakNet::BitStream* packet) { void WriteKey(RakNet::BitStream& packet) const {
packet->Write(static_cast<uint8_t>(this->key.length() * sizeof(uint16_t))); packet.Write<uint8_t>(this->key.length() * sizeof(uint16_t));
for (uint32_t i = 0; i < this->key.length(); ++i) { for (uint32_t i = 0; i < this->key.length(); ++i) {
packet->Write(static_cast<uint16_t>(this->key[i])); packet.Write<uint16_t>(this->key[i]);
} }
} }
//! Writes the value to the packet //! Writes the value to the packet
void WriteValue(RakNet::BitStream* packet) { void WriteValue(RakNet::BitStream& packet) const {
packet->Write(static_cast<uint8_t>(this->GetValueType())); packet.Write<uint8_t>(this->GetValueType());
packet->Write(this->value); packet.Write(this->value);
} }
public: public:
@@ -90,7 +90,7 @@ public:
/*! /*!
\return The value \return The value
*/ */
const T& GetValue(void) { return this->value; } const T& GetValue(void) const { return this->value; }
//! Sets the value //! Sets the value
/*! /*!
@@ -102,13 +102,13 @@ public:
/*! /*!
\return The value string \return The value string
*/ */
std::string GetValueString(void) { return ""; } std::string GetValueString(void) const { return ""; }
//! Writes the data to a packet //! Writes the data to a packet
/*! /*!
\param packet The packet \param packet The packet
*/ */
void WriteToPacket(RakNet::BitStream* packet) override { void WriteToPacket(RakNet::BitStream& packet) const override {
this->WriteKey(packet); this->WriteKey(packet);
this->WriteValue(packet); this->WriteValue(packet);
} }
@@ -117,13 +117,13 @@ public:
/*! /*!
\return The key \return The key
*/ */
const std::u16string& GetKey(void) override { return this->key; } const std::u16string& GetKey(void) const override { return this->key; }
//! Gets the LDF Type //! Gets the LDF Type
/*! /*!
\return The LDF value type \return The LDF value type
*/ */
eLDFType GetValueType(void) override { return LDF_TYPE_UNKNOWN; } eLDFType GetValueType(void) const override { return LDF_TYPE_UNKNOWN; }
//! Gets the string data //! Gets the string data
/*! /*!
@@ -131,7 +131,7 @@ public:
\param includeTypeId Whether or not to include the type id in the data \param includeTypeId Whether or not to include the type id in the data
\return The string representation of the data \return The string representation of the data
*/ */
std::string GetString(const bool includeKey = true, const bool includeTypeId = true) override { std::string GetString(const bool includeKey = true, const bool includeTypeId = true) const override {
if (GetValueType() == -1) { if (GetValueType() == -1) {
return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>"; return GeneralUtils::UTF16ToWTF8(this->key) + "=-1:<server variable>";
} }
@@ -154,70 +154,70 @@ public:
return stream.str(); return stream.str();
} }
std::string GetValueAsString() override { std::string GetValueAsString() const override {
return this->GetValueString(); return this->GetValueString();
} }
LDFBaseData* Copy() override { LDFBaseData* Copy() const override {
return new LDFData<T>(key, value); return new LDFData<T>(key, value);
} }
inline static T Default = {}; inline static const T Default = {};
}; };
// LDF Types // LDF Types
template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) { return LDF_TYPE_UTF_16; }; template<> inline eLDFType LDFData<std::u16string>::GetValueType(void) const { return LDF_TYPE_UTF_16; };
template<> inline eLDFType LDFData<int32_t>::GetValueType(void) { return LDF_TYPE_S32; }; template<> inline eLDFType LDFData<int32_t>::GetValueType(void) const { return LDF_TYPE_S32; };
template<> inline eLDFType LDFData<float>::GetValueType(void) { return LDF_TYPE_FLOAT; }; template<> inline eLDFType LDFData<float>::GetValueType(void) const { return LDF_TYPE_FLOAT; };
template<> inline eLDFType LDFData<double>::GetValueType(void) { return LDF_TYPE_DOUBLE; }; template<> inline eLDFType LDFData<double>::GetValueType(void) const { return LDF_TYPE_DOUBLE; };
template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) { return LDF_TYPE_U32; }; template<> inline eLDFType LDFData<uint32_t>::GetValueType(void) const { return LDF_TYPE_U32; };
template<> inline eLDFType LDFData<bool>::GetValueType(void) { return LDF_TYPE_BOOLEAN; }; template<> inline eLDFType LDFData<bool>::GetValueType(void) const { return LDF_TYPE_BOOLEAN; };
template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) { return LDF_TYPE_U64; }; template<> inline eLDFType LDFData<uint64_t>::GetValueType(void) const { return LDF_TYPE_U64; };
template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) { return LDF_TYPE_OBJID; }; template<> inline eLDFType LDFData<LWOOBJID>::GetValueType(void) const { return LDF_TYPE_OBJID; };
template<> inline eLDFType LDFData<std::string>::GetValueType(void) { return LDF_TYPE_UTF_8; }; template<> inline eLDFType LDFData<std::string>::GetValueType(void) const { return LDF_TYPE_UTF_8; };
// The specialized version for std::u16string (UTF-16) // The specialized version for std::u16string (UTF-16)
template<> template<>
inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) const {
packet->Write(static_cast<uint8_t>(this->GetValueType())); packet.Write<uint8_t>(this->GetValueType());
packet->Write(static_cast<uint32_t>(this->value.length())); packet.Write<uint32_t>(this->value.length());
for (uint32_t i = 0; i < this->value.length(); ++i) { for (uint32_t i = 0; i < this->value.length(); ++i) {
packet->Write(static_cast<uint16_t>(this->value[i])); packet.Write<uint16_t>(this->value[i]);
} }
} }
// The specialized version for bool // The specialized version for bool
template<> template<>
inline void LDFData<bool>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) const {
packet->Write(static_cast<uint8_t>(this->GetValueType())); packet.Write<uint8_t>(this->GetValueType());
packet->Write(static_cast<uint8_t>(this->value)); packet.Write<uint8_t>(this->value);
} }
// The specialized version for std::string (UTF-8) // The specialized version for std::string (UTF-8)
template<> template<>
inline void LDFData<std::string>::WriteValue(RakNet::BitStream* packet) { inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) const {
packet->Write(static_cast<uint8_t>(this->GetValueType())); packet.Write<uint8_t>(this->GetValueType());
packet->Write(static_cast<uint32_t>(this->value.length())); packet.Write<uint32_t>(this->value.length());
for (uint32_t i = 0; i < this->value.length(); ++i) { for (uint32_t i = 0; i < this->value.length(); ++i) {
packet->Write(static_cast<uint8_t>(this->value[i])); packet.Write<uint8_t>(this->value[i]);
} }
} }
template<> inline std::string LDFData<std::u16string>::GetValueString() { template<> inline std::string LDFData<std::u16string>::GetValueString() const {
return GeneralUtils::UTF16ToWTF8(this->value, this->value.size()); return GeneralUtils::UTF16ToWTF8(this->value, this->value.size());
} }
template<> inline std::string LDFData<int32_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<int32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<float>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<float>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<double>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<double>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint32_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<uint32_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<bool>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<bool>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<uint64_t>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<uint64_t>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<LWOOBJID>::GetValueString() { return std::to_string(this->value); } template<> inline std::string LDFData<LWOOBJID>::GetValueString() const { return std::to_string(this->value); }
template<> inline std::string LDFData<std::string>::GetValueString() { return this->value; } template<> inline std::string LDFData<std::string>::GetValueString() const { return this->value; }
#endif //!__LDFFORMAT__H__ #endif //!__LDFFORMAT__H__

98
dCommon/Logger.cpp Normal file
View File

@@ -0,0 +1,98 @@
#include "Logger.h"
#include <algorithm>
#include <ctime>
#include <filesystem>
#include <stdarg.h>
Writer::~Writer() {
// Flush before we close
Flush();
// Dont try to close stdcout...
if (!m_Outfile || m_IsConsoleWriter) return;
fclose(m_Outfile);
m_Outfile = NULL;
}
void Writer::Log(const char* time, const char* message) {
if (!m_Outfile || !m_Enabled) return;
fputs(time, m_Outfile);
fputs(message, m_Outfile);
}
void Writer::Flush() {
if (!m_Outfile) return;
fflush(m_Outfile);
}
FileWriter::FileWriter(const char* outpath) {
m_Outfile = fopen(outpath, "wt");
if (!m_Outfile) printf("Couldn't open %s for writing!\n", outpath);
m_Outpath = outpath;
m_IsConsoleWriter = false;
}
ConsoleWriter::ConsoleWriter(bool enabled) {
m_Enabled = enabled;
m_Outfile = stdout;
m_IsConsoleWriter = true;
}
Logger::Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements) {
m_logDebugStatements = logDebugStatements;
std::filesystem::path outpathPath(outpath);
if (!std::filesystem::exists(outpathPath.parent_path())) std::filesystem::create_directories(outpathPath.parent_path());
m_Writers.push_back(std::make_unique<FileWriter>(outpath));
m_Writers.push_back(std::make_unique<ConsoleWriter>(logToConsole));
}
void Logger::vLog(const char* format, va_list args) {
time_t t = time(NULL);
struct tm* time = localtime(&t);
char timeStr[70];
strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time);
char message[2048];
vsnprintf(message, 2048, format, args);
for (const auto& writer : m_Writers) {
writer->Log(timeStr, message);
}
}
void Logger::Log(const char* className, const char* format, ...) {
va_list args;
std::string log = std::string(className) + "] " + std::string(format) + "\n";
va_start(args, format);
vLog(log.c_str(), args);
va_end(args);
}
void Logger::LogDebug(const char* className, const char* format, ...) {
if (!m_logDebugStatements) return;
va_list args;
std::string log = std::string(className) + "] " + std::string(format) + "\n";
va_start(args, format);
vLog(log.c_str(), args);
va_end(args);
}
void Logger::Flush() {
for (const auto& writer : m_Writers) {
writer->Flush();
}
}
void Logger::SetLogToConsole(bool logToConsole) {
for (const auto& writer : m_Writers) {
if (writer->IsConsoleWriter()) writer->SetEnabled(logToConsole);
}
}
bool Logger::GetLogToConsole() const {
bool toReturn = false;
for (const auto& writer : m_Writers) {
if (writer->IsConsoleWriter()) toReturn |= writer->GetEnabled();
}
return toReturn;
}

89
dCommon/Logger.h Normal file
View File

@@ -0,0 +1,89 @@
#pragma once
#include <memory>
#include <string>
#include <vector>
#define STRINGIFY_IMPL(x) #x
#define STRINGIFY(x) STRINGIFY_IMPL(x)
#define GET_FILE_NAME(x, y) GetFileNameFromAbsolutePath(__FILE__ x y)
#define FILENAME_AND_LINE GET_FILE_NAME(":", STRINGIFY(__LINE__))
// Calculate the filename at compile time from the path.
// We just do this by scanning the path for the last '/' or '\' character and returning the string after it.
constexpr const char* GetFileNameFromAbsolutePath(const char* path) {
const char* file = path;
while (*path) {
char nextChar = *path++;
if (nextChar == '/' || nextChar == '\\') {
file = path;
}
}
return file;
}
// These have to have a constexpr variable to store the filename_and_line result in a local variable otherwise
// they will not be valid constexpr and will be evaluated at runtime instead of compile time!
// The full string is still stored in the binary, however the offset of the filename in the absolute paths
// is used in the instruction instead of the start of the absolute path.
#define LOG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->Log(str, message, ##__VA_ARGS__); } while(0)
#define LOG_DEBUG(message, ...) do { auto str = FILENAME_AND_LINE; Game::logger->LogDebug(str, message, ##__VA_ARGS__); } while(0)
// Writer class for writing data to files.
class Writer {
public:
Writer(bool enabled = true) : m_Enabled(enabled) {};
virtual ~Writer();
virtual void Log(const char* time, const char* message);
virtual void Flush();
void SetEnabled(bool disabled) { m_Enabled = disabled; }
bool GetEnabled() const { return m_Enabled; }
bool IsConsoleWriter() { return m_IsConsoleWriter; }
public:
bool m_Enabled = true;
bool m_IsConsoleWriter = false;
FILE* m_Outfile;
};
// FileWriter class for writing data to a file on a disk.
class FileWriter : public Writer {
public:
FileWriter(const char* outpath);
FileWriter(const std::string& outpath) : FileWriter(outpath.c_str()) {};
private:
std::string m_Outpath;
};
// ConsoleWriter class for writing data to the console.
class ConsoleWriter : public Writer {
public:
ConsoleWriter(bool enabled);
};
class Logger {
public:
Logger() = delete;
Logger(const std::string& outpath, bool logToConsole, bool logDebugStatements);
void Log(const char* filenameAndLine, const char* format, ...);
void LogDebug(const char* filenameAndLine, const char* format, ...);
void Flush();
bool GetLogToConsole() const;
void SetLogToConsole(bool logToConsole);
void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; }
private:
void vLog(const char* format, va_list args);
bool m_logDebugStatements;
std::vector<std::unique_ptr<Writer>> m_Writers;
};

View File

@@ -107,7 +107,7 @@ void Metrics::EndMeasurement(MetricVariable variable) {
} }
float Metrics::ToMiliseconds(int64_t nanoseconds) { float Metrics::ToMiliseconds(int64_t nanoseconds) {
return (float)nanoseconds / 1e6; return static_cast<float>(nanoseconds) / 1e6;
} }
std::string Metrics::MetricVariableToString(MetricVariable variable) { std::string Metrics::MetricVariableToString(MetricVariable variable) {
@@ -193,34 +193,34 @@ size_t Metrics::GetPeakRSS() {
/* Windows -------------------------------------------------- */ /* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info; PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return (size_t)info.PeakWorkingSetSize; return static_cast<size_t>(info.PeakWorkingSetSize);
#elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__))) #elif (defined(_AIX) || defined(__TOS__AIX__)) || (defined(__sun__) || defined(__sun) || defined(sun) && (defined(__SVR4) || defined(__svr4__)))
/* AIX and Solaris ------------------------------------------ */ /* AIX and Solaris ------------------------------------------ */
struct psinfo psinfo; struct psinfo psinfo;
int fd = -1; int fd = -1;
if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1) if ((fd = open("/proc/self/psinfo", O_RDONLY)) == -1)
return (size_t)0L; /* Can't open? */ return static_cast<size_t>(0L); /* Can't open? */
if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) { if (read(fd, &psinfo, sizeof(psinfo)) != sizeof(psinfo)) {
close(fd); close(fd);
return (size_t)0L; /* Can't read? */ return static_cast<size_t>(0L); /* Can't read? */
} }
close(fd); close(fd);
return (size_t)(psinfo.pr_rssize * 1024L); return static_cast<size_t>(psinfo.pr_rssize * 1024L);
#elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__)) #elif defined(__unix__) || defined(__unix) || defined(unix) || (defined(__APPLE__) && defined(__MACH__))
/* BSD, Linux, and OSX -------------------------------------- */ /* BSD, Linux, and OSX -------------------------------------- */
struct rusage rusage; struct rusage rusage;
getrusage(RUSAGE_SELF, &rusage); getrusage(RUSAGE_SELF, &rusage);
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
return (size_t)rusage.ru_maxrss; return static_cast<size_t>(rusage.ru_maxrss);
#else #else
return (size_t)(rusage.ru_maxrss * 1024L); return static_cast<size_t>(rusage.ru_maxrss * 1024L);
#endif #endif
#else #else
/* Unknown OS ----------------------------------------------- */ /* Unknown OS ----------------------------------------------- */
return (size_t)0L; /* Unsupported. */ return static_cast<size_t>(0L); /* Unsupported. */
#endif #endif
} }
@@ -234,33 +234,33 @@ size_t Metrics::GetCurrentRSS() {
/* Windows -------------------------------------------------- */ /* Windows -------------------------------------------------- */
PROCESS_MEMORY_COUNTERS info; PROCESS_MEMORY_COUNTERS info;
GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info)); GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
return (size_t)info.WorkingSetSize; return static_cast<size_t>(info.WorkingSetSize);
#elif defined(__APPLE__) && defined(__MACH__) #elif defined(__APPLE__) && defined(__MACH__)
/* OSX ------------------------------------------------------ */ /* OSX ------------------------------------------------------ */
struct mach_task_basic_info info; struct mach_task_basic_info info;
mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT; mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO,
(task_info_t)&info, &infoCount) != KERN_SUCCESS) reinterpret_cast<task_info_t>(&info), &infoCount) != KERN_SUCCESS)
return (size_t)0L; /* Can't access? */ return static_cast<size_t>(0L); /* Can't access? */
return (size_t)info.resident_size; return static_cast<size_t>(info.resident_size);
#elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__) #elif defined(__linux__) || defined(__linux) || defined(linux) || defined(__gnu_linux__)
/* Linux ---------------------------------------------------- */ /* Linux ---------------------------------------------------- */
long rss = 0L; long rss = 0L;
FILE* fp = NULL; FILE* fp = NULL;
if ((fp = fopen("/proc/self/statm", "r")) == NULL) if ((fp = fopen("/proc/self/statm", "r")) == NULL)
return (size_t)0L; /* Can't open? */ return static_cast<size_t>(0L); /* Can't open? */
if (fscanf(fp, "%*s%ld", &rss) != 1) { if (fscanf(fp, "%*s%ld", &rss) != 1) {
fclose(fp); fclose(fp);
return (size_t)0L; /* Can't read? */ return static_cast<size_t>(0L); /* Can't read? */
} }
fclose(fp); fclose(fp);
return (size_t)rss * (size_t)sysconf(_SC_PAGESIZE); return static_cast<size_t>(rss) * static_cast<size_t>(sysconf(_SC_PAGESIZE));
#else #else
/* AIX, BSD, Solaris, and Unknown OS ------------------------ */ /* AIX, BSD, Solaris, and Unknown OS ------------------------ */
return (size_t)0L; /* Unsupported. */ return static_cast<size_t>(0L); /* Unsupported. */
#endif #endif
} }

View File

@@ -1,201 +1,24 @@
#include "NiPoint3.h" #include "NiPoint3.h"
#include "NiQuaternion.h"
// C++ // C++
#include <cmath> #include <cmath>
// Static Variables // MARK: Member Functions
const NiPoint3 NiPoint3::ZERO(0.0f, 0.0f, 0.0f);
const NiPoint3 NiPoint3::UNIT_X(1.0f, 0.0f, 0.0f);
const NiPoint3 NiPoint3::UNIT_Y(0.0f, 1.0f, 0.0f);
const NiPoint3 NiPoint3::UNIT_Z(0.0f, 0.0f, 1.0f);
const NiPoint3 NiPoint3::UNIT_ALL(1.0f, 1.0f, 1.0f);
//! Initializer
NiPoint3::NiPoint3(void) {
this->x = 0;
this->y = 0;
this->z = 0;
}
//! Initializer
NiPoint3::NiPoint3(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
//! Copy Constructor
NiPoint3::NiPoint3(const NiPoint3& point) {
this->x = point.x;
this->y = point.y;
this->z = point.z;
}
//! Destructor
NiPoint3::~NiPoint3(void) {}
// MARK: Getters / Setters
//! Gets the X coordinate
float NiPoint3::GetX(void) const {
return this->x;
}
//! Sets the X coordinate
void NiPoint3::SetX(float x) {
this->x = x;
}
//! Gets the Y coordinate
float NiPoint3::GetY(void) const {
return this->y;
}
//! Sets the Y coordinate
void NiPoint3::SetY(float y) {
this->y = y;
}
//! Gets the Z coordinate
float NiPoint3::GetZ(void) const {
return this->z;
}
//! Sets the Z coordinate
void NiPoint3::SetZ(float z) {
this->z = z;
}
// MARK: Functions
//! Gets the length of the vector //! Gets the length of the vector
float NiPoint3::Length(void) const { float NiPoint3::Length() const {
return sqrt(x * x + y * y + z * z); return std::sqrt(x * x + y * y + z * z);
}
//! Gets the squared length of a vector
float NiPoint3::SquaredLength(void) const {
return (x * x + y * y + z * z);
}
//! Returns the dot product of the vector dotted with another vector
float NiPoint3::DotProduct(const Vector3& vec) const {
return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z));
}
//! Returns the cross product of the vector crossed with another vector
Vector3 NiPoint3::CrossProduct(const Vector3& vec) const {
return Vector3(((this->y * vec.z) - (this->z * vec.y)),
((this->z * vec.x) - (this->x * vec.z)),
((this->x * vec.y) - (this->y * vec.x)));
} }
//! Unitize the vector //! Unitize the vector
NiPoint3 NiPoint3::Unitize(void) const { NiPoint3 NiPoint3::Unitize() const {
float length = this->Length(); float length = this->Length();
return length != 0 ? *this / length : NiPoint3::ZERO; return length != 0 ? *this / length : NiPoint3Constant::ZERO;
} }
// MARK: Operators
//! Operator to check for equality
bool NiPoint3::operator==(const NiPoint3& point) const {
return point.x == this->x && point.y == this->y && point.z == this->z;
}
//! Operator to check for inequality
bool NiPoint3::operator!=(const NiPoint3& point) const {
return !(*this == point);
}
//! Operator for subscripting
float& NiPoint3::operator[](int i) {
float* base = &x;
return (float&)base[i];
}
//! Operator for subscripting
const float& NiPoint3::operator[](int i) const {
const float* base = &x;
return (float&)base[i];
}
//! Operator for addition of vectors
NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
}
//! Operator for addition of vectors
NiPoint3 NiPoint3::operator+=(const NiPoint3& point) const {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
}
//! Operator for subtraction of vectors
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
}
//! Operator for addition of a scalar on all vector components
NiPoint3 NiPoint3::operator+(float fScalar) const {
return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar);
}
//! Operator for subtraction of a scalar on all vector components
NiPoint3 NiPoint3::operator-(float fScalar) const {
return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar);
}
//! Operator for scalar multiplication of a vector
NiPoint3 NiPoint3::operator*(float fScalar) const {
return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar);
}
//! Operator for scalar division of a vector
NiPoint3 NiPoint3::operator/(float fScalar) const {
float retX = this->x != 0 ? this->x / fScalar : 0;
float retY = this->y != 0 ? this->y / fScalar : 0;
float retZ = this->z != 0 ? this->z / fScalar : 0;
return NiPoint3(retX, retY, retZ);
}
// MARK: Helper Functions // MARK: Helper Functions
//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box
bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) {
if (this->x < minPoint.x) return false;
if (this->x > maxPoint.x) return false;
if (this->y < minPoint.y) return false;
if (this->y > maxPoint.y) return false;
return (this->z < maxPoint.z&& this->z > minPoint.z);
}
//! Checks to see if the point (or vector) is within a sphere
bool NiPoint3::IsWithinSpehere(const NiPoint3& sphereCenter, float radius) {
Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ());
return (diffVec.SquaredLength() <= (radius * radius));
}
NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) {
if (a == b) return a;
const auto pa = p - a;
const auto ab = b - a;
const auto t = pa.DotProduct(ab) / ab.SquaredLength();
if (t <= 0.0f) return a;
if (t >= 1.0f) return b;
return a + ab * t;
}
float NiPoint3::Angle(const NiPoint3& a, const NiPoint3& b) { float NiPoint3::Angle(const NiPoint3& a, const NiPoint3& b) {
const auto dot = a.DotProduct(b); const auto dot = a.DotProduct(b);
const auto lenA = a.SquaredLength(); const auto lenA = a.SquaredLength();
@@ -211,47 +34,24 @@ float NiPoint3::Distance(const NiPoint3& a, const NiPoint3& b) {
return std::sqrt(dx * dx + dy * dy + dz * dz); return std::sqrt(dx * dx + dy * dy + dz * dz);
} }
float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) { NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta) {
const auto dx = a.x - b.x;
const auto dy = a.y - b.y;
const auto dz = a.z - b.z;
return dx * dx + dy * dy + dz * dz;
}
NiPoint3 NiPoint3::MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta) {
float dx = target.x - current.x; float dx = target.x - current.x;
float dy = target.y - current.y; float dy = target.y - current.y;
float dz = target.z - current.z; float dz = target.z - current.z;
float lengthSquared = (float)((double)dx * (double)dx + (double)dy * (double)dy + (double)dz * (double)dz);
if ((double)lengthSquared == 0.0 || (double)maxDistanceDelta >= 0.0 && (double)lengthSquared <= (double)maxDistanceDelta * (double)maxDistanceDelta) float lengthSquared = static_cast<float>(
static_cast<double>(dx) * static_cast<double>(dx) +
static_cast<double>(dy) * static_cast<double>(dy) +
static_cast<double>(dz) * static_cast<double>(dz)
);
if (static_cast<double>(lengthSquared) == 0.0
|| static_cast<double>(maxDistanceDelta) >= 0.0
&& static_cast<double>(lengthSquared)
<= static_cast<double>(maxDistanceDelta) * static_cast<double>(maxDistanceDelta)) {
return target; return target;
float length = (float)std::sqrt((double)lengthSquared); }
float length = std::sqrt(lengthSquared);
return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta); return NiPoint3(current.x + dx / length * maxDistanceDelta, current.y + dy / length * maxDistanceDelta, current.z + dz / length * maxDistanceDelta);
} }
//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) {
Vector3 vector;
float num12 = rotation.x + rotation.x;
float num2 = rotation.y + rotation.y;
float num = rotation.z + rotation.z;
float num11 = rotation.w * num12;
float num10 = rotation.w * num2;
float num9 = rotation.w * num;
float num8 = rotation.x * num12;
float num7 = rotation.x * num2;
float num6 = rotation.x * num;
float num5 = rotation.y * num2;
float num4 = rotation.y * num;
float num3 = rotation.z * num;
NiPoint3 value = *this;
float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10));
float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11));
float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5));
vector.x = num15;
vector.y = num14;
vector.z = num13;
return vector;
}

View File

@@ -1,4 +1,5 @@
#pragma once #ifndef __NIPOINT3_H__
#define __NIPOINT3_H__
/*! /*!
\file NiPoint3.hpp \file NiPoint3.hpp
@@ -12,13 +13,13 @@ typedef NiPoint3 Vector3; //!< The Vector3 class is technically the NiPoin
//! A custom class the defines a point in space //! A custom class the defines a point in space
class NiPoint3 { class NiPoint3 {
public: public:
float x; //!< The x position float x{ 0 }; //!< The x position
float y; //!< The y position float y{ 0 }; //!< The y position
float z; //!< The z position float z{ 0 }; //!< The z position
//! Initializer //! Initializer
NiPoint3(void); constexpr NiPoint3() = default;
//! Initializer //! Initializer
/*! /*!
@@ -26,23 +27,21 @@ public:
\param y The y coordinate \param y The y coordinate
\param z The z coordinate \param z The z coordinate
*/ */
NiPoint3(float x, float y, float z); constexpr NiPoint3(const float x, const float y, const float z) noexcept
: x{ x }
, y{ y }
, z{ z } {
}
//! Copy Constructor //! Copy Constructor
/*! /*!
\param point The point to copy \param point The point to copy
*/ */
NiPoint3(const NiPoint3& point); constexpr NiPoint3(const NiPoint3& point) noexcept
: x{ point.x }
//! Destructor , y{ point.y }
~NiPoint3(void); , z{ point.z } {
}
// MARK: Constants
static const NiPoint3 ZERO; //!< Point(0, 0, 0)
static const NiPoint3 UNIT_X; //!< Point(1, 0, 0)
static const NiPoint3 UNIT_Y; //!< Point(0, 1, 0)
static const NiPoint3 UNIT_Z; //!< Point(0, 0, 1)
static const NiPoint3 UNIT_ALL; //!< Point(1, 1, 1)
// MARK: Getters / Setters // MARK: Getters / Setters
@@ -50,38 +49,37 @@ public:
/*! /*!
\return The x coordinate \return The x coordinate
*/ */
float GetX(void) const; [[nodiscard]] constexpr float GetX() const noexcept;
//! Sets the X coordinate //! Sets the X coordinate
/*! /*!
\param x The x coordinate \param x The x coordinate
*/ */
void SetX(float x); constexpr void SetX(const float x) noexcept;
//! Gets the Y coordinate //! Gets the Y coordinate
/*! /*!
\return The y coordinate \return The y coordinate
*/ */
float GetY(void) const; [[nodiscard]] constexpr float GetY() const noexcept;
//! Sets the Y coordinate //! Sets the Y coordinate
/*! /*!
\param y The y coordinate \param y The y coordinate
*/ */
void SetY(float y); constexpr void SetY(const float y) noexcept;
//! Gets the Z coordinate //! Gets the Z coordinate
/*! /*!
\return The z coordinate \return The z coordinate
*/ */
float GetZ(void) const; [[nodiscard]] constexpr float GetZ() const noexcept;
//! Sets the Z coordinate //! Sets the Z coordinate
/*! /*!
\param z The z coordinate \param z The z coordinate
*/ */
void SetZ(float z); constexpr void SetZ(const float z) noexcept;
// MARK: Member Functions // MARK: Member Functions
@@ -89,70 +87,70 @@ public:
/*! /*!
\return The scalar length of the vector \return The scalar length of the vector
*/ */
float Length(void) const; [[nodiscard]] float Length() const;
//! Gets the squared length of a vector //! Gets the squared length of a vector
/*! /*!
\return The squared length of a vector \return The squared length of a vector
*/ */
float SquaredLength(void) const; [[nodiscard]] constexpr float SquaredLength() const noexcept;
//! Returns the dot product of the vector dotted with another vector //! Returns the dot product of the vector dotted with another vector
/*! /*!
\param vec The second vector \param vec The second vector
\return The dot product of the two vectors \return The dot product of the two vectors
*/ */
float DotProduct(const Vector3& vec) const; [[nodiscard]] constexpr float DotProduct(const Vector3& vec) const noexcept;
//! Returns the cross product of the vector crossed with another vector //! Returns the cross product of the vector crossed with another vector
/*! /*!
\param vec The second vector \param vec The second vector
\return The cross product of the two vectors \return The cross product of the two vectors
*/ */
Vector3 CrossProduct(const Vector3& vec) const; [[nodiscard]] constexpr Vector3 CrossProduct(const Vector3& vec) const noexcept;
//! Unitize the vector //! Unitize the vector
/*! /*!
\returns The current vector \returns The current vector
*/ */
NiPoint3 Unitize(void) const; [[nodiscard]] NiPoint3 Unitize() const;
// MARK: Operators // MARK: Operators
//! Operator to check for equality //! Operator to check for equality
bool operator==(const NiPoint3& point) const; constexpr bool operator==(const NiPoint3& point) const noexcept;
//! Operator to check for inequality //! Operator to check for inequality
bool operator!=(const NiPoint3& point) const; constexpr bool operator!=(const NiPoint3& point) const noexcept;
//! Operator for subscripting //! Operator for subscripting
float& operator[](int i); constexpr float& operator[](const int i) noexcept;
//! Operator for subscripting //! Operator for subscripting
const float& operator[](int i) const; constexpr const float& operator[](const int i) const noexcept;
//! Operator for addition of vectors //! Operator for addition of vectors
NiPoint3 operator+(const NiPoint3& point) const; constexpr NiPoint3 operator+(const NiPoint3& point) const noexcept;
//! Operator for addition of vectors //! Operator for addition of vectors
NiPoint3 operator+=(const NiPoint3& point) const; constexpr NiPoint3& operator+=(const NiPoint3& point) noexcept;
constexpr NiPoint3& operator*=(const float scalar) noexcept;
//! Operator for subtraction of vectors //! Operator for subtraction of vectors
NiPoint3 operator-(const NiPoint3& point) const; constexpr NiPoint3 operator-(const NiPoint3& point) const noexcept;
//! Operator for addition of a scalar on all vector components //! Operator for addition of a scalar on all vector components
NiPoint3 operator+(float fScalar) const; constexpr NiPoint3 operator+(const float fScalar) const noexcept;
//! Operator for subtraction of a scalar on all vector components //! Operator for subtraction of a scalar on all vector components
NiPoint3 operator-(float fScalar) const; constexpr NiPoint3 operator-(const float fScalar) const noexcept;
//! Operator for scalar multiplication of a vector //! Operator for scalar multiplication of a vector
NiPoint3 operator*(float fScalar) const; constexpr NiPoint3 operator*(const float fScalar) const noexcept;
//! Operator for scalar division of a vector //! Operator for scalar division of a vector
NiPoint3 operator/(float fScalar) const; constexpr NiPoint3 operator/(const float fScalar) const noexcept;
// MARK: Helper Functions // MARK: Helper Functions
@@ -162,14 +160,14 @@ public:
\param maxPoint The maximum point of the bounding box \param maxPoint The maximum point of the bounding box
\return Whether or not this point lies within the box \return Whether or not this point lies within the box
*/ */
bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint); [[nodiscard]] constexpr bool IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept;
//! Checks to see if the point (or vector) is within a sphere //! Checks to see if the point (or vector) is within a sphere
/*! /*!
\param sphereCenter The sphere center \param sphereCenter The sphere center
\param radius The radius \param radius The radius
*/ */
bool IsWithinSpehere(const NiPoint3& sphereCenter, float radius); [[nodiscard]] constexpr bool IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept;
/*! /*!
\param a Start of line \param a Start of line
@@ -177,15 +175,30 @@ public:
\param p Refrence point \param p Refrence point
\return The point of line AB which is closest to P \return The point of line AB which is closest to P
*/ */
static NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p); [[nodiscard]] static constexpr NiPoint3 ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept;
static float Angle(const NiPoint3& a, const NiPoint3& b); [[nodiscard]] static float Angle(const NiPoint3& a, const NiPoint3& b);
static float Distance(const NiPoint3& a, const NiPoint3& b); [[nodiscard]] static float Distance(const NiPoint3& a, const NiPoint3& b);
static float DistanceSquared(const NiPoint3& a, const NiPoint3& b); [[nodiscard]] static constexpr float DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept;
static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, float maxDistanceDelta); [[nodiscard]] static NiPoint3 MoveTowards(const NiPoint3& current, const NiPoint3& target, const float maxDistanceDelta);
NiPoint3 RotateByQuaternion(const NiQuaternion& rotation); //This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
[[nodiscard]] constexpr NiPoint3 RotateByQuaternion(const NiQuaternion& rotation) noexcept;
}; };
// Static Variables
namespace NiPoint3Constant {
constexpr NiPoint3 ZERO(0.0f, 0.0f, 0.0f);
constexpr NiPoint3 UNIT_X(1.0f, 0.0f, 0.0f);
constexpr NiPoint3 UNIT_Y(0.0f, 1.0f, 0.0f);
constexpr NiPoint3 UNIT_Z(0.0f, 0.0f, 1.0f);
constexpr NiPoint3 UNIT_ALL(1.0f, 1.0f, 1.0f);
}
// .inl file needed for code organization and to circumvent circular dependency issues
#include "NiPoint3.inl"
#endif // !__NIPOINT3_H__

196
dCommon/NiPoint3.inl Normal file
View File

@@ -0,0 +1,196 @@
#pragma once
#ifndef __NIPOINT3_H__
#error "This should only be included inline in NiPoint3.h: Do not include directly!"
#endif
#include "NiQuaternion.h"
// MARK: Getters / Setters
//! Gets the X coordinate
constexpr float NiPoint3::GetX() const noexcept {
return this->x;
}
//! Sets the X coordinate
constexpr void NiPoint3::SetX(const float x) noexcept {
this->x = x;
}
//! Gets the Y coordinate
constexpr float NiPoint3::GetY() const noexcept {
return this->y;
}
//! Sets the Y coordinate
constexpr void NiPoint3::SetY(const float y) noexcept {
this->y = y;
}
//! Gets the Z coordinate
constexpr float NiPoint3::GetZ() const noexcept {
return this->z;
}
//! Sets the Z coordinate
constexpr void NiPoint3::SetZ(const float z) noexcept {
this->z = z;
}
// MARK: Member Functions
//! Gets the squared length of a vector
constexpr float NiPoint3::SquaredLength() const noexcept {
return (x * x + y * y + z * z);
}
//! Returns the dot product of the vector dotted with another vector
constexpr float NiPoint3::DotProduct(const Vector3& vec) const noexcept {
return ((this->x * vec.x) + (this->y * vec.y) + (this->z * vec.z));
}
//! Returns the cross product of the vector crossed with another vector
constexpr Vector3 NiPoint3::CrossProduct(const Vector3& vec) const noexcept {
return Vector3(((this->y * vec.z) - (this->z * vec.y)),
((this->z * vec.x) - (this->x * vec.z)),
((this->x * vec.y) - (this->y * vec.x)));
}
// MARK: Operators
//! Operator to check for equality
constexpr bool NiPoint3::operator==(const NiPoint3& point) const noexcept {
return point.x == this->x && point.y == this->y && point.z == this->z;
}
//! Operator to check for inequality
constexpr bool NiPoint3::operator!=(const NiPoint3& point) const noexcept {
return !(*this == point);
}
//! Operator for subscripting
constexpr float& NiPoint3::operator[](const int i) noexcept {
float* base = &x;
return base[i];
}
//! Operator for subscripting
constexpr const float& NiPoint3::operator[](const int i) const noexcept {
const float* base = &x;
return base[i];
}
//! Operator for addition of vectors
constexpr NiPoint3 NiPoint3::operator+(const NiPoint3& point) const noexcept {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
}
//! Operator for addition of vectors
constexpr NiPoint3& NiPoint3::operator+=(const NiPoint3& point) noexcept {
this->x += point.x;
this->y += point.y;
this->z += point.z;
return *this;
}
constexpr NiPoint3& NiPoint3::operator*=(const float scalar) noexcept {
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
return *this;
}
//! Operator for subtraction of vectors
constexpr NiPoint3 NiPoint3::operator-(const NiPoint3& point) const noexcept {
return NiPoint3(this->x - point.x, this->y - point.y, this->z - point.z);
}
//! Operator for addition of a scalar on all vector components
constexpr NiPoint3 NiPoint3::operator+(const float fScalar) const noexcept {
return NiPoint3(this->x + fScalar, this->y + fScalar, this->z + fScalar);
}
//! Operator for subtraction of a scalar on all vector components
constexpr NiPoint3 NiPoint3::operator-(const float fScalar) const noexcept {
return NiPoint3(this->x - fScalar, this->y - fScalar, this->z - fScalar);
}
//! Operator for scalar multiplication of a vector
constexpr NiPoint3 NiPoint3::operator*(const float fScalar) const noexcept {
return NiPoint3(this->x * fScalar, this->y * fScalar, this->z * fScalar);
}
//! Operator for scalar division of a vector
constexpr NiPoint3 NiPoint3::operator/(const float fScalar) const noexcept {
float retX = this->x != 0 ? this->x / fScalar : 0;
float retY = this->y != 0 ? this->y / fScalar : 0;
float retZ = this->z != 0 ? this->z / fScalar : 0;
return NiPoint3(retX, retY, retZ);
}
// MARK: Helper Functions
//! Checks to see if the point (or vector) is with an Axis-Aligned Bounding Box
constexpr bool NiPoint3::IsWithinAxisAlignedBox(const NiPoint3& minPoint, const NiPoint3& maxPoint) noexcept {
if (this->x < minPoint.x) return false;
if (this->x > maxPoint.x) return false;
if (this->y < minPoint.y) return false;
if (this->y > maxPoint.y) return false;
return (this->z < maxPoint.z && this->z > minPoint.z);
}
//! Checks to see if the point (or vector) is within a sphere
constexpr bool NiPoint3::IsWithinSphere(const NiPoint3& sphereCenter, const float radius) noexcept {
Vector3 diffVec = Vector3(x - sphereCenter.GetX(), y - sphereCenter.GetY(), z - sphereCenter.GetZ());
return (diffVec.SquaredLength() <= (radius * radius));
}
constexpr NiPoint3 NiPoint3::ClosestPointOnLine(const NiPoint3& a, const NiPoint3& b, const NiPoint3& p) noexcept {
if (a == b) return a;
const auto pa = p - a;
const auto ab = b - a;
const auto t = pa.DotProduct(ab) / ab.SquaredLength();
if (t <= 0.0f) return a;
if (t >= 1.0f) return b;
return a + ab * t;
}
constexpr float NiPoint3::DistanceSquared(const NiPoint3& a, const NiPoint3& b) noexcept {
const auto dx = a.x - b.x;
const auto dy = a.y - b.y;
const auto dz = a.z - b.z;
return dx * dx + dy * dy + dz * dz;
}
//This code is yoinked from the MS XNA code, so it should be right, even if it's horrible.
constexpr NiPoint3 NiPoint3::RotateByQuaternion(const NiQuaternion& rotation) noexcept {
Vector3 vector;
float num12 = rotation.x + rotation.x;
float num2 = rotation.y + rotation.y;
float num = rotation.z + rotation.z;
float num11 = rotation.w * num12;
float num10 = rotation.w * num2;
float num9 = rotation.w * num;
float num8 = rotation.x * num12;
float num7 = rotation.x * num2;
float num6 = rotation.x * num;
float num5 = rotation.y * num2;
float num4 = rotation.y * num;
float num3 = rotation.z * num;
NiPoint3 value = *this;
float num15 = ((value.x * ((1.0f - num5) - num3)) + (value.y * (num7 - num9))) + (value.z * (num6 + num10));
float num14 = ((value.x * (num7 + num9)) + (value.y * ((1.0f - num8) - num3))) + (value.z * (num4 - num11));
float num13 = ((value.x * (num6 - num10)) + (value.y * (num4 + num11))) + (value.z * ((1.0f - num8) - num5));
vector.x = num15;
vector.y = num14;
vector.z = num13;
return vector;
}

View File

@@ -3,89 +3,8 @@
// C++ // C++
#include <cmath> #include <cmath>
// Static Variables
const NiQuaternion NiQuaternion::IDENTITY(1, 0, 0, 0);
//! The initializer
NiQuaternion::NiQuaternion(void) {
this->w = 1;
this->x = 0;
this->y = 0;
this->z = 0;
}
//! The initializer
NiQuaternion::NiQuaternion(float w, float x, float y, float z) {
this->w = w;
this->x = x;
this->y = y;
this->z = z;
}
//! Destructor
NiQuaternion::~NiQuaternion(void) {}
// MARK: Setters / Getters
//! Gets the W coordinate
float NiQuaternion::GetW(void) const {
return this->w;
}
//! Sets the W coordinate
void NiQuaternion::SetW(float w) {
this->w = w;
}
//! Gets the X coordinate
float NiQuaternion::GetX(void) const {
return this->x;
}
//! Sets the X coordinate
void NiQuaternion::SetX(float x) {
this->x = x;
}
//! Gets the Y coordinate
float NiQuaternion::GetY(void) const {
return this->y;
}
//! Sets the Y coordinate
void NiQuaternion::SetY(float y) {
this->y = y;
}
//! Gets the Z coordinate
float NiQuaternion::GetZ(void) const {
return this->z;
}
//! Sets the Z coordinate
void NiQuaternion::SetZ(float z) {
this->z = z;
}
// MARK: Member Functions // MARK: Member Functions
//! Returns the forward vector from the quaternion
Vector3 NiQuaternion::GetForwardVector(void) const {
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
}
//! Returns the up vector from the quaternion
Vector3 NiQuaternion::GetUpVector(void) const {
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
}
//! Returns the right vector from the quaternion
Vector3 NiQuaternion::GetRightVector(void) const {
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
}
Vector3 NiQuaternion::GetEulerAngles() const { Vector3 NiQuaternion::GetEulerAngles() const {
Vector3 angles; Vector3 angles;
@@ -111,22 +30,9 @@ Vector3 NiQuaternion::GetEulerAngles() const {
return angles; return angles;
} }
// MARK: Operators
//! Operator to check for equality
bool NiQuaternion::operator==(const NiQuaternion& rot) const {
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
}
//! Operator to check for inequality
bool NiQuaternion::operator!=(const NiQuaternion& rot) const {
return !(*this == rot);
}
// MARK: Helper Functions // MARK: Helper Functions
//! Look from a specific point in space to another point in space //! Look from a specific point in space to another point in space (Y-locked)
NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
//To make sure we don't orient around the X/Z axis: //To make sure we don't orient around the X/Z axis:
NiPoint3 source = sourcePoint; NiPoint3 source = sourcePoint;
@@ -136,7 +42,7 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
NiPoint3 forwardVector = NiPoint3(dest - source).Unitize(); NiPoint3 forwardVector = NiPoint3(dest - source).Unitize();
NiPoint3 posZ = NiPoint3::UNIT_Z; NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize(); NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
float dot = posZ.DotProduct(forwardVector); float dot = posZ.DotProduct(forwardVector);
@@ -148,10 +54,11 @@ NiQuaternion NiQuaternion::LookAt(const NiPoint3& sourcePoint, const NiPoint3& d
return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle); return NiQuaternion::CreateFromAxisAngle(vecA, rotAngle);
} }
//! Look from a specific point in space to another point in space
NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) { NiQuaternion NiQuaternion::LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint) {
NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize(); NiPoint3 forwardVector = NiPoint3(destPoint - sourcePoint).Unitize();
NiPoint3 posZ = NiPoint3::UNIT_Z; NiPoint3 posZ = NiPoint3Constant::UNIT_Z;
NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize(); NiPoint3 vecA = posZ.CrossProduct(forwardVector).Unitize();
float dot = posZ.DotProduct(forwardVector); float dot = posZ.DotProduct(forwardVector);

View File

@@ -1,4 +1,5 @@
#pragma once #ifndef __NIQUATERNION_H__
#define __NIQUATERNION_H__
// Custom Classes // Custom Classes
#include "NiPoint3.h" #include "NiPoint3.h"
@@ -14,14 +15,14 @@ typedef NiQuaternion Quaternion; //!< A typedef for a shorthand version o
//! A class that defines a rotation in space //! A class that defines a rotation in space
class NiQuaternion { class NiQuaternion {
public: public:
float w; //!< The w coordinate float w{ 1 }; //!< The w coordinate
float x; //!< The x coordinate float x{ 0 }; //!< The x coordinate
float y; //!< The y coordinate float y{ 0 }; //!< The y coordinate
float z; //!< The z coordinate float z{ 0 }; //!< The z coordinate
//! The initializer //! The initializer
NiQuaternion(void); constexpr NiQuaternion() = default;
//! The initializer //! The initializer
/*! /*!
@@ -30,13 +31,12 @@ public:
\param y The y coordinate \param y The y coordinate
\param z The z coordinate \param z The z coordinate
*/ */
NiQuaternion(float w, float x, float y, float z); constexpr NiQuaternion(const float w, const float x, const float y, const float z) noexcept
: w{ w }
//! Destructor , x{ x }
~NiQuaternion(void); , y{ y }
, z{ z } {
// MARK: Constants }
static const NiQuaternion IDENTITY; //!< Quaternion(1, 0, 0, 0)
// MARK: Setters / Getters // MARK: Setters / Getters
@@ -44,50 +44,49 @@ public:
/*! /*!
\return The w coordinate \return The w coordinate
*/ */
float GetW(void) const; [[nodiscard]] constexpr float GetW() const noexcept;
//! Sets the W coordinate //! Sets the W coordinate
/*! /*!
\param w The w coordinate \param w The w coordinate
*/ */
void SetW(float w); constexpr void SetW(const float w) noexcept;
//! Gets the X coordinate //! Gets the X coordinate
/*! /*!
\return The x coordinate \return The x coordinate
*/ */
float GetX(void) const; [[nodiscard]] constexpr float GetX() const noexcept;
//! Sets the X coordinate //! Sets the X coordinate
/*! /*!
\param x The x coordinate \param x The x coordinate
*/ */
void SetX(float x); constexpr void SetX(const float x) noexcept;
//! Gets the Y coordinate //! Gets the Y coordinate
/*! /*!
\return The y coordinate \return The y coordinate
*/ */
float GetY(void) const; [[nodiscard]] constexpr float GetY() const noexcept;
//! Sets the Y coordinate //! Sets the Y coordinate
/*! /*!
\param y The y coordinate \param y The y coordinate
*/ */
void SetY(float y); constexpr void SetY(const float y) noexcept;
//! Gets the Z coordinate //! Gets the Z coordinate
/*! /*!
\return The z coordinate \return The z coordinate
*/ */
float GetZ(void) const; [[nodiscard]] constexpr float GetZ() const noexcept;
//! Sets the Z coordinate //! Sets the Z coordinate
/*! /*!
\param z The z coordinate \param z The z coordinate
*/ */
void SetZ(float z); constexpr void SetZ(const float z) noexcept;
// MARK: Member Functions // MARK: Member Functions
@@ -95,31 +94,29 @@ public:
/*! /*!
\return The forward vector of the quaternion \return The forward vector of the quaternion
*/ */
Vector3 GetForwardVector(void) const; [[nodiscard]] constexpr Vector3 GetForwardVector() const noexcept;
//! Returns the up vector from the quaternion //! Returns the up vector from the quaternion
/*! /*!
\return The up vector fo the quaternion \return The up vector fo the quaternion
*/ */
Vector3 GetUpVector(void) const; [[nodiscard]] constexpr Vector3 GetUpVector() const noexcept;
//! Returns the right vector from the quaternion //! Returns the right vector from the quaternion
/*! /*!
\return The right vector of the quaternion \return The right vector of the quaternion
*/ */
Vector3 GetRightVector(void) const; [[nodiscard]] constexpr Vector3 GetRightVector() const noexcept;
Vector3 GetEulerAngles() const;
[[nodiscard]] Vector3 GetEulerAngles() const;
// MARK: Operators // MARK: Operators
//! Operator to check for equality //! Operator to check for equality
bool operator==(const NiQuaternion& rot) const; constexpr bool operator==(const NiQuaternion& rot) const noexcept;
//! Operator to check for inequality //! Operator to check for inequality
bool operator!=(const NiQuaternion& rot) const; constexpr bool operator!=(const NiQuaternion& rot) const noexcept;
// MARK: Helper Functions // MARK: Helper Functions
@@ -129,7 +126,7 @@ public:
\param destPoint The destination location \param destPoint The destination location
\return The Quaternion with the rotation towards the destination \return The Quaternion with the rotation towards the destination
*/ */
static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint); [[nodiscard]] static NiQuaternion LookAt(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
//! Look from a specific point in space to another point in space //! Look from a specific point in space to another point in space
/*! /*!
@@ -137,7 +134,7 @@ public:
\param destPoint The destination location \param destPoint The destination location
\return The Quaternion with the rotation towards the destination \return The Quaternion with the rotation towards the destination
*/ */
static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint); [[nodiscard]] static NiQuaternion LookAtUnlocked(const NiPoint3& sourcePoint, const NiPoint3& destPoint);
//! Creates a Quaternion from a specific axis and angle relative to that axis //! Creates a Quaternion from a specific axis and angle relative to that axis
/*! /*!
@@ -145,7 +142,17 @@ public:
\param angle The angle relative to this axis \param angle The angle relative to this axis
\return A quaternion created from the axis and angle \return A quaternion created from the axis and angle
*/ */
static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle); [[nodiscard]] static NiQuaternion CreateFromAxisAngle(const Vector3& axis, float angle);
static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles); [[nodiscard]] static NiQuaternion FromEulerAngles(const NiPoint3& eulerAngles);
}; };
// Static Variables
namespace NiQuaternionConstant {
constexpr NiQuaternion IDENTITY(1, 0, 0, 0);
}
// Include constexpr and inline function definitions in a seperate file for readability
#include "NiQuaternion.inl"
#endif // !__NIQUATERNION_H__

75
dCommon/NiQuaternion.inl Normal file
View File

@@ -0,0 +1,75 @@
#pragma once
#ifndef __NIQUATERNION_H__
#error "This should only be included inline in NiQuaternion.h: Do not include directly!"
#endif
// MARK: Setters / Getters
//! Gets the W coordinate
constexpr float NiQuaternion::GetW() const noexcept {
return this->w;
}
//! Sets the W coordinate
constexpr void NiQuaternion::SetW(const float w) noexcept {
this->w = w;
}
//! Gets the X coordinate
constexpr float NiQuaternion::GetX() const noexcept {
return this->x;
}
//! Sets the X coordinate
constexpr void NiQuaternion::SetX(const float x) noexcept {
this->x = x;
}
//! Gets the Y coordinate
constexpr float NiQuaternion::GetY() const noexcept {
return this->y;
}
//! Sets the Y coordinate
constexpr void NiQuaternion::SetY(const float y) noexcept {
this->y = y;
}
//! Gets the Z coordinate
constexpr float NiQuaternion::GetZ() const noexcept {
return this->z;
}
//! Sets the Z coordinate
constexpr void NiQuaternion::SetZ(const float z) noexcept {
this->z = z;
}
// MARK: Member Functions
//! Returns the forward vector from the quaternion
constexpr Vector3 NiQuaternion::GetForwardVector() const noexcept {
return Vector3(2 * (x * z + w * y), 2 * (y * z - w * x), 1 - 2 * (x * x + y * y));
}
//! Returns the up vector from the quaternion
constexpr Vector3 NiQuaternion::GetUpVector() const noexcept {
return Vector3(2 * (x * y - w * z), 1 - 2 * (x * x + z * z), 2 * (y * z + w * x));
}
//! Returns the right vector from the quaternion
constexpr Vector3 NiQuaternion::GetRightVector() const noexcept {
return Vector3(1 - 2 * (y * y + z * z), 2 * (x * y + w * z), 2 * (x * z - w * y));
}
// MARK: Operators
//! Operator to check for equality
constexpr bool NiQuaternion::operator==(const NiQuaternion& rot) const noexcept {
return rot.x == this->x && rot.y == this->y && rot.z == this->z && rot.w == this->w;
}
//! Operator to check for inequality
constexpr bool NiQuaternion::operator!=(const NiQuaternion& rot) const noexcept {
return !(*this == rot);
}

36
dCommon/PositionUpdate.h Normal file
View File

@@ -0,0 +1,36 @@
#ifndef __POSITIONUPDATE__H__
#define __POSITIONUPDATE__H__
#include "NiPoint3.h"
#include "NiQuaternion.h"
struct RemoteInputInfo {
bool operator==(const RemoteInputInfo& other) {
return m_RemoteInputX == other.m_RemoteInputX && m_RemoteInputY == other.m_RemoteInputY && m_IsPowersliding == other.m_IsPowersliding && m_IsModified == other.m_IsModified;
}
float m_RemoteInputX = 0;
float m_RemoteInputY = 0;
bool m_IsPowersliding = false;
bool m_IsModified = false;
};
struct LocalSpaceInfo {
LWOOBJID objectId = LWOOBJID_EMPTY;
NiPoint3 position = NiPoint3Constant::ZERO;
NiPoint3 linearVelocity = NiPoint3Constant::ZERO;
};
struct PositionUpdate {
NiPoint3 position = NiPoint3Constant::ZERO;
NiQuaternion rotation = NiQuaternionConstant::IDENTITY;
bool onGround = false;
bool onRail = false;
NiPoint3 velocity = NiPoint3Constant::ZERO;
NiPoint3 angularVelocity = NiPoint3Constant::ZERO;
LocalSpaceInfo localSpaceInfo;
RemoteInputInfo remoteInputInfo;
};
#endif //!__POSITIONUPDATE__H__

View File

@@ -1,152 +0,0 @@
#include "SHA512.h"
#include <cstring>
#include <fstream>
const unsigned long long SHA512::sha512_k[80] = //ULL = uint64
{ 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL,
0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL,
0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL,
0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL,
0xd807aa98a3030242ULL, 0x12835b0145706fbeULL,
0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL,
0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL,
0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL,
0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL,
0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL,
0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL,
0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL,
0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL,
0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL,
0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL,
0x06ca6351e003826fULL, 0x142929670a0e6e70ULL,
0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL,
0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL,
0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL,
0x81c2c92e47edaee6ULL, 0x92722c851482353bULL,
0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL,
0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL,
0xd192e819d6ef5218ULL, 0xd69906245565a910ULL,
0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL,
0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL,
0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL,
0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL,
0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL,
0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL,
0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL,
0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL,
0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL,
0xca273eceea26619cULL, 0xd186b8c721c0c207ULL,
0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL,
0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL,
0x113f9804bef90daeULL, 0x1b710b35131c471bULL,
0x28db77f523047d84ULL, 0x32caab7b40c72493ULL,
0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL,
0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL,
0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL };
void SHA512::transform(const unsigned char* message, unsigned int block_nb) {
uint64 w[80];
uint64 wv[8];
uint64 t1, t2;
const unsigned char* sub_block;
int i, j;
for (i = 0; i < (int)block_nb; i++) {
sub_block = message + (i << 7);
for (j = 0; j < 16; j++) {
SHA2_PACK64(&sub_block[j << 3], &w[j]);
}
for (j = 16; j < 80; j++) {
w[j] = SHA512_F4(w[j - 2]) + w[j - 7] + SHA512_F3(w[j - 15]) + w[j - 16];
}
for (j = 0; j < 8; j++) {
wv[j] = m_h[j];
}
for (j = 0; j < 80; j++) {
t1 = wv[7] + SHA512_F2(wv[4]) + SHA2_CH(wv[4], wv[5], wv[6])
+ sha512_k[j] + w[j];
t2 = SHA512_F1(wv[0]) + SHA2_MAJ(wv[0], wv[1], wv[2]);
wv[7] = wv[6];
wv[6] = wv[5];
wv[5] = wv[4];
wv[4] = wv[3] + t1;
wv[3] = wv[2];
wv[2] = wv[1];
wv[1] = wv[0];
wv[0] = t1 + t2;
}
for (j = 0; j < 8; j++) {
m_h[j] += wv[j];
}
}
}
void SHA512::init() {
m_h[0] = 0x6a09e667f3bcc908ULL;
m_h[1] = 0xbb67ae8584caa73bULL;
m_h[2] = 0x3c6ef372fe94f82bULL;
m_h[3] = 0xa54ff53a5f1d36f1ULL;
m_h[4] = 0x510e527fade682d1ULL;
m_h[5] = 0x9b05688c2b3e6c1fULL;
m_h[6] = 0x1f83d9abfb41bd6bULL;
m_h[7] = 0x5be0cd19137e2179ULL;
m_len = 0;
m_tot_len = 0;
}
void SHA512::update(const unsigned char* message, unsigned int len) {
unsigned int block_nb;
unsigned int new_len, rem_len, tmp_len;
const unsigned char* shifted_message;
tmp_len = SHA384_512_BLOCK_SIZE - m_len;
rem_len = len < tmp_len ? len : tmp_len;
memcpy(&m_block[m_len], message, rem_len);
if (m_len + len < SHA384_512_BLOCK_SIZE) {
m_len += len;
return;
}
new_len = len - rem_len;
block_nb = new_len / SHA384_512_BLOCK_SIZE;
shifted_message = message + rem_len;
transform(m_block, 1);
transform(shifted_message, block_nb);
rem_len = new_len % SHA384_512_BLOCK_SIZE;
memcpy(m_block, &shifted_message[block_nb << 7], rem_len);
m_len = rem_len;
m_tot_len += (block_nb + 1) << 7;
}
void SHA512::final(unsigned char* digest) {
unsigned int block_nb;
unsigned int pm_len;
unsigned int len_b;
int i;
block_nb = 1 + ((SHA384_512_BLOCK_SIZE - 17)
< (m_len % SHA384_512_BLOCK_SIZE));
len_b = (m_tot_len + m_len) << 3;
pm_len = block_nb << 7;
memset(m_block + m_len, 0, pm_len - m_len);
m_block[m_len] = 0x80;
SHA2_UNPACK32(len_b, m_block + pm_len - 4);
transform(m_block, block_nb);
for (i = 0; i < 8; i++) {
SHA2_UNPACK64(m_h[i], &digest[i << 3]);
}
}
std::string sha512(std::string input) {
unsigned char digest[SHA512::DIGEST_SIZE];
memset(digest, 0, SHA512::DIGEST_SIZE);
class SHA512 ctx;
ctx.init();
ctx.update((unsigned char*)input.c_str(), input.length());
ctx.final(digest);
char buf[2 * SHA512::DIGEST_SIZE + 1];
buf[2 * SHA512::DIGEST_SIZE] = 0;
for (int i = 0; i < SHA512::DIGEST_SIZE; i++)
sprintf(buf + i * 2, "%02x", digest[i]);
return std::string(buf);
}

View File

@@ -1,68 +0,0 @@
#pragma once
// C++
#include <string>
class SHA512 {
protected:
typedef unsigned char uint8;
typedef unsigned int uint32;
typedef unsigned long long uint64;
const static uint64 sha512_k[];
static const unsigned int SHA384_512_BLOCK_SIZE = (1024 / 8);
public:
void init();
void update(const unsigned char* message, unsigned int len);
void final(unsigned char* digest);
static const unsigned int DIGEST_SIZE = (512 / 8);
protected:
void transform(const unsigned char* message, unsigned int block_nb);
unsigned int m_tot_len;
unsigned int m_len;
unsigned char m_block[2 * SHA384_512_BLOCK_SIZE];
uint64 m_h[8];
};
std::string sha512(std::string input);
#define SHA2_SHFR(x, n) (x >> n)
#define SHA2_ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n)))
#define SHA2_ROTL(x, n) ((x << n) | (x >> ((sizeof(x) << 3) - n)))
#define SHA2_CH(x, y, z) ((x & y) ^ (~x & z))
#define SHA2_MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z))
#define SHA512_F1(x) (SHA2_ROTR(x, 28) ^ SHA2_ROTR(x, 34) ^ SHA2_ROTR(x, 39))
#define SHA512_F2(x) (SHA2_ROTR(x, 14) ^ SHA2_ROTR(x, 18) ^ SHA2_ROTR(x, 41))
#define SHA512_F3(x) (SHA2_ROTR(x, 1) ^ SHA2_ROTR(x, 8) ^ SHA2_SHFR(x, 7))
#define SHA512_F4(x) (SHA2_ROTR(x, 19) ^ SHA2_ROTR(x, 61) ^ SHA2_SHFR(x, 6))
#define SHA2_UNPACK32(x, str) \
{ \
*((str) + 3) = (uint8) ((x) ); \
*((str) + 2) = (uint8) ((x) >> 8); \
*((str) + 1) = (uint8) ((x) >> 16); \
*((str) + 0) = (uint8) ((x) >> 24); \
}
#define SHA2_UNPACK64(x, str) \
{ \
*((str) + 7) = (uint8) ((x) ); \
*((str) + 6) = (uint8) ((x) >> 8); \
*((str) + 5) = (uint8) ((x) >> 16); \
*((str) + 4) = (uint8) ((x) >> 24); \
*((str) + 3) = (uint8) ((x) >> 32); \
*((str) + 2) = (uint8) ((x) >> 40); \
*((str) + 1) = (uint8) ((x) >> 48); \
*((str) + 0) = (uint8) ((x) >> 56); \
}
#define SHA2_PACK64(str, x) \
{ \
*(x) = ((uint64) *((str) + 7) ) \
| ((uint64) *((str) + 6) << 8) \
| ((uint64) *((str) + 5) << 16) \
| ((uint64) *((str) + 4) << 24) \
| ((uint64) *((str) + 3) << 32) \
| ((uint64) *((str) + 2) << 40) \
| ((uint64) *((str) + 1) << 48) \
| ((uint64) *((str) + 0) << 56); \
}

View File

@@ -1,27 +0,0 @@
#include "Type.h"
#ifdef __GNUG__
#include <cstdlib>
#include <memory>
#include <cxxabi.h>
std::string demangle(const char* name) {
int status = -4; // some arbitrary value to eliminate the compiler warning
// enable c++11 by passing the flag -std=c++11 to g++
std::unique_ptr<char, void(*)(void*)> res{
abi::__cxa_demangle(name, NULL, NULL, &status),
std::free
};
return (status == 0) ? res.get() : name;
}
#else
// does nothing if not g++
std::string demangle(const char* name) {
return name;
}
#endif

View File

@@ -1,12 +0,0 @@
#pragma once
#include <string>
#include <typeinfo>
std::string demangle(const char* name);
template <class T>
std::string type(const T& t) {
return demangle(typeid(t).name());
}

View File

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

View File

@@ -2,9 +2,9 @@
#include "AssetManager.h" #include "AssetManager.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
#include <zlib.h> #include "zlib.h"
AssetManager::AssetManager(const std::filesystem::path& path) { AssetManager::AssetManager(const std::filesystem::path& path) {
if (!std::filesystem::is_directory(path)) { if (!std::filesystem::is_directory(path)) {
@@ -81,8 +81,8 @@ bool AssetManager::HasFile(const char* name) {
std::replace(fixedName.begin(), fixedName.end(), '/', '\\'); std::replace(fixedName.begin(), fixedName.end(), '/', '\\');
if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName; if (fixedName.rfind("client\\res\\", 0) != 0) fixedName = "client\\res\\" + fixedName;
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4); crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {
@@ -113,7 +113,7 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
#endif #endif
fseek(file, 0, SEEK_END); fseek(file, 0, SEEK_END);
*len = ftell(file); *len = ftell(file);
*data = (char*)malloc(*len); *data = static_cast<char*>(malloc(*len));
fseek(file, 0, SEEK_SET); fseek(file, 0, SEEK_SET);
int32_t readInData = fread(*data, sizeof(uint8_t), *len, file); int32_t readInData = fread(*data, sizeof(uint8_t), *len, file);
fclose(file); fclose(file);
@@ -129,8 +129,8 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
fixedName = "client\\res\\" + fixedName; fixedName = "client\\res\\" + fixedName;
} }
int32_t packIndex = -1; int32_t packIndex = -1;
uint32_t crc = crc32b(0xFFFFFFFF, (uint8_t*)fixedName.c_str(), fixedName.size()); uint32_t crc = crc32b(0xFFFFFFFF, reinterpret_cast<uint8_t*>(const_cast<char*>(fixedName.c_str())), fixedName.size());
crc = crc32b(crc, (Bytef*)"\0\0\0\0", 4); crc = crc32b(crc, reinterpret_cast<Bytef*>(const_cast<char*>("\0\0\0\0")), 4);
for (const auto& item : this->m_PackIndex->GetPackFileIndices()) { for (const auto& item : this->m_PackIndex->GetPackFileIndices()) {
if (item.m_Crc == crc) { if (item.m_Crc == crc) {
@@ -152,13 +152,12 @@ bool AssetManager::GetFile(const char* name, char** data, uint32_t* len) {
return success; return success;
} }
AssetMemoryBuffer AssetManager::GetFileAsBuffer(const char* name) { AssetStream AssetManager::GetFile(const char* name) {
char* buf; char* buf; uint32_t len;
uint32_t len;
bool success = this->GetFile(name, &buf, &len); bool success = this->GetFile(name, &buf, &len);
return AssetMemoryBuffer(buf, len, success); return AssetStream(buf, len, success);
} }
uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) { uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
@@ -168,7 +167,7 @@ uint32_t AssetManager::crc32b(uint32_t base, uint8_t* message, size_t l) {
crc = base; crc = base;
for (i = 0; i < l; i++) { for (i = 0; i < l; i++) {
// xor next byte to upper bits of crc // xor next byte to upper bits of crc
crc ^= (((unsigned int)message[i]) << 24); crc ^= (static_cast<unsigned int>(message[i]) << 24);
for (j = 0; j < 8; j++) { // Do eight times. for (j = 0; j < 8; j++) { // Do eight times.
msb = crc >> 31; msb = crc >> 31;
crc <<= 1; crc <<= 1;

View File

@@ -25,6 +25,10 @@ struct AssetMemoryBuffer : std::streambuf {
this->setg(base, base, base + n); this->setg(base, base, base + n);
} }
~AssetMemoryBuffer() {
if (m_Success) free(m_Base);
}
pos_type seekpos(pos_type sp, std::ios_base::openmode which) override { pos_type seekpos(pos_type sp, std::ios_base::openmode which) override {
return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which); return seekoff(sp - pos_type(off_type(0)), std::ios_base::beg, which);
} }
@@ -40,9 +44,17 @@ struct AssetMemoryBuffer : std::streambuf {
setg(eback(), eback() + off, egptr()); setg(eback(), eback() + off, egptr());
return gptr() - eback(); return gptr() - eback();
} }
};
void close() { struct AssetStream : std::istream {
delete m_Base; AssetStream(char* base, std::ptrdiff_t n, bool success) : std::istream(new AssetMemoryBuffer(base, n, success)) {}
~AssetStream() {
delete rdbuf();
}
operator bool() {
return reinterpret_cast<AssetMemoryBuffer*>(rdbuf())->m_Success;
} }
}; };
@@ -56,7 +68,7 @@ public:
bool HasFile(const char* name); bool HasFile(const char* name);
bool GetFile(const char* name, char** data, uint32_t* len); bool GetFile(const char* name, char** data, uint32_t* len);
AssetMemoryBuffer GetFileAsBuffer(const char* name); AssetStream GetFile(const char* name);
private: private:
void LoadPackIndex(); void LoadPackIndex();

View File

@@ -1,6 +1,6 @@
set(DCOMMON_DCLIENT_SOURCES set(DCOMMON_DCLIENT_SOURCES
"AssetManager.cpp"
"PackIndex.cpp" "PackIndex.cpp"
"Pack.cpp" "Pack.cpp"
"AssetManager.cpp"
PARENT_SCOPE PARENT_SCOPE
) )

View File

@@ -0,0 +1,12 @@
#ifndef __CLIENTVERSION_H__
#define __CLIENTVERSION_H__
#include <cstdint>
namespace ClientVersion {
constexpr uint16_t major = 1;
constexpr uint16_t current = 10;
constexpr uint16_t minor = 64;
}
#endif // !__CLIENTVERSION_H__

View File

@@ -76,7 +76,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
fseek(file, pos, SEEK_SET); fseek(file, pos, SEEK_SET);
if (!isCompressed) { if (!isCompressed) {
char* tempData = (char*)malloc(pkRecord.m_UncompressedSize); char* tempData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file); int32_t readInData = fread(tempData, sizeof(uint8_t), pkRecord.m_UncompressedSize, file);
*data = tempData; *data = tempData;
@@ -90,7 +90,7 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
fseek(file, pos, SEEK_SET); fseek(file, pos, SEEK_SET);
char* decompressedData = (char*)malloc(pkRecord.m_UncompressedSize); char* decompressedData = static_cast<char*>(malloc(pkRecord.m_UncompressedSize));
uint32_t currentReadPos = 0; uint32_t currentReadPos = 0;
while (true) { while (true) {
@@ -100,12 +100,12 @@ bool Pack::ReadFileFromPack(uint32_t crc, char** data, uint32_t* len) {
int32_t readInData = fread(&size, sizeof(uint32_t), 1, file); int32_t readInData = fread(&size, sizeof(uint32_t), 1, file);
pos += 4; // Move pointer position 4 to the right pos += 4; // Move pointer position 4 to the right
char* chunk = (char*)malloc(size); char* chunk = static_cast<char*>(malloc(size));
int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file); int32_t readInData2 = fread(chunk, sizeof(int8_t), size, file);
pos += size; // Move pointer position the amount of bytes read to the right pos += size; // Move pointer position the amount of bytes read to the right
int32_t err; int32_t err;
currentReadPos += ZCompression::Decompress((uint8_t*)chunk, size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err); currentReadPos += ZCompression::Decompress(reinterpret_cast<uint8_t*>(chunk), size, reinterpret_cast<uint8_t*>(decompressedData + currentReadPos), ZCompression::MAX_SD0_CHUNK_SIZE, err);
free(chunk); free(chunk);
} }

View File

@@ -3,6 +3,7 @@
#include <vector> #include <vector>
#include <string> #include <string>
#include <filesystem> #include <filesystem>
#include <fstream>
#pragma pack(push, 1) #pragma pack(push, 1)
struct PackRecord { struct PackRecord {

View File

@@ -1,28 +1,17 @@
#include "PackIndex.h" #include "PackIndex.h"
#include "BinaryIO.h" #include "BinaryIO.h"
#include "Game.h" #include "Game.h"
#include "dLogger.h" #include "Logger.h"
PackIndex::PackIndex(const std::filesystem::path& filePath) { PackIndex::PackIndex(const std::filesystem::path& filePath) {
m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary); m_FileStream = std::ifstream(filePath / "versions" / "primary.pki", std::ios::in | std::ios::binary);
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_Version);
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackPathCount);
for (int i = 0; i < m_PackPathCount; i++) { m_PackPaths.resize(m_PackPathCount);
uint32_t stringLen = 0; for (auto& item : m_PackPaths) {
BinaryIO::BinaryRead<uint32_t>(m_FileStream, stringLen); BinaryIO::ReadString<uint32_t>(m_FileStream, item, BinaryIO::ReadType::String);
std::string path;
for (int j = 0; j < stringLen; j++) {
char inChar;
BinaryIO::BinaryRead<char>(m_FileStream, inChar);
path += inChar;
}
m_PackPaths.push_back(path);
} }
BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount); BinaryIO::BinaryRead<uint32_t>(m_FileStream, m_PackFileIndexCount);
@@ -34,7 +23,7 @@ PackIndex::PackIndex(const std::filesystem::path& filePath) {
m_PackFileIndices.push_back(packFileIndex); m_PackFileIndices.push_back(packFileIndex);
} }
Game::logger->Log("PackIndex", "Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size()); LOG("Loaded pack catalog with %i pack files and %i files", m_PackPaths.size(), m_PackFileIndices.size());
for (auto& item : m_PackPaths) { for (auto& item : m_PackPaths) {
std::replace(item.begin(), item.end(), '\\', '/'); std::replace(item.begin(), item.end(), '\\', '/');

View File

@@ -10,8 +10,23 @@ dConfig::dConfig(const std::string& filepath) {
LoadConfig(); LoadConfig();
} }
std::filesystem::path GetConfigDir() {
std::filesystem::path config_dir = BinaryPathFinder::GetBinaryDir();
if (const char* env_p = std::getenv("DLU_CONFIG_DIR")) {
config_dir /= env_p;
}
return config_dir;
}
const bool dConfig::Exists(const std::string& filepath) {
std::filesystem::path config_dir = GetConfigDir();
return std::filesystem::exists(config_dir / filepath);
}
void dConfig::LoadConfig() { void dConfig::LoadConfig() {
std::ifstream in(BinaryPathFinder::GetBinaryDir() / m_ConfigFilePath); std::filesystem::path config_dir = GetConfigDir();
std::ifstream in(config_dir / m_ConfigFilePath);
if (!in.good()) return; if (!in.good()) return;
std::string line{}; std::string line{};
@@ -19,7 +34,7 @@ void dConfig::LoadConfig() {
if (!line.empty() && line.front() != '#') ProcessLine(line); if (!line.empty() && line.front() != '#') ProcessLine(line);
} }
std::ifstream sharedConfig(BinaryPathFinder::GetBinaryDir() / "sharedconfig.ini", std::ios::in); std::ifstream sharedConfig(config_dir / "sharedconfig.ini", std::ios::in);
if (!sharedConfig.good()) return; if (!sharedConfig.good()) return;
line.clear(); line.clear();
@@ -34,17 +49,20 @@ void dConfig::ReloadConfig() {
} }
const std::string& dConfig::GetValue(std::string key) { const std::string& dConfig::GetValue(std::string key) {
std::string upper_key(key);
std::transform(upper_key.begin(), upper_key.end(), upper_key.begin(), ::toupper);
if (const char* env_p = std::getenv(upper_key.c_str())) {
this->m_ConfigValues[key] = env_p;
}
return this->m_ConfigValues[key]; return this->m_ConfigValues[key];
} }
void dConfig::ProcessLine(const std::string& line) { void dConfig::ProcessLine(const std::string& line) {
auto splitLine = GeneralUtils::SplitString(line, '='); auto splitLoc = line.find('=');
auto key = line.substr(0, splitLoc);
if (splitLine.size() != 2) return; auto value = line.substr(splitLoc + 1);
//Make sure that on Linux, we remove special characters: //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 (!value.empty() && value.at(value.size() - 1) == '\r') value.erase(value.size() - 1);
if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return; if (this->m_ConfigValues.find(key) != this->m_ConfigValues.end()) return;

View File

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

View File

@@ -0,0 +1,29 @@
#ifndef __STRINGIFIEDENUM_H__
#define __STRINGIFIEDENUM_H__
#include <string>
#include "magic_enum.hpp"
namespace StringifiedEnum {
template<typename T>
const std::string_view ToString(const T e) {
static_assert(std::is_enum_v<T>, "Not an enum"); // Check type
constexpr auto& sv = magic_enum::enum_entries<T>();
const auto it = std::lower_bound(
sv.begin(), sv.end(), e,
[&](const std::pair<T, std::string_view>& lhs, const T rhs) { return lhs.first < rhs; }
);
std::string_view output;
if (it != sv.end() && it->first == e) {
output = it->second;
} else {
output = "UNKNOWN";
}
return output;
}
}
#endif // !__STRINGIFIEDENUM_H__

View File

@@ -9,6 +9,7 @@
#include "BitStream.h" #include "BitStream.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "BitStreamUtils.h"
#pragma warning (disable:4251) //Disables SQL warnings #pragma warning (disable:4251) //Disables SQL warnings
@@ -32,92 +33,90 @@ 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 PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); #define CMSGHEADER BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::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);
//=========== TYPEDEFS ========== //=========== TYPEDEFS ==========
typedef int32_t LOT; //!< A LOT using LOT = int32_t; //!< A LOT
typedef int64_t LWOOBJID; //!< An object ID (should be unsigned actually but ok) using LWOOBJID = int64_t; //!< An object ID (should be unsigned actually but ok)
typedef int32_t TSkillID; //!< A skill ID using TSkillID = int32_t; //!< A skill ID
typedef uint32_t LWOCLONEID; //!< Used for Clone IDs using LWOCLONEID = uint32_t; //!< Used for Clone IDs
typedef uint16_t LWOMAPID; //!< Used for Map IDs using LWOMAPID = uint16_t; //!< Used for Map IDs
typedef uint16_t LWOINSTANCEID; //!< Used for Instance IDs using LWOINSTANCEID = uint16_t; //!< Used for Instance IDs
typedef uint32_t PROPERTYCLONELIST; //!< Used for Property Clone IDs using PROPERTYCLONELIST = uint32_t; //!< Used for Property Clone IDs
typedef uint32_t StripId; using StripId = uint32_t;
const LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID constexpr LWOOBJID LWOOBJID_EMPTY = 0; //!< An empty object ID
const LOT LOT_NULL = -1; //!< A null LOT constexpr LOT LOT_NULL = -1; //!< A null LOT
const int32_t LOOTTYPE_NONE = 0; //!< No loot type available constexpr int32_t LOOTTYPE_NONE = 0; //!< No loot type available
const float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority constexpr float SECONDARY_PRIORITY = 1.0f; //!< Secondary Priority
const uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size constexpr uint32_t INVENTORY_MAX = 9999999; //!< The Maximum Inventory Size
const uint32_t LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID constexpr LWOCLONEID LWOCLONEID_INVALID = -1; //!< Invalid LWOCLONEID
const uint16_t LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID constexpr LWOINSTANCEID LWOINSTANCEID_INVALID = -1; //!< Invalid LWOINSTANCEID
const uint16_t LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID constexpr LWOMAPID LWOMAPID_INVALID = -1; //!< Invalid LWOMAPID
const uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID constexpr uint64_t LWOZONEID_INVALID = 0; //!< Invalid LWOZONEID
const float PI = 3.14159f; constexpr float PI = 3.14159f;
//============ STRUCTS ============== //============ STRUCTS ==============
struct LWOSCENEID { struct LWOSCENEID {
public: public:
LWOSCENEID() { m_sceneID = -1; m_layerID = 0; } constexpr LWOSCENEID() noexcept { m_sceneID = -1; m_layerID = 0; }
LWOSCENEID(int sceneID) { m_sceneID = sceneID; m_layerID = 0; } constexpr LWOSCENEID(int32_t sceneID) noexcept { m_sceneID = sceneID; m_layerID = 0; }
LWOSCENEID(int sceneID, unsigned int layerID) { m_sceneID = sceneID; m_layerID = layerID; } constexpr LWOSCENEID(int32_t sceneID, uint32_t layerID) noexcept { m_sceneID = sceneID; m_layerID = layerID; }
LWOSCENEID& operator=(const LWOSCENEID& rhs) { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; } constexpr LWOSCENEID& operator=(const LWOSCENEID& rhs) noexcept { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; }
LWOSCENEID& operator=(const int rhs) { m_sceneID = rhs; m_layerID = 0; return *this; } constexpr LWOSCENEID& operator=(const int32_t rhs) noexcept { m_sceneID = rhs; m_layerID = 0; return *this; }
bool operator<(const LWOSCENEID& rhs) const { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); } constexpr bool operator<(const LWOSCENEID& rhs) const noexcept { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); }
bool operator<(const int rhs) const { return m_sceneID < rhs; } constexpr bool operator<(const int32_t rhs) const noexcept { return m_sceneID < rhs; }
bool operator==(const LWOSCENEID& rhs) const { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); } constexpr bool operator==(const LWOSCENEID& rhs) const noexcept { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); }
bool operator==(const int rhs) const { return m_sceneID == rhs; } constexpr bool operator==(const int32_t rhs) const noexcept { return m_sceneID == rhs; }
const int GetSceneID() const { return m_sceneID; } constexpr int32_t GetSceneID() const noexcept { return m_sceneID; }
const unsigned int GetLayerID() const { return m_layerID; } constexpr uint32_t GetLayerID() const noexcept { return m_layerID; }
void SetSceneID(const int sceneID) { m_sceneID = sceneID; } constexpr void SetSceneID(const int32_t sceneID) noexcept { m_sceneID = sceneID; }
void SetLayerID(const unsigned int layerID) { m_layerID = layerID; } constexpr void SetLayerID(const uint32_t layerID) noexcept { m_layerID = layerID; }
private: private:
int m_sceneID; int32_t m_sceneID;
unsigned int m_layerID; uint32_t m_layerID;
}; };
struct LWOZONEID { struct LWOZONEID {
public: public:
const LWOMAPID& GetMapID() const { return m_MapID; } constexpr const LWOMAPID& GetMapID() const noexcept { return m_MapID; }
const LWOINSTANCEID& GetInstanceID() const { return m_InstanceID; } constexpr const LWOINSTANCEID& GetInstanceID() const noexcept { return m_InstanceID; }
const LWOCLONEID& GetCloneID() const { return m_CloneID; } constexpr const LWOCLONEID& GetCloneID() const noexcept { return m_CloneID; }
//In order: def constr, constr, assign op //In order: def constr, constr, assign op
LWOZONEID() { m_MapID = LWOMAPID_INVALID; m_InstanceID = LWOINSTANCEID_INVALID; m_CloneID = LWOCLONEID_INVALID; } constexpr LWOZONEID() noexcept = default;
LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; } constexpr LWOZONEID(const LWOMAPID& mapID, const LWOINSTANCEID& instanceID, const LWOCLONEID& cloneID) noexcept { m_MapID = mapID; m_InstanceID = instanceID; m_CloneID = cloneID; }
LWOZONEID(const LWOZONEID& replacement) { *this = replacement; } constexpr LWOZONEID(const LWOZONEID& replacement) noexcept { *this = replacement; }
private: private:
LWOMAPID m_MapID; //1000 for VE, 1100 for AG, etc... LWOMAPID m_MapID = LWOMAPID_INVALID; //1000 for VE, 1100 for AG, etc...
LWOINSTANCEID m_InstanceID; //Instances host the same world, but on a different dWorld process. LWOINSTANCEID m_InstanceID = LWOINSTANCEID_INVALID; //Instances host the same world, but on a different dWorld process.
LWOCLONEID m_CloneID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds. LWOCLONEID m_CloneID = LWOCLONEID_INVALID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds.
}; };
const LWOSCENEID LWOSCENEID_INVALID = -1; constexpr LWOSCENEID LWOSCENEID_INVALID = -1;
struct LWONameValue { struct LWONameValue {
uint32_t length = 0; //!< The length of the name uint32_t length = 0; //!< The length of the name
std::u16string name; //!< The name std::u16string name; //!< The name
LWONameValue(void) {} LWONameValue() = default;
LWONameValue(const std::u16string& name) { LWONameValue(const std::u16string& name) {
this->name = name; this->name = name;
this->length = static_cast<uint32_t>(name.length()); this->length = static_cast<uint32_t>(name.length());
} }
~LWONameValue(void) {}
}; };
struct FriendData { struct FriendData {
@@ -129,7 +128,7 @@ public:
LWOOBJID friendID; LWOOBJID friendID;
std::string friendName; std::string friendName;
void Serialize(RakNet::BitStream& bitStream) { void Serialize(RakNet::BitStream& bitStream) const {
bitStream.Write<uint8_t>(isOnline); bitStream.Write<uint8_t>(isOnline);
bitStream.Write<uint8_t>(isBestFriend); bitStream.Write<uint8_t>(isBestFriend);
bitStream.Write<uint8_t>(isFTP); bitStream.Write<uint8_t>(isFTP);
@@ -147,11 +146,11 @@ public:
if (size > maxSize) size = maxSize; if (size > maxSize) size = maxSize;
for (uint32_t i = 0; i < size; ++i) { for (uint32_t i = 0; i < size; ++i) {
bitStream.Write(static_cast<uint16_t>(friendName[i])); bitStream.Write<uint16_t>(friendName[i]);
} }
for (uint32_t j = 0; j < remSize; ++j) { for (uint32_t j = 0; j < remSize; ++j) {
bitStream.Write(static_cast<uint16_t>(0)); bitStream.Write<uint16_t>(0);
} }
bitStream.Write<uint32_t>(0); //??? bitStream.Write<uint32_t>(0); //???

View File

@@ -1,31 +0,0 @@
#ifndef __ECHATINTERNALMESSAGETYPE__H__
#define __ECHATINTERNALMESSAGETYPE__H__
#include <cstdint>
enum eChatInternalMessageType : uint32_t {
PLAYER_ADDED_NOTIFICATION = 0,
PLAYER_REMOVED_NOTIFICATION,
ADD_FRIEND,
ADD_BEST_FRIEND,
ADD_TO_TEAM,
ADD_BLOCK,
REMOVE_FRIEND,
REMOVE_BLOCK,
REMOVE_FROM_TEAM,
DELETE_TEAM,
REPORT,
PRIVATE_CHAT,
PRIVATE_CHAT_RESPONSE,
ANNOUNCEMENT,
MAIL_COUNT_UPDATE,
MAIL_SEND_NOTIFY,
REQUEST_USER_LIST,
FRIEND_LIST,
ROUTE_TO_PLAYER,
TEAM_UPDATE,
MUTE_UPDATE,
CREATE_TEAM,
};
#endif //!__ECHATINTERNALMESSAGETYPE__H__

View File

@@ -72,7 +72,9 @@ enum class eChatMessageType :uint32_t {
UPDATE_DONATION, UPDATE_DONATION,
PRG_CSR_COMMAND, PRG_CSR_COMMAND,
HEARTBEAT_REQUEST_FROM_WORLD, HEARTBEAT_REQUEST_FROM_WORLD,
UPDATE_FREE_TRIAL_STATUS UPDATE_FREE_TRIAL_STATUS,
// CUSTOM DLU MESSAGE ID FOR INTERNAL USE
CREATE_TEAM,
}; };
#endif //!__ECHATMESSAGETYPE__H__ #endif //!__ECHATMESSAGETYPE__H__

View File

@@ -5,8 +5,7 @@ enum class eConnectionType : uint16_t {
SERVER = 0, SERVER = 0,
AUTH, AUTH,
CHAT, CHAT,
CHAT_INTERNAL, WORLD = 4,
WORLD,
CLIENT, CLIENT,
MASTER MASTER
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -27,20 +27,6 @@ enum class ePermissionMap : uint64_t {
* The character has restricted chat access, bit 6. * The character has restricted chat access, bit 6.
*/ */
RestrictedChatAccess = 0x1 << 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__ #endif //!__EPERMISSIONMAP__H__

View File

@@ -0,0 +1,13 @@
#ifndef __EPETABILITYTYPE__H__
#define __EPETABILITYTYPE__H__
#include <cstdint>
enum class ePetAbilityType : uint32_t {
Invalid,
GoToObject,
JumpOnObject,
DigAtPosition
};
#endif //!__EPETABILITYTYPE__H__

View File

@@ -166,7 +166,8 @@ enum ePlayerFlag : int32_t {
NJ_LIGHTNING_SPINJITZU = 2031, NJ_LIGHTNING_SPINJITZU = 2031,
NJ_ICE_SPINJITZU = 2032, NJ_ICE_SPINJITZU = 2032,
NJ_FIRE_SPINJITZU = 2033, 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__ #endif //!__EPLAYERFLAG__H__

View File

@@ -0,0 +1,15 @@
#ifndef __EQUICKBUILDSTATE__H__
#define __EQUICKBUILDSTATE__H__
#include <cstdint>
enum class eQuickBuildState : uint32_t {
OPEN,
COMPLETED = 2,
RESETTING = 4,
BUILDING,
INCOMPLETE
};
#endif //!__EQUICKBUILDSTATE__H__

View File

@@ -1,15 +0,0 @@
#ifndef __EREBUILDSTATE__H__
#define __EREBUILDSTATE__H__
#include <cstdint>
enum class eRebuildState : uint32_t {
OPEN,
COMPLETED = 2,
RESETTING = 4,
BUILDING,
INCOMPLETE
};
#endif //!__EREBUILDSTATE__H__

View File

@@ -34,7 +34,7 @@ enum class eReplicaComponentType : uint32_t {
PLATFORM_BOUNDARY, PLATFORM_BOUNDARY,
MODULE, MODULE,
ARCADE, ARCADE,
VEHICLE_PHYSICS, // Havok demo based HAVOK_VEHICLE_PHYSICS,
MOVEMENT_AI, MOVEMENT_AI,
EXHIBIT, EXHIBIT,
OVERHEAD_ICON, OVERHEAD_ICON,
@@ -50,11 +50,11 @@ enum class eReplicaComponentType : uint32_t {
PROPERTY_ENTRANCE, PROPERTY_ENTRANCE,
FX, FX,
PROPERTY_MANAGEMENT, PROPERTY_MANAGEMENT,
VEHICLE_PHYSICS_NEW, // internal physics based on havok VEHICLE_PHYSICS,
PHYSICS_SYSTEM, PHYSICS_SYSTEM,
QUICK_BUILD, QUICK_BUILD,
SWITCH, SWITCH,
ZONE_CONTROL, // Minigame MINI_GAME_CONTROL,
CHANGLING, CHANGLING,
CHOICE_BUILD, CHOICE_BUILD,
PACKAGE, PACKAGE,
@@ -101,13 +101,13 @@ enum class eReplicaComponentType : uint32_t {
TRADE, TRADE,
USER_CONTROL, USER_CONTROL,
IGNORE_LIST, IGNORE_LIST,
ROCKET_LAUNCH_LUP, MULTI_ZONE_ENTRANCE,
BUFF_REAL, // the real buff component, should just be name BUFF BUFF_REAL, // the real buff component, should just be name BUFF
INTERACTION_MANAGER, INTERACTION_MANAGER,
DONATION_VENDOR, DONATION_VENDOR,
COMBAT_MEDIATOR, COMBAT_MEDIATOR,
COMMENDATION_VENDOR, ACHIEVEMENT_VENDOR,
UNKNOWN_103, GATE_RUSH_CONTROL,
RAIL_ACTIVATOR, RAIL_ACTIVATOR,
ROLLER, ROLLER,
PLAYER_FORCED_MOVEMENT, PLAYER_FORCED_MOVEMENT,
@@ -119,7 +119,7 @@ enum class eReplicaComponentType : uint32_t {
UNKNOWN_112, UNKNOWN_112,
PROPERTY_PLAQUE, PROPERTY_PLAQUE,
BUILD_BORDER, BUILD_BORDER,
UNKOWN_115, UNKNOWN_115,
CULLING_PLANE, CULLING_PLANE,
DESTROYABLE = 1000 // Actually 7 DESTROYABLE = 1000 // Actually 7
}; };

View File

@@ -0,0 +1,15 @@
#ifndef __EVENDORTRANSACTIONRESULT__
#define __EVENDORTRANSACTIONRESULT__
#include <cstdint>
enum class eVendorTransactionResult : uint32_t {
SELL_SUCCESS = 0,
SELL_FAIL,
PURCHASE_SUCCESS,
PURCHASE_FAIL,
DONATION_FAIL,
DONATION_FULL
};
#endif // !__EVENDORTRANSACTIONRESULT__

View File

@@ -0,0 +1,59 @@
#ifndef __EWAYPOINTCOMMANDTYPES__H__
#define __EWAYPOINTCOMMANDTYPES__H__
#include <cstdint>
enum class eWaypointCommandType : uint32_t {
INVALID,
BOUNCE,
STOP,
GROUP_EMOTE,
SET_VARIABLE,
CAST_SKILL,
EQUIP_INVENTORY,
UNEQUIP_INVENTORY,
DELAY,
EMOTE,
TELEPORT,
PATH_SPEED,
REMOVE_NPC,
CHANGE_WAYPOINT,
DELETE_SELF,
KILL_SELF,
SPAWN_OBJECT,
PLAY_SOUND,
};
class WaypointCommandType {
public:
static eWaypointCommandType StringToWaypointCommandType(std::string commandString) {
const std::map<std::string, eWaypointCommandType> WaypointCommandTypeMap = {
{"bounce", eWaypointCommandType::BOUNCE},
{"stop", eWaypointCommandType::STOP},
{"groupemote", eWaypointCommandType::GROUP_EMOTE},
{"setvar", eWaypointCommandType::SET_VARIABLE},
{"castskill", eWaypointCommandType::CAST_SKILL},
{"eqInvent", eWaypointCommandType::EQUIP_INVENTORY},
{"unInvent", eWaypointCommandType::UNEQUIP_INVENTORY},
{"delay", eWaypointCommandType::DELAY},
{"femote", eWaypointCommandType::EMOTE},
{"emote", eWaypointCommandType::EMOTE},
{"teleport", eWaypointCommandType::TELEPORT},
{"pathspeed", eWaypointCommandType::PATH_SPEED},
{"removeNPC", eWaypointCommandType::REMOVE_NPC},
{"changeWP", eWaypointCommandType::CHANGE_WAYPOINT},
{"DeleteSelf", eWaypointCommandType::DELETE_SELF},
{"killself", eWaypointCommandType::KILL_SELF},
{"removeself", eWaypointCommandType::DELETE_SELF},
{"spawnOBJ", eWaypointCommandType::SPAWN_OBJECT},
{"playSound", eWaypointCommandType::PLAY_SOUND},
};
auto intermed = WaypointCommandTypeMap.find(commandString);
return (intermed != WaypointCommandTypeMap.end()) ? intermed->second : eWaypointCommandType::INVALID;
};
};
#endif //!__EWAYPOINTCOMMANDTYPES__H__

View File

@@ -3,6 +3,8 @@
#include <cstdint> #include <cstdint>
#include "magic_enum.hpp"
enum class eWorldMessageType : uint32_t { enum class eWorldMessageType : uint32_t {
VALIDATION = 1, // Session info VALIDATION = 1, // Session info
CHARACTER_LIST_REQUEST, CHARACTER_LIST_REQUEST,
@@ -27,8 +29,8 @@ enum class eWorldMessageType : uint32_t {
ROUTE_PACKET, // Social? ROUTE_PACKET, // Social?
POSITION_UPDATE, POSITION_UPDATE,
MAIL, MAIL,
WORD_CHECK, // Whitelist word check WORD_CHECK, // AllowList word check
STRING_CHECK, // Whitelist string check STRING_CHECK, // AllowList string check
GET_PLAYERS_IN_ZONE, GET_PLAYERS_IN_ZONE,
REQUEST_UGC_MANIFEST_INFO, REQUEST_UGC_MANIFEST_INFO,
BLUEPRINT_GET_ALL_DATA_REQUEST, BLUEPRINT_GET_ALL_DATA_REQUEST,
@@ -36,7 +38,14 @@ enum class eWorldMessageType : uint32_t {
HANDLE_FUNNESS, HANDLE_FUNNESS,
FAKE_PRG_CSR_MESSAGE, FAKE_PRG_CSR_MESSAGE,
REQUEST_FREE_TRIAL_REFRESH, REQUEST_FREE_TRIAL_REFRESH,
GM_SET_FREE_TRIAL_STATUS 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__ #endif //!__EWORLDMESSAGETYPE__H__

View File

@@ -1,110 +0,0 @@
#include "dLogger.h"
dLogger::dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements) {
m_logToConsole = logToConsole;
m_logDebugStatements = logDebugStatements;
m_outpath = outpath;
#ifdef _WIN32
mFile = std::ofstream(m_outpath);
if (!mFile) { printf("Couldn't open %s for writing!\n", outpath.c_str()); }
#else
fp = fopen(outpath.c_str(), "wt");
if (fp == NULL) { printf("Couldn't open %s for writing!\n", outpath.c_str()); }
#endif
}
dLogger::~dLogger() {
#ifdef _WIN32
mFile.close();
#else
if (fp != nullptr) {
fclose(fp);
fp = nullptr;
}
#endif
}
void dLogger::vLog(const char* format, va_list args) {
#ifdef _WIN32
time_t t = time(NULL);
struct tm time;
localtime_s(&time, &t);
char timeStr[70];
strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", &time);
char message[2048];
vsnprintf(message, 2048, format, args);
if (m_logToConsole) std::cout << "[" << timeStr << "] " << message;
mFile << "[" << timeStr << "] " << message;
#else
time_t t = time(NULL);
struct tm* time = localtime(&t);
char timeStr[70];
strftime(timeStr, sizeof(timeStr), "%d-%m-%y %H:%M:%S", time);
char message[2048];
vsnprintf(message, 2048, format, args);
if (m_logToConsole) {
fputs("[", stdout);
fputs(timeStr, stdout);
fputs("] ", stdout);
fputs(message, stdout);
}
if (fp != nullptr) {
fputs("[", fp);
fputs(timeStr, fp);
fputs("] ", fp);
fputs(message, fp);
} else {
printf("Logger not initialized!\n");
}
#endif
}
void dLogger::LogBasic(const char* format, ...) {
va_list args;
va_start(args, format);
vLog(format, args);
va_end(args);
}
void dLogger::LogBasic(const std::string& message) {
LogBasic(message.c_str());
}
void dLogger::Log(const char* className, const char* format, ...) {
va_list args;
std::string log = "[" + std::string(className) + "] " + std::string(format) + "\n";
va_start(args, format);
vLog(log.c_str(), args);
va_end(args);
}
void dLogger::Log(const std::string& className, const std::string& message) {
Log(className.c_str(), message.c_str());
}
void dLogger::LogDebug(const char* className, const char* format, ...) {
if (!m_logDebugStatements) return;
va_list args;
std::string log = "[" + std::string(className) + "] " + std::string(format) + "\n";
va_start(args, format);
vLog(log.c_str(), args);
va_end(args);
}
void dLogger::LogDebug(const std::string& className, const std::string& message) {
LogDebug(className.c_str(), message.c_str());
}
void dLogger::Flush() {
#ifdef _WIN32
mFile.flush();
#else
if (fp != nullptr) {
std::fflush(fp);
}
#endif
}

View File

@@ -1,38 +0,0 @@
#pragma once
#include <ctime>
#include <cstdarg>
#include <string>
#include <fstream>
#include <iostream>
class dLogger {
public:
dLogger(const std::string& outpath, bool logToConsole, bool logDebugStatements);
~dLogger();
void SetLogToConsole(bool logToConsole) { m_logToConsole = logToConsole; }
void SetLogDebugStatements(bool logDebugStatements) { m_logDebugStatements = logDebugStatements; }
void vLog(const char* format, va_list args);
void LogBasic(const std::string& message);
void LogBasic(const char* format, ...);
void Log(const char* className, const char* format, ...);
void Log(const std::string& className, const std::string& message);
void LogDebug(const std::string& className, const std::string& message);
void LogDebug(const char* className, const char* format, ...);
void Flush();
const bool GetIsLoggingToConsole() const { return m_logToConsole; }
private:
bool m_logDebugStatements;
bool m_logToConsole;
std::string m_outpath;
std::ofstream mFile;
#ifndef _WIN32
//Glorious linux can run with SPEED:
FILE* fp = nullptr;
#endif
};

View File

@@ -4,9 +4,13 @@
// Static Variables // Static Variables
static CppSQLite3DB* conn = new CppSQLite3DB(); static CppSQLite3DB* conn = new CppSQLite3DB();
// Status Variables
bool CDClientDatabase::isConnected = false;
//! Opens a connection with the CDClient //! Opens a connection with the CDClient
void CDClientDatabase::Connect(const std::string& filename) { void CDClientDatabase::Connect(const std::string& filename) {
conn->open(filename.c_str()); conn->open(filename.c_str());
isConnected = true;
} }
//! Queries the CDClient //! Queries the CDClient

View File

@@ -13,19 +13,12 @@
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
// Enable this to cache all entries in each table for fast access, comes with more memory cost //! The CDClient Database namespace
//#define CDCLIENT_CACHE_ALL
// Enable this to skip some unused columns in some tables
#define UNUSED(v)
/*!
\file CDClientDatabase.hpp
\brief An interface between the CDClient.sqlite file and the server
*/
//! The CDClient Database namespace
namespace CDClientDatabase { namespace CDClientDatabase {
/**
* Boolean defining the connection status of CDClient
*/
extern bool isConnected;
//! Opens a connection with the CDClient //! Opens a connection with the CDClient
/*! /*!

View File

@@ -0,0 +1,159 @@
#include "CDClientManager.h"
#include "CDActivityRewardsTable.h"
#include "CDAnimationsTable.h"
#include "CDBehaviorParameterTable.h"
#include "CDBehaviorTemplateTable.h"
#include "CDClientDatabase.h"
#include "CDComponentsRegistryTable.h"
#include "CDCurrencyTableTable.h"
#include "CDDestructibleComponentTable.h"
#include "CDEmoteTable.h"
#include "CDInventoryComponentTable.h"
#include "CDItemComponentTable.h"
#include "CDItemSetsTable.h"
#include "CDItemSetSkillsTable.h"
#include "CDLevelProgressionLookupTable.h"
#include "CDLootMatrixTable.h"
#include "CDLootTableTable.h"
#include "CDMissionNPCComponentTable.h"
#include "CDMissionTasksTable.h"
#include "CDMissionsTable.h"
#include "CDObjectSkillsTable.h"
#include "CDObjectsTable.h"
#include "CDPhysicsComponentTable.h"
#include "CDRebuildComponentTable.h"
#include "CDScriptComponentTable.h"
#include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h"
#include "CDTamingBuildPuzzleTable.h"
#include "CDVendorComponentTable.h"
#include "CDActivitiesTable.h"
#include "CDPackageComponentTable.h"
#include "CDProximityMonitorComponentTable.h"
#include "CDMovementAIComponentTable.h"
#include "CDBrickIDTableTable.h"
#include "CDRarityTableTable.h"
#include "CDMissionEmailTable.h"
#include "CDRewardsTable.h"
#include "CDPropertyEntranceComponentTable.h"
#include "CDPropertyTemplateTable.h"
#include "CDFeatureGatingTable.h"
#include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h"
#ifndef CDCLIENT_CACHE_ALL
// Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
// A vanilla CDClient takes about 46MB of memory + the regular world data.
// # define CDCLIENT_CACHE_ALL
#endif // CDCLIENT_CACHE_ALL
#ifdef CDCLIENT_CACHE_ALL
#define CDCLIENT_DONT_CACHE_TABLE(x) x
#else
#define CDCLIENT_DONT_CACHE_TABLE(x)
#endif
// Using a macro to reduce repetitive code and issues from copy and paste.
// As a note, ## in a macro is used to concatenate two tokens together.
#define SPECIALIZE_TABLE_STORAGE(table) \
template<> typename table::StorageType& CDClientManager::GetEntriesMutable<table>() { return table##Entries; };
#define DEFINE_TABLE_STORAGE(table) namespace { table::StorageType table##Entries; }; SPECIALIZE_TABLE_STORAGE(table)
DEFINE_TABLE_STORAGE(CDActivityRewardsTable);
DEFINE_TABLE_STORAGE(CDActivitiesTable);
DEFINE_TABLE_STORAGE(CDAnimationsTable);
DEFINE_TABLE_STORAGE(CDBehaviorParameterTable);
DEFINE_TABLE_STORAGE(CDBehaviorTemplateTable);
DEFINE_TABLE_STORAGE(CDBrickIDTableTable);
DEFINE_TABLE_STORAGE(CDComponentsRegistryTable);
DEFINE_TABLE_STORAGE(CDCurrencyTableTable);
DEFINE_TABLE_STORAGE(CDDestructibleComponentTable);
DEFINE_TABLE_STORAGE(CDEmoteTableTable);
DEFINE_TABLE_STORAGE(CDFeatureGatingTable);
DEFINE_TABLE_STORAGE(CDInventoryComponentTable);
DEFINE_TABLE_STORAGE(CDItemComponentTable);
DEFINE_TABLE_STORAGE(CDItemSetSkillsTable);
DEFINE_TABLE_STORAGE(CDItemSetsTable);
DEFINE_TABLE_STORAGE(CDLevelProgressionLookupTable);
DEFINE_TABLE_STORAGE(CDLootMatrixTable);
DEFINE_TABLE_STORAGE(CDLootTableTable);
DEFINE_TABLE_STORAGE(CDMissionEmailTable);
DEFINE_TABLE_STORAGE(CDMissionNPCComponentTable);
DEFINE_TABLE_STORAGE(CDMissionTasksTable);
DEFINE_TABLE_STORAGE(CDMissionsTable);
DEFINE_TABLE_STORAGE(CDMovementAIComponentTable);
DEFINE_TABLE_STORAGE(CDObjectSkillsTable);
DEFINE_TABLE_STORAGE(CDObjectsTable);
DEFINE_TABLE_STORAGE(CDPhysicsComponentTable);
DEFINE_TABLE_STORAGE(CDPackageComponentTable);
DEFINE_TABLE_STORAGE(CDPetComponentTable);
DEFINE_TABLE_STORAGE(CDProximityMonitorComponentTable);
DEFINE_TABLE_STORAGE(CDPropertyEntranceComponentTable);
DEFINE_TABLE_STORAGE(CDPropertyTemplateTable);
DEFINE_TABLE_STORAGE(CDRailActivatorComponentTable);
DEFINE_TABLE_STORAGE(CDRarityTableTable);
DEFINE_TABLE_STORAGE(CDRebuildComponentTable);
DEFINE_TABLE_STORAGE(CDRewardCodesTable);
DEFINE_TABLE_STORAGE(CDRewardsTable);
DEFINE_TABLE_STORAGE(CDScriptComponentTable);
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
DEFINE_TABLE_STORAGE(CDZoneTableTable);
void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) {
throw std::runtime_error{ "CDClientDatabase is not connected!" };
}
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
CDActivitiesTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase());
CDBehaviorParameterTable::Instance().LoadValuesFromDatabase();
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase());
CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
CDEmoteTableTable::Instance().LoadValuesFromDatabase();
CDFeatureGatingTable::Instance().LoadValuesFromDatabase();
CDInventoryComponentTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDItemComponentTable::Instance().LoadValuesFromDatabase());
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
CDItemSetsTable::Instance().LoadValuesFromDatabase();
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase());
CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase());
CDMissionEmailTable::Instance().LoadValuesFromDatabase();
CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase();
CDMissionTasksTable::Instance().LoadValuesFromDatabase();
CDMissionsTable::Instance().LoadValuesFromDatabase();
CDMovementAIComponentTable::Instance().LoadValuesFromDatabase();
CDObjectSkillsTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase());
CDPhysicsComponentTable::Instance().LoadValuesFromDatabase();
CDPackageComponentTable::Instance().LoadValuesFromDatabase();
CDPetComponentTable::Instance().LoadValuesFromDatabase();
CDProximityMonitorComponentTable::Instance().LoadValuesFromDatabase();
CDPropertyEntranceComponentTable::Instance().LoadValuesFromDatabase();
CDPropertyTemplateTable::Instance().LoadValuesFromDatabase();
CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase();
CDRarityTableTable::Instance().LoadValuesFromDatabase();
CDRebuildComponentTable::Instance().LoadValuesFromDatabase();
CDRewardCodesTable::Instance().LoadValuesFromDatabase();
CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase();
}
void CDClientManager::LoadValuesFromDefaults() {
LOG("Loading default CDClient tables!");
CDPetComponentTable::Instance().LoadValuesFromDefaults();
}

View File

@@ -0,0 +1,44 @@
#ifndef __CDCLIENTMANAGER__H__
#define __CDCLIENTMANAGER__H__
#define UNUSED_TABLE(v)
/**
* Initialize the CDClient tables so they are all loaded into memory.
*/
namespace CDClientManager {
void LoadValuesFromDatabase();
void LoadValuesFromDefaults();
/**
* Fetch a table from CDClient
*
* @tparam Table type to fetch
* @return A pointer to the requested table.
*/
template<typename T>
T* GetTable();
/**
* Fetch a table from CDClient
* Note: Calling this function without a template specialization in CDClientManager.cpp will cause a linker error.
*
* @tparam Table type to fetch
* @return A pointer to the requested table.
*/
template<typename T>
typename T::StorageType& GetEntriesMutable();
};
// These are included after the CDClientManager namespace declaration as CDTable as of Jan 29 2024 relies on CDClientManager in Templated code.
#include "CDTable.h"
#include "Singleton.h"
template<typename T>
T* CDClientManager::GetTable() {
return &T::Instance();
};
#endif //!__CDCLIENTMANAGER__H__

View File

@@ -1,9 +1,9 @@
#include "CDActivitiesTable.h" #include "CDActivitiesTable.h"
CDActivitiesTable::CDActivitiesTable(void) {
void CDActivitiesTable::LoadValuesFromDatabase() {
// First, get the size of the table // First, get the size of the table
unsigned int size = 0; uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Activities"); auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Activities");
while (!tableSize.eof()) { while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0); size = tableSize.getIntField(0, 0);
@@ -14,7 +14,8 @@ CDActivitiesTable::CDActivitiesTable(void) {
tableSize.finalize(); tableSize.finalize();
// Reserve the size // Reserve the size
this->entries.reserve(size); auto& entries = GetEntriesMutable();
entries.reserve(size);
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Activities"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Activities");
@@ -40,7 +41,7 @@ CDActivitiesTable::CDActivitiesTable(void) {
entry.noTeamLootOnDeath = tableData.getIntField("noTeamLootOnDeath", -1); entry.noTeamLootOnDeath = tableData.getIntField("noTeamLootOnDeath", -1);
entry.optionalPercentage = tableData.getFloatField("optionalPercentage", -1.0f); entry.optionalPercentage = tableData.getFloatField("optionalPercentage", -1.0f);
this->entries.push_back(entry); entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
@@ -49,14 +50,9 @@ CDActivitiesTable::CDActivitiesTable(void) {
std::vector<CDActivities> CDActivitiesTable::Query(std::function<bool(CDActivities)> predicate) { std::vector<CDActivities> CDActivitiesTable::Query(std::function<bool(CDActivities)> predicate) {
std::vector<CDActivities> data = cpplinq::from(this->entries) std::vector<CDActivities> data = cpplinq::from(GetEntries())
>> cpplinq::where(predicate) >> cpplinq::where(predicate)
>> cpplinq::to_vector(); >> cpplinq::to_vector();
return data; return data;
} }
std::vector<CDActivities> CDActivitiesTable::GetEntries(void) const {
return this->entries;
}

View File

@@ -0,0 +1,34 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDActivities {
uint32_t ActivityID;
uint32_t locStatus;
uint32_t instanceMapID;
uint32_t minTeams;
uint32_t maxTeams;
uint32_t minTeamSize;
uint32_t maxTeamSize;
uint32_t waitTime;
uint32_t startDelay;
bool requiresUniqueData;
uint32_t leaderboardType;
bool localize;
int32_t optionalCostLOT;
int32_t optionalCostCount;
bool showUIRewards;
uint32_t CommunityActivityFlagID;
std::string gate_version;
bool noTeamLootOnDeath;
float optionalPercentage;
};
class CDActivitiesTable : public CDTable<CDActivitiesTable, std::vector<CDActivities>> {
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDActivities> Query(std::function<bool(CDActivities)> predicate);
};

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