Compare commits

..

95 Commits

Author SHA1 Message Date
bf58cf85cc us static cast and enum 2024-06-07 15:07:37 -05:00
f6f3cdc3c0 fix compiling 2024-06-07 09:31:54 -05:00
66cf96af1a whoops
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:34:29 -05:00
0e6cb8ab19 implict bool
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:34:10 -05:00
42cf2b6377 remove unused code
Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-06-06 21:33:42 -05:00
3cfbc9d3df Document what needs to be done
May not do the recursive restriction cause they aren't used in the live game
2024-06-06 21:11:43 -05:00
5ba37ff7d6 fix compiling 2024-06-06 08:50:00 -05:00
8f00f1601c Merge branch 'main' into issue-960 2024-06-06 08:23:41 -05:00
Wincent Holm
1454fcd003 Fix g++ 14 (#1610)
* Fix g++ 14

* Update thirdparty/CMakeLists.txt

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

---------

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

* add if check for packet lengths

* move purge down further

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

Testing splitting AgSpaceStuff into AgSpaceStuff and AgShipShake

* fixed inclusions

* Removed DoShake

* cleaning up

* consistent if statements

* Update dScripts/ai/AG/AgShipShake.h

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

---------

Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com>
2024-05-27 01:24:48 -05:00
Gie "Max" Vanommeslaeghe
9328021339 Merge pull request #1600 from DarkflameUniverse/add-missing-scripts
fix: add back missing scripts from scripts refactor
2024-05-26 12:02:12 +02:00
Gie "Max" Vanommeslaeghe
d1134fdd62 Merge pull request #1601 from DarkflameUniverse/fix-using-skill-in-race
fix: players using non-car skills in a race
2024-05-26 12:01:54 +02:00
David Markowitz
efa658bc31 fix players using non-car skills in a race 2024-05-25 19:59:15 -07:00
David Markowitz
e59525d2ae Update CppScripts.cpp 2024-05-25 19:32:18 -07:00
David Markowitz
0348db72a5 fiux mission (#1596) 2024-05-25 12:24:02 -05:00
TAHuntling
debc2a96e2 Update CppScripts exclusion list (#1597) 2024-05-25 01:43:32 -07:00
David Markowitz
8ae1a8ff7c fix stale reference (#1594) 2024-05-24 09:15:30 -05: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
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
203 changed files with 7398 additions and 4903 deletions

View File

@@ -1,9 +1,18 @@
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 20) set(CMAKE_CXX_STANDARD 20)
set(CXX_STANDARD_REQUIRED ON) 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") set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
# Read variables from file # Read variables from file
@@ -70,7 +79,8 @@ if(UNIX)
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()
@@ -100,7 +110,7 @@ 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") message(STATUS "Checking resource file integrity")
include(Utils) include(Utils)
@@ -211,29 +221,29 @@ if (APPLE)
endif() endif()
# Load all of our third party directories # Load all of our third party directories
add_subdirectory(thirdparty) add_subdirectory(thirdparty SYSTEM)
# Create our list of include directories # Create our list of include directories
set(INCLUDED_DIRECTORIES include_directories(
"dPhysics" "dPhysics"
"dNavigation" "dNavigation"
"dNet" "dNet"
"thirdparty/magic_enum/include/magic_enum"
"thirdparty/raknet/Source"
"thirdparty/tinyxml2"
"thirdparty/recastnavigation"
"thirdparty/SQLite"
"thirdparty/cpplinq"
"thirdparty/cpp-httplib"
"thirdparty/MD5"
"tests" "tests"
"tests/dCommonTests" "tests/dCommonTests"
"tests/dGameTests" "tests/dGameTests"
"tests/dGameTests/dComponentsTests" "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) # Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
@@ -242,14 +252,9 @@ if(APPLE)
include_directories("/usr/local/include/") include_directories("/usr/local/include/")
endif() endif()
# Actually include the directories from our list
foreach(dir ${INCLUDED_DIRECTORIES})
include_directories(${PROJECT_SOURCE_DIR}/${dir})
endforeach()
# Add linking directories: # Add linking directories:
if (UNIX) if (UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wold-style-cast -Werror") # Warning flags
endif() endif()
file( file(
GLOB HEADERS_DZONEMANAGER GLOB HEADERS_DZONEMANAGER

View File

@@ -1,6 +1,6 @@
PROJECT_VERSION_MAJOR=1 PROJECT_VERSION_MAJOR=2
PROJECT_VERSION_MINOR=1 PROJECT_VERSION_MINOR=3
PROJECT_VERSION_PATCH=1 PROJECT_VERSION_PATCH=0
# 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.

View File

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

View File

@@ -51,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.
@@ -73,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

View File

@@ -141,6 +141,6 @@ elseif(APPLE)
endif() endif()
# Add directories to include lists # Add directories to include lists
target_include_directories(MariaDB::ConnCpp INTERFACE ${MARIADB_INCLUDE_DIR}) target_include_directories(MariaDB::ConnCpp SYSTEM INTERFACE ${MARIADB_INCLUDE_DIR})
set(MariaDB_FOUND TRUE) set(MariaDB_FOUND TRUE)

View File

@@ -27,8 +27,8 @@ 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:
@@ -44,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;
@@ -70,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);
} }
@@ -90,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);
} }
@@ -105,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;
@@ -126,16 +126,16 @@ 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());
} }

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

View File

@@ -14,11 +14,11 @@
#include "eObjectBits.h" #include "eObjectBits.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatMessageType.h" #include "eChatMessageType.h"
#include "eChatInternalMessageType.h"
#include "eClientMessageType.h" #include "eClientMessageType.h"
#include "eGameMessageType.h" #include "eGameMessageType.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
#include "eGameMasterLevel.h" #include "eGameMasterLevel.h"
#include "ChatPackets.h"
void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) { void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Get from the packet which player we want to do something with: //Get from the packet which player we want to do something with:
@@ -60,7 +60,7 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from: //Now, we need to send the friendlist to the server they came from:
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(playerID); bitStream.Write(playerID);
//portion that will get routed: //portion that will get routed:
@@ -355,6 +355,67 @@ void ChatPacketHandler::HandleGMLevelUpdate(Packet* packet) {
inStream.Read(player.gmLevel); inStream.Read(player.gmLevel);
} }
void ChatPacketHandler::HandleWho(Packet* packet) {
CINSTREAM_SKIP_HEADER;
FindPlayerRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
const auto& player = Game::playerContainer.GetPlayerData(request.playerName.GetAsString());
bool online = player;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::WHO_RESPONSE);
bitStream.Write<uint8_t>(online);
bitStream.Write(player.zoneID.GetMapID());
bitStream.Write(player.zoneID.GetInstanceID());
bitStream.Write(player.zoneID.GetCloneID());
bitStream.Write(request.playerName);
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
void ChatPacketHandler::HandleShowAll(Packet* packet) {
CINSTREAM_SKIP_HEADER;
ShowAllRequest request;
request.Deserialize(inStream);
const auto& sender = Game::playerContainer.GetPlayerData(request.requestor);
if (!sender) return;
CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(request.requestor);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::SHOW_ALL_RESPONSE);
bitStream.Write<uint8_t>(!request.displayZoneData && !request.displayIndividualPlayers);
bitStream.Write(Game::playerContainer.GetPlayerCount());
bitStream.Write(Game::playerContainer.GetSimCount());
bitStream.Write<uint8_t>(request.displayIndividualPlayers);
bitStream.Write<uint8_t>(request.displayZoneData);
if (request.displayZoneData || request.displayIndividualPlayers){
for (auto& [playerID, playerData ]: Game::playerContainer.GetAllPlayers()){
if (!playerData) continue;
bitStream.Write<uint8_t>(0); // structure packing
if (request.displayIndividualPlayers) bitStream.Write(LUWString(playerData.playerName));
if (request.displayZoneData) {
bitStream.Write(playerData.zoneID.GetMapID());
bitStream.Write(playerData.zoneID.GetInstanceID());
bitStream.Write(playerData.zoneID.GetCloneID());
}
}
}
SystemAddress sysAddr = sender.sysAddr;
SEND_PACKET;
}
// the structure the client uses to send this packet is shared in many chat messages // the structure the client uses to send this packet is shared in many chat messages
// that are sent to the server. Because of this, there are large gaps of unused data in chat messages // that are sent to the server. Because of this, there are large gaps of unused data in chat messages
void ChatPacketHandler::HandleChatMessage(Packet* packet) { void ChatPacketHandler::HandleChatMessage(Packet* packet) {
@@ -454,7 +515,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) { void ChatPacketHandler::SendPrivateChatMessage(const PlayerData& sender, const PlayerData& receiver, const PlayerData& routeTo, const LUWString& message, const eChatChannel channel, const eChatMessageResponseCode responseCode) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(routeTo.playerID); bitStream.Write(routeTo.playerID);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
@@ -696,7 +757,7 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) { void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerData& sender) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -711,7 +772,7 @@ void ChatPacketHandler::SendTeamInvite(const PlayerData& receiver, const PlayerD
void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -738,7 +799,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(const PlayerData& receiver, bool b
void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) { void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -763,7 +824,7 @@ void ChatPacketHandler::SendTeamStatus(const PlayerData& receiver, LWOOBJID i64L
void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) { void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i64PlayerID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -780,7 +841,7 @@ void ChatPacketHandler::SendTeamSetLeader(const PlayerData& receiver, LWOOBJID i
void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -809,7 +870,7 @@ void ChatPacketHandler::SendTeamAddPlayer(const PlayerData& receiver, bool bIsFr
void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) { void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -835,7 +896,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(const PlayerData& receiver, bool bD
void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) { void ChatPacketHandler::SendTeamSetOffWorldFlag(const PlayerData& receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -869,7 +930,7 @@ void ChatPacketHandler::SendFriendUpdate(const PlayerData& friendData, const Pla
[bool] - is FTP*/ [bool] - is FTP*/
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(friendData.playerID); bitStream.Write(friendData.playerID);
//portion that will get routed: //portion that will get routed:
@@ -906,7 +967,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
} }
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:
@@ -920,7 +981,7 @@ void ChatPacketHandler::SendFriendRequest(const PlayerData& receiver, const Play
void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) { void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const PlayerData& sender, eAddFriendResponseType responseCode, uint8_t isBestFriendsAlready, uint8_t isBestFriendRequest) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
// Portion that will get routed: // Portion that will get routed:
@@ -943,7 +1004,7 @@ void ChatPacketHandler::SendFriendResponse(const PlayerData& receiver, const Pla
void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) { void ChatPacketHandler::SendRemoveFriend(const PlayerData& receiver, std::string& personToRemove, bool isSuccessful) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::WORLD_ROUTE_PACKET);
bitStream.Write(receiver.playerID); bitStream.Write(receiver.playerID);
//portion that will get routed: //portion that will get routed:

View File

@@ -50,6 +50,8 @@ namespace ChatPacketHandler {
void HandleFriendResponse(Packet* packet); void HandleFriendResponse(Packet* packet);
void HandleRemoveFriend(Packet* packet); void HandleRemoveFriend(Packet* packet);
void HandleGMLevelUpdate(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);

View File

@@ -17,7 +17,6 @@
#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 "ChatIgnoreList.h"
#include "StringifiedEnum.h" #include "StringifiedEnum.h"
@@ -180,49 +179,32 @@ int main(int argc, char** argv) {
} }
void HandlePacket(Packet* packet) { void HandlePacket(Packet* packet) {
if (packet->length < 1) return;
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) {
LOG("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) {
if (packet->data[0] == ID_NEW_INCOMING_CONNECTION) {
LOG("A server is connecting, awaiting user list."); 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->length < 4) return; // Nothing left to process. Need 4 bytes to continue. CINSTREAM;
inStream.SetReadOffset(BYTES_TO_BITS(1));
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT_INTERNAL) { eConnectionType connection;
switch (static_cast<eChatInternalMessageType>(packet->data[3])) { eChatMessageType chatMessageID;
case eChatInternalMessageType::PLAYER_ADDED_NOTIFICATION:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION: inStream.Read(connection);
Game::playerContainer.RemovePlayer(packet); if (connection != eConnectionType::CHAT) return;
break; inStream.Read(chatMessageID);
case eChatInternalMessageType::MUTE_UPDATE: switch (chatMessageID) {
case eChatMessageType::GM_MUTE:
Game::playerContainer.MuteUpdate(packet); Game::playerContainer.MuteUpdate(packet);
break; break;
case eChatInternalMessageType::CREATE_TEAM: case eChatMessageType::CREATE_TEAM:
Game::playerContainer.CreateTeamServer(packet); Game::playerContainer.CreateTeamServer(packet);
break; 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:
LOG("Unknown CHAT_INTERNAL id: %i", int(packet->data[3]));
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::CHAT) {
eChatMessageType chat_message_type = static_cast<eChatMessageType>(packet->data[3]);
switch (chat_message_type) {
case eChatMessageType::GET_FRIENDS_LIST: case eChatMessageType::GET_FRIENDS_LIST:
ChatPacketHandler::HandleFriendlistRequest(packet); ChatPacketHandler::HandleFriendlistRequest(packet);
break; break;
@@ -296,6 +278,23 @@ void HandlePacket(Packet* packet) {
ChatPacketHandler::HandleGMLevelUpdate(packet); ChatPacketHandler::HandleGMLevelUpdate(packet);
break; break;
case eChatMessageType::LOGIN_SESSION_NOTIFY: case eChatMessageType::LOGIN_SESSION_NOTIFY:
Game::playerContainer.InsertPlayer(packet);
break;
case eChatMessageType::GM_ANNOUNCE:{
// we just forward this packet to every connected server
inStream.ResetReadPointer();
Game::server->Send(inStream, packet->systemAddress, true); // send to everyone except origin
}
break;
case eChatMessageType::UNEXPECTED_DISCONNECT:
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::USER_CHANNEL_CHAT_MESSAGE:
case eChatMessageType::WORLD_DISCONNECT_REQUEST: case eChatMessageType::WORLD_DISCONNECT_REQUEST:
case eChatMessageType::WORLD_PROXIMITY_RESPONSE: case eChatMessageType::WORLD_PROXIMITY_RESPONSE:
@@ -308,7 +307,6 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::GUILD_KICK: case eChatMessageType::GUILD_KICK:
case eChatMessageType::GUILD_GET_STATUS: case eChatMessageType::GUILD_GET_STATUS:
case eChatMessageType::GUILD_GET_ALL: case eChatMessageType::GUILD_GET_ALL:
case eChatMessageType::SHOW_ALL:
case eChatMessageType::BLUEPRINT_MODERATED: case eChatMessageType::BLUEPRINT_MODERATED:
case eChatMessageType::BLUEPRINT_MODEL_READY: case eChatMessageType::BLUEPRINT_MODEL_READY:
case eChatMessageType::PROPERTY_READY_FOR_APPROVAL: case eChatMessageType::PROPERTY_READY_FOR_APPROVAL:
@@ -323,7 +321,6 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::CSR_REQUEST: case eChatMessageType::CSR_REQUEST:
case eChatMessageType::CSR_REPLY: case eChatMessageType::CSR_REPLY:
case eChatMessageType::GM_KICK: case eChatMessageType::GM_KICK:
case eChatMessageType::GM_ANNOUNCE:
case eChatMessageType::WORLD_ROUTE_PACKET: case eChatMessageType::WORLD_ROUTE_PACKET:
case eChatMessageType::GET_ZONE_POPULATIONS: case eChatMessageType::GET_ZONE_POPULATIONS:
case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE: case eChatMessageType::REQUEST_MINIMUM_CHAT_MODE:
@@ -332,33 +329,18 @@ void HandlePacket(Packet* packet) {
case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE: case eChatMessageType::UGCMANIFEST_REPORT_DONE_FILE:
case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT: case eChatMessageType::UGCMANIFEST_REPORT_DONE_BLUEPRINT:
case eChatMessageType::UGCC_REQUEST: case eChatMessageType::UGCC_REQUEST:
case eChatMessageType::WHO:
case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE: case eChatMessageType::WORLD_PLAYERS_PET_MODERATED_ACKNOWLEDGE:
case eChatMessageType::ACHIEVEMENT_NOTIFY: case eChatMessageType::ACHIEVEMENT_NOTIFY:
case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW: case eChatMessageType::GM_CLOSE_PRIVATE_CHAT_WINDOW:
case eChatMessageType::UNEXPECTED_DISCONNECT:
case eChatMessageType::PLAYER_READY: case eChatMessageType::PLAYER_READY:
case eChatMessageType::GET_DONATION_TOTAL: case eChatMessageType::GET_DONATION_TOTAL:
case eChatMessageType::UPDATE_DONATION: case eChatMessageType::UPDATE_DONATION:
case eChatMessageType::PRG_CSR_COMMAND: case eChatMessageType::PRG_CSR_COMMAND:
case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD: case eChatMessageType::HEARTBEAT_REQUEST_FROM_WORLD:
case eChatMessageType::UPDATE_FREE_TRIAL_STATUS: case eChatMessageType::UPDATE_FREE_TRIAL_STATUS:
LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chat_message_type).data(), chat_message_type); LOG("Unhandled CHAT Message id: %s (%i)", StringifiedEnum::ToString(chatMessageID).data(), chatMessageID);
break; break;
default: default:
LOG("Unknown CHAT Message id: %i", chat_message_type); LOG("Unknown CHAT Message id: %i", chatMessageID);
}
}
if (static_cast<eConnectionType>(packet->data[1]) == eConnectionType::WORLD) {
switch (static_cast<eWorldMessageType>(packet->data[3])) {
case eWorldMessageType::ROUTE_PACKET: {
LOG("Routing packet from world");
break;
}
default:
LOG("Unknown World id: %i", int(packet->data[3]));
}
} }
} }

View File

@@ -9,9 +9,9 @@
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "Database.h" #include "Database.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h" #include "ChatPackets.h"
#include "dConfig.h" #include "dConfig.h"
#include "eChatMessageType.h"
void PlayerContainer::Initialize() { void PlayerContainer::Initialize() {
m_MaxNumberOfBestFriends = m_MaxNumberOfBestFriends =
@@ -49,6 +49,7 @@ void PlayerContainer::InsertPlayer(Packet* packet) {
data.sysAddr = packet->systemAddress; data.sysAddr = packet->systemAddress;
m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName); m_Names[data.playerID] = GeneralUtils::UTF8ToUTF16(data.playerName);
m_PlayerCount++;
LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID()); LOG("Added user: %s (%llu), zone: %i", data.playerName.c_str(), data.playerID, data.zoneID.GetMapID());
@@ -87,6 +88,7 @@ void PlayerContainer::RemovePlayer(Packet* packet) {
} }
} }
m_PlayerCount--;
LOG("Removed user: %llu", playerID); LOG("Removed user: %llu", playerID);
m_Players.erase(playerID); m_Players.erase(playerID);
@@ -145,7 +147,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) { void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::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);
@@ -352,7 +354,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) { void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_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);
@@ -390,7 +392,7 @@ LWOOBJID PlayerContainer::GetId(const std::u16string& playerName) {
} }
PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) { PlayerData& PlayerContainer::GetPlayerDataMutable(const LWOOBJID& playerID) {
return m_Players[playerID]; return m_Players.contains(playerID) ? m_Players[playerID] : m_Players[LWOOBJID_EMPTY];
} }
PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) { PlayerData& PlayerContainer::GetPlayerDataMutable(const std::string& playerName) {

View File

@@ -71,6 +71,9 @@ public:
const PlayerData& GetPlayerData(const std::string& playerName); const PlayerData& GetPlayerData(const std::string& playerName);
PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID); PlayerData& GetPlayerDataMutable(const LWOOBJID& playerID);
PlayerData& GetPlayerDataMutable(const std::string& playerName); PlayerData& GetPlayerDataMutable(const std::string& playerName);
uint32_t GetPlayerCount() { return m_PlayerCount; };
uint32_t GetSimCount() { return m_SimCount; };
const std::map<LWOOBJID, PlayerData>& GetAllPlayers() { return m_Players; };
TeamData* CreateLocalTeam(std::vector<LWOOBJID> members); TeamData* CreateLocalTeam(std::vector<LWOOBJID> members);
TeamData* CreateTeam(LWOOBJID leader, bool local = false); TeamData* CreateTeam(LWOOBJID leader, bool local = false);
@@ -93,5 +96,7 @@ private:
std::unordered_map<LWOOBJID, std::u16string> m_Names; std::unordered_map<LWOOBJID, std::u16string> m_Names;
uint32_t m_MaxNumberOfBestFriends = 5; uint32_t m_MaxNumberOfBestFriends = 5;
uint32_t m_MaxNumberOfFriends = 50; uint32_t m_MaxNumberOfFriends = 50;
uint32_t m_PlayerCount = 0;
uint32_t m_SimCount = 0;
}; };

View File

@@ -120,6 +120,8 @@ void CatchUnhandled(int sig) {
if (eptr) std::rethrow_exception(eptr); if (eptr) std::rethrow_exception(eptr);
} catch(const std::exception& e) { } catch(const std::exception& e) {
LOG("Caught exception: '%s'", e.what()); LOG("Caught exception: '%s'", e.what());
} catch (...) {
LOG("Caught unknown exception.");
} }
#ifndef INCLUDE_BACKTRACE #ifndef INCLUDE_BACKTRACE
@@ -199,7 +201,7 @@ void OnTerminate() {
} }
void MakeBacktrace() { void MakeBacktrace() {
struct sigaction sigact; struct sigaction sigact{};
sigact.sa_sigaction = CritErrHdlr; sigact.sa_sigaction = CritErrHdlr;
sigact.sa_flags = SA_RESTART | SA_SIGINFO; sigact.sa_flags = SA_RESTART | SA_SIGINFO;

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 = reinterpret_cast<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,8 +271,7 @@ 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;
} }
@@ -283,7 +280,7 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
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);
@@ -292,35 +289,35 @@ std::u16string GeneralUtils::ReadWString(RakNet::BitStream& inStream) {
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;
} }
#ifdef DARKFLAME_PLATFORM_MACOS #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse function specializations // MacOS floating-point parse function specializations
namespace GeneralUtils::details { namespace GeneralUtils::details {

View File

@@ -3,17 +3,18 @@
// C++ // C++
#include <charconv> #include <charconv>
#include <cstdint> #include <cstdint>
#include <random>
#include <ctime> #include <ctime>
#include <functional>
#include <optional>
#include <random>
#include <span>
#include <stdexcept>
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <optional>
#include <functional>
#include <type_traits> #include <type_traits>
#include <stdexcept>
#include "BitStream.h" #include "BitStream.h"
#include "NiPoint3.h" #include "NiPoint3.h"
#include "dPlatforms.h" #include "dPlatforms.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
@@ -32,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
@@ -62,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
@@ -70,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;
} }
@@ -82,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;
} }
@@ -97,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
/*! /*!
@@ -112,19 +115,19 @@ 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& from, const std::string& to); bool ReplaceInString(std::string& str, const std::string_view from, const std::string_view to);
std::u16string ReadWString(RakNet::BitStream& inStream); std::u16string ReadWString(RakNet::BitStream& inStream);
std::vector<std::wstring> SplitString(std::wstring& str, wchar_t delimiter); std::vector<std::wstring> SplitString(const std::wstring_view str, const wchar_t delimiter);
std::vector<std::u16string> SplitString(const std::u16string& str, char16_t delimiter); std::vector<std::u16string> SplitString(const std::u16string_view str, const char16_t delimiter);
std::vector<std::string> SplitString(const std::string& str, char delimiter); std::vector<std::string> SplitString(const std::string_view str, const char delimiter);
std::vector<std::string> GetSqlFileNamesFromFolder(const std::string& folder); std::vector<std::string> GetSqlFileNamesFromFolder(const std::string_view folder);
// Concept constraining to enum types // Concept constraining to enum types
template <typename T> template <typename T>
@@ -144,7 +147,7 @@ namespace GeneralUtils {
// If a boolean, present an alias to an intermediate integral type for parsing // If a boolean, present an alias to an intermediate integral type for parsing
template <Numeric T> requires std::same_as<T, bool> template <Numeric T> requires std::same_as<T, bool>
struct numeric_parse<T> { using type = uint32_t; }; struct numeric_parse<T> { using type = uint8_t; };
// Shorthand type alias // Shorthand type alias
template <Numeric T> template <Numeric T>
@@ -156,8 +159,9 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string * @returns An std::optional containing the desired value if it is equivalent to the string
*/ */
template <Numeric T> template <Numeric T>
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) { [[nodiscard]] std::optional<T> TryParse(std::string_view str) {
numeric_parse_t<T> result; 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 char* const strEnd = str.data() + str.size();
const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result); const auto [parseEnd, ec] = std::from_chars(str.data(), strEnd, result);
@@ -166,7 +170,7 @@ namespace GeneralUtils {
return isParsed ? static_cast<T>(result) : std::optional<T>{}; return isParsed ? static_cast<T>(result) : std::optional<T>{};
} }
#ifdef DARKFLAME_PLATFORM_MACOS #if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
// MacOS floating-point parse helper function specializations // MacOS floating-point parse helper function specializations
namespace details { namespace details {
@@ -181,8 +185,10 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired value if it is equivalent to the string * @returns An std::optional containing the desired value if it is equivalent to the string
*/ */
template <std::floating_point T> template <std::floating_point T>
[[nodiscard]] std::optional<T> TryParse(const std::string_view str) noexcept [[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
try { try {
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
size_t parseNum; size_t parseNum;
const T result = details::_parse<T>(str, parseNum); const T result = details::_parse<T>(str, parseNum);
const bool isParsed = str.length() == parseNum; const bool isParsed = str.length() == parseNum;
@@ -202,7 +208,7 @@ namespace GeneralUtils {
* @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::string& strX, const std::string& strY, const std::string& strZ) { [[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); const auto x = TryParse<float>(strX);
if (!x) return std::nullopt; if (!x) return std::nullopt;
@@ -214,17 +220,17 @@ namespace GeneralUtils {
} }
/** /**
* The TryParse overload for handling NiPoint3 by passingn a reference to a vector of three strings * The TryParse overload for handling NiPoint3 by passing a span of three strings
* @param str The string vector representing the X, Y, and Xcoordinates * @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 * @returns An std::optional containing the desired NiPoint3 if it can be constructed from the string parameters
*/ */
template <typename T> template <typename T>
[[nodiscard]] std::optional<NiPoint3> TryParse(const std::vector<std::string>& str) { [[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; return (str.size() == 3) ? TryParse<NiPoint3>(str[0], str[1], str[2]) : std::nullopt;
} }
template <typename T> template <typename T>
std::u16string to_u16string(T value) { std::u16string to_u16string(const T value) {
return GeneralUtils::ASCIIToUTF16(std::to_string(value)); return GeneralUtils::ASCIIToUTF16(std::to_string(value));
} }
@@ -243,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");
@@ -264,16 +270,16 @@ namespace GeneralUtils {
* @returns The enum entry's value in its underlying type * @returns The enum entry's value in its underlying type
*/ */
template <Enum eType> template <Enum eType>
constexpr typename std::underlying_type_t<eType> CastUnderlyingType(const eType entry) noexcept { constexpr std::underlying_type_t<eType> ToUnderlying(const eType entry) noexcept {
return static_cast<typename std::underlying_type_t<eType>>(entry); return static_cast<std::underlying_type_t<eType>>(entry);
} }
// on Windows we need to undef these or else they conflict with our numeric limits calls // on Windows we need to undef these or else they conflict with our numeric limits calls
// DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS // DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS DEVELOPERS
#ifdef _WIN32 #ifdef _WIN32
#undef min #undef min
#undef max #undef max
#endif #endif
template <typename T> template <typename T>
inline T GenerateRandomNumber() { inline T GenerateRandomNumber() {

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,7 +62,7 @@ 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<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<uint16_t>(this->key[i]); packet.Write<uint16_t>(this->key[i]);
@@ -70,7 +70,7 @@ private:
} }
//! 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<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write(this->value); packet.Write(this->value);
} }
@@ -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,11 +154,11 @@ 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);
} }
@@ -166,19 +166,19 @@ public:
}; };
// 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<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length()); packet.Write<uint32_t>(this->value.length());
@@ -189,7 +189,7 @@ inline void LDFData<std::u16string>::WriteValue(RakNet::BitStream& packet) {
// 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<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint8_t>(this->value); packet.Write<uint8_t>(this->value);
@@ -197,7 +197,7 @@ inline void LDFData<bool>::WriteValue(RakNet::BitStream& packet) {
// 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<uint8_t>(this->GetValueType()); packet.Write<uint8_t>(this->GetValueType());
packet.Write<uint32_t>(this->value.length()); packet.Write<uint32_t>(this->value.length());
@@ -206,18 +206,18 @@ inline void LDFData<std::string>::WriteValue(RakNet::BitStream& packet) {
} }
} }
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__

View File

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

View File

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

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

View File

@@ -0,0 +1,16 @@
#ifndef __EDELETIONRESTRICTIONSCHECKTYPE__H__
#define __EDELETIONRESTRICTIONSCHECKTYPE__H__
#include <cstdint>
enum class eDeletionRestrictionsCheckType : uint32_t {
INCLUDE_LOTS,
EXCLUDE_LOTS,
ANY_OF_THESE,
ALL_OF_THESE,
WHILE_IN_ZONE,
ALWAYS_RESTRICTED,
MAX
};
#endif //!__EDELETIONRESTRICTIONSCHECKTYPE__H__

View File

@@ -790,9 +790,10 @@ enum class eGameMessageType : uint16_t {
GET_MISSION_TYPE_STATES = 853, GET_MISSION_TYPE_STATES = 853,
GET_TIME_PLAYED = 854, GET_TIME_PLAYED = 854,
SET_MISSION_VIEWED = 855, SET_MISSION_VIEWED = 855,
SLASH_COMMAND_TEXT_FEEDBACK = 856, HKX_VEHICLE_LOADED = 856,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 857, SLASH_COMMAND_TEXT_FEEDBACK = 857,
BROADCAST_TEXT_TO_CHATBOX = 858, BROADCAST_TEXT_TO_CHATBOX = 858,
HANDLE_SLASH_COMMAND_KORE_DEBUGGER = 859,
OPEN_PROPERTY_MANAGEMENT = 860, OPEN_PROPERTY_MANAGEMENT = 860,
OPEN_PROPERTY_VENDOR = 861, OPEN_PROPERTY_VENDOR = 861,
VOTE_ON_PROPERTY = 862, VOTE_ON_PROPERTY = 862,

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

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

View File

@@ -25,6 +25,7 @@
#include "CDScriptComponentTable.h" #include "CDScriptComponentTable.h"
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
#include "CDZoneTableTable.h" #include "CDZoneTableTable.h"
#include "CDTamingBuildPuzzleTable.h"
#include "CDVendorComponentTable.h" #include "CDVendorComponentTable.h"
#include "CDActivitiesTable.h" #include "CDActivitiesTable.h"
#include "CDPackageComponentTable.h" #include "CDPackageComponentTable.h"
@@ -40,8 +41,7 @@
#include "CDRailActivatorComponent.h" #include "CDRailActivatorComponent.h"
#include "CDRewardCodesTable.h" #include "CDRewardCodesTable.h"
#include "CDPetComponentTable.h" #include "CDPetComponentTable.h"
#include "CDDeletionRestrictionsTable.h"
#include <exception>
#ifndef CDCLIENT_CACHE_ALL #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. // Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory.
@@ -55,13 +55,6 @@
#define CDCLIENT_DONT_CACHE_TABLE(x) #define CDCLIENT_DONT_CACHE_TABLE(x)
#endif #endif
class CDClientConnectionException : public std::exception {
public:
virtual const char* what() const throw() {
return "CDClientDatabase is not connected!";
}
};
// Using a macro to reduce repetitive code and issues from copy and paste. // 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. // As a note, ## in a macro is used to concatenate two tokens together.
@@ -108,11 +101,15 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable);
DEFINE_TABLE_STORAGE(CDRewardsTable); DEFINE_TABLE_STORAGE(CDRewardsTable);
DEFINE_TABLE_STORAGE(CDScriptComponentTable); DEFINE_TABLE_STORAGE(CDScriptComponentTable);
DEFINE_TABLE_STORAGE(CDSkillBehaviorTable); DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
DEFINE_TABLE_STORAGE(CDVendorComponentTable); DEFINE_TABLE_STORAGE(CDVendorComponentTable);
DEFINE_TABLE_STORAGE(CDZoneTableTable); DEFINE_TABLE_STORAGE(CDZoneTableTable);
DEFINE_TABLE_STORAGE(CDDeletionRestrictionsTable)
void CDClientManager::LoadValuesFromDatabase() { void CDClientManager::LoadValuesFromDatabase() {
if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); if (!CDClientDatabase::isConnected) {
throw std::runtime_error{ "CDClientDatabase is not connected!" };
}
CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
CDActivitiesTable::Instance().LoadValuesFromDatabase(); CDActivitiesTable::Instance().LoadValuesFromDatabase();
@@ -152,8 +149,10 @@ void CDClientManager::LoadValuesFromDatabase() {
CDRewardsTable::Instance().LoadValuesFromDatabase(); CDRewardsTable::Instance().LoadValuesFromDatabase();
CDScriptComponentTable::Instance().LoadValuesFromDatabase(); CDScriptComponentTable::Instance().LoadValuesFromDatabase();
CDSkillBehaviorTable::Instance().LoadValuesFromDatabase(); CDSkillBehaviorTable::Instance().LoadValuesFromDatabase();
CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase();
CDVendorComponentTable::Instance().LoadValuesFromDatabase(); CDVendorComponentTable::Instance().LoadValuesFromDatabase();
CDZoneTableTable::Instance().LoadValuesFromDatabase(); CDZoneTableTable::Instance().LoadValuesFromDatabase();
CDDeletionRestrictionsTable::Instance().LoadValuesFromDatabase();
} }
void CDClientManager::LoadValuesFromDefaults() { void CDClientManager::LoadValuesFromDefaults() {

View File

@@ -0,0 +1,45 @@
#include "CDDeletionRestrictionsTable.h"
#include "GeneralUtils.h"
#include "eDeletionRestrictionsCheckType.h"
CDDeletionRestriction CDDeletionRestrictionsTable::Default = {
.id = 0,
.restricted = false,
.ids = {},
.checkType = eDeletionRestrictionsCheckType::MAX
};
void CDDeletionRestrictionsTable::LoadValuesFromDatabase() {
auto& entries = GetEntriesMutable();
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM DeletionRestrictions");
while (!tableData.eof()) {
CDDeletionRestriction entry;
entry.id = tableData.getIntField("id", -1);
if (entry.id == -1) continue;
entry.restricted = tableData.getIntField("restricted", -1);
const std::string raw_ids = tableData.getStringField("ids", "");
if (!raw_ids.empty()) {
for (const auto& idstr : GeneralUtils::SplitString(raw_ids, ',')) {
if (!idstr.empty()) {
const auto id = GeneralUtils::TryParse<int32_t>(idstr);
if (id) entry.ids.push_back(id.value());
}
}
}
entry.checkType = static_cast<eDeletionRestrictionsCheckType>(tableData.getIntField("checkType", static_cast<int>(eDeletionRestrictionsCheckType::MAX)));
entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
}
const CDDeletionRestriction& CDDeletionRestrictionsTable::GetByID(uint32_t id) {
auto& entries = GetEntries();
const auto& it = entries.find(id);
if (it != entries.end()) {
return it->second;
}
return Default;
}

View File

@@ -0,0 +1,20 @@
#pragma once
#include "CDTable.h"
enum class eDeletionRestrictionsCheckType : uint32_t;
struct CDDeletionRestriction {
uint32_t id;
bool restricted;
std::vector<uint32_t> ids;
eDeletionRestrictionsCheckType checkType;
};
class CDDeletionRestrictionsTable : public CDTable<CDDeletionRestrictionsTable, std::map<uint32_t, CDDeletionRestriction>> {
public:
void LoadValuesFromDatabase();
const CDDeletionRestriction& GetByID(uint32_t id);
static CDDeletionRestriction Default;
};

View File

@@ -33,7 +33,7 @@ struct CDItemComponent {
uint32_t itemRating; //!< ??? uint32_t itemRating; //!< ???
bool isTwoHanded; //!< Whether or not the item is double handed bool isTwoHanded; //!< Whether or not the item is double handed
uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object? uint32_t minNumRequired; //!< Maybe the minimum number required for a mission, or to own this object?
uint32_t delResIndex; //!< ??? uint32_t delResIndex; //!< Relates to DeletionRestrictions Table
uint32_t currencyLOT; //!< ??? uint32_t currencyLOT; //!< ???
uint32_t altCurrencyCost; //!< ??? uint32_t altCurrencyCost; //!< ???
std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set) std::string subItems; //!< A comma seperate string of sub items (maybe for multi-itemed things like faction test gear set)

View File

@@ -58,7 +58,7 @@ void CDLootTableTable::LoadValuesFromDatabase() {
CDLootTable entry; CDLootTable entry;
uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1); uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
entries[lootTableIndex].push_back(ReadRow(tableData)); entries[lootTableIndex].emplace_back(ReadRow(tableData));
tableData.nextRow(); tableData.nextRow();
} }
for (auto& [id, table] : entries) { for (auto& [id, table] : entries) {
@@ -66,7 +66,7 @@ void CDLootTableTable::LoadValuesFromDatabase() {
} }
} }
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) { const LootTableEntries& CDLootTableTable::GetTable(const uint32_t tableId) {
auto& entries = GetEntriesMutable(); auto& entries = GetEntriesMutable();
auto itr = entries.find(tableId); auto itr = entries.find(tableId);
if (itr != entries.end()) { if (itr != entries.end()) {
@@ -79,7 +79,7 @@ const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
while (!tableData.eof()) { while (!tableData.eof()) {
CDLootTable entry; CDLootTable entry;
entries[tableId].push_back(ReadRow(tableData)); entries[tableId].emplace_back(ReadRow(tableData));
tableData.nextRow(); tableData.nextRow();
} }
SortTable(entries[tableId]); SortTable(entries[tableId]);

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDLootTable { struct CDLootTable {
uint32_t itemid; //!< The LOT of the item uint32_t itemid; //!< The LOT of the item
uint32_t LootTableIndex; //!< The Loot Table Index uint32_t LootTableIndex; //!< The Loot Table Index
@@ -20,6 +22,5 @@ private:
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
const LootTableEntries& GetTable(uint32_t tableId); const LootTableEntries& GetTable(const uint32_t tableId);
}; };

View File

@@ -20,7 +20,7 @@ void CDMissionEmailTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionEmail"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionEmail");
while (!tableData.eof()) { while (!tableData.eof()) {
CDMissionEmail entry; auto& entry = entries.emplace_back();
entry.ID = tableData.getIntField("ID", -1); entry.ID = tableData.getIntField("ID", -1);
entry.messageType = tableData.getIntField("messageType", -1); entry.messageType = tableData.getIntField("messageType", -1);
entry.notificationGroup = tableData.getIntField("notificationGroup", -1); entry.notificationGroup = tableData.getIntField("notificationGroup", -1);
@@ -30,11 +30,8 @@ void CDMissionEmailTable::LoadValuesFromDatabase() {
entry.locStatus = tableData.getIntField("locStatus", -1); entry.locStatus = tableData.getIntField("locStatus", -1);
entry.gate_version = tableData.getStringField("gate_version", ""); entry.gate_version = tableData.getStringField("gate_version", "");
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
//! Queries the table with a custom "where" clause //! Queries the table with a custom "where" clause

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDMissionEmail { struct CDMissionEmail {
uint32_t ID; uint32_t ID;
uint32_t messageType; uint32_t messageType;

View File

@@ -20,18 +20,15 @@ void CDMissionNPCComponentTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionNPCComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionNPCComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDMissionNPCComponent entry; auto& entry = entries.emplace_back();
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.missionID = tableData.getIntField("missionID", -1); entry.missionID = tableData.getIntField("missionID", -1);
entry.offersMission = tableData.getIntField("offersMission", -1) == 1 ? true : false; entry.offersMission = tableData.getIntField("offersMission", -1) == 1 ? true : false;
entry.acceptsMission = tableData.getIntField("acceptsMission", -1) == 1 ? true : false; entry.acceptsMission = tableData.getIntField("acceptsMission", -1) == 1 ? true : false;
entry.gate_version = tableData.getStringField("gate_version", ""); entry.gate_version = tableData.getStringField("gate_version", "");
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
//! Queries the table with a custom "where" clause //! Queries the table with a custom "where" clause

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDMissionNPCComponent { struct CDMissionNPCComponent {
uint32_t id; //!< The ID uint32_t id; //!< The ID
uint32_t missionID; //!< The Mission ID uint32_t missionID; //!< The Mission ID
@@ -17,4 +19,3 @@ public:
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDMissionNPCComponent> Query(std::function<bool(CDMissionNPCComponent)> predicate); std::vector<CDMissionNPCComponent> Query(std::function<bool(CDMissionNPCComponent)> predicate);
}; };

View File

@@ -20,7 +20,7 @@ void CDMissionTasksTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionTasks"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MissionTasks");
while (!tableData.eof()) { while (!tableData.eof()) {
CDMissionTasks entry; auto& entry = entries.emplace_back();
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1)); UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
entry.taskType = tableData.getIntField("taskType", -1); entry.taskType = tableData.getIntField("taskType", -1);
@@ -35,11 +35,8 @@ void CDMissionTasksTable::LoadValuesFromDatabase() {
UNUSED(entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false); UNUSED(entry.localize = tableData.getIntField("localize", -1) == 1 ? true : false);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
std::vector<CDMissionTasks> CDMissionTasksTable::Query(std::function<bool(CDMissionTasks)> predicate) { std::vector<CDMissionTasks> CDMissionTasksTable::Query(std::function<bool(CDMissionTasks)> predicate) {
@@ -51,7 +48,7 @@ std::vector<CDMissionTasks> CDMissionTasksTable::Query(std::function<bool(CDMiss
return data; return data;
} }
std::vector<CDMissionTasks*> CDMissionTasksTable::GetByMissionID(uint32_t missionID) { std::vector<CDMissionTasks*> CDMissionTasksTable::GetByMissionID(const uint32_t missionID) {
std::vector<CDMissionTasks*> tasks; std::vector<CDMissionTasks*> tasks;
// TODO: this should not be linear(?) and also shouldnt need to be a pointer // TODO: this should not be linear(?) and also shouldnt need to be a pointer

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDMissionTasks { struct CDMissionTasks {
uint32_t id; //!< The Mission ID that the task belongs to uint32_t id; //!< The Mission ID that the task belongs to
UNUSED(uint32_t locStatus); //!< ??? UNUSED(uint32_t locStatus); //!< ???
@@ -25,7 +27,7 @@ public:
// Queries the table with a custom "where" clause // Queries the table with a custom "where" clause
std::vector<CDMissionTasks> Query(std::function<bool(CDMissionTasks)> predicate); std::vector<CDMissionTasks> Query(std::function<bool(CDMissionTasks)> predicate);
std::vector<CDMissionTasks*> GetByMissionID(uint32_t missionID); std::vector<CDMissionTasks*> GetByMissionID(const uint32_t missionID);
// TODO: Remove this and replace it with a proper lookup function. // TODO: Remove this and replace it with a proper lookup function.
const CDTable::StorageType& GetEntries() const; const CDTable::StorageType& GetEntries() const;

View File

@@ -22,7 +22,7 @@ void CDMissionsTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Missions"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Missions");
while (!tableData.eof()) { while (!tableData.eof()) {
CDMissions entry; auto& entry = entries.emplace_back();
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.defined_type = tableData.getStringField("defined_type", ""); entry.defined_type = tableData.getStringField("defined_type", "");
entry.defined_subtype = tableData.getStringField("defined_subtype", ""); entry.defined_subtype = tableData.getStringField("defined_subtype", "");
@@ -76,7 +76,6 @@ void CDMissionsTable::LoadValuesFromDatabase() {
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1)); UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
entry.reward_bankinventory = tableData.getIntField("reward_bankinventory", -1); entry.reward_bankinventory = tableData.getIntField("reward_bankinventory", -1);
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize(); tableData.finalize();

View File

@@ -75,4 +75,3 @@ public:
static CDMissions Default; static CDMissions Default;
}; };

View File

@@ -20,7 +20,7 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MovementAIComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM MovementAIComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDMovementAIComponent entry; auto& entry = entries.emplace_back();
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.MovementType = tableData.getStringField("MovementType", ""); entry.MovementType = tableData.getStringField("MovementType", "");
entry.WanderChance = tableData.getFloatField("WanderChance", -1.0f); entry.WanderChance = tableData.getFloatField("WanderChance", -1.0f);
@@ -30,11 +30,8 @@ void CDMovementAIComponentTable::LoadValuesFromDatabase() {
entry.WanderRadius = tableData.getFloatField("WanderRadius", -1.0f); entry.WanderRadius = tableData.getFloatField("WanderRadius", -1.0f);
entry.attachedPath = tableData.getStringField("attachedPath", ""); entry.attachedPath = tableData.getStringField("attachedPath", "");
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
std::vector<CDMovementAIComponent> CDMovementAIComponentTable::Query(std::function<bool(CDMovementAIComponent)> predicate) { std::vector<CDMovementAIComponent> CDMovementAIComponentTable::Query(std::function<bool(CDMovementAIComponent)> predicate) {

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDMovementAIComponent { struct CDMovementAIComponent {
uint32_t id; uint32_t id;
std::string MovementType; std::string MovementType;

View File

@@ -20,17 +20,14 @@ void CDObjectSkillsTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ObjectSkills"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ObjectSkills");
while (!tableData.eof()) { while (!tableData.eof()) {
CDObjectSkills entry; auto &entry = entries.emplace_back();
entry.objectTemplate = tableData.getIntField("objectTemplate", -1); entry.objectTemplate = tableData.getIntField("objectTemplate", -1);
entry.skillID = tableData.getIntField("skillID", -1); entry.skillID = tableData.getIntField("skillID", -1);
entry.castOnType = tableData.getIntField("castOnType", -1); entry.castOnType = tableData.getIntField("castOnType", -1);
entry.AICombatWeight = tableData.getIntField("AICombatWeight", -1); entry.AICombatWeight = tableData.getIntField("AICombatWeight", -1);
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObjectSkills)> predicate) { std::vector<CDObjectSkills> CDObjectSkillsTable::Query(std::function<bool(CDObjectSkills)> predicate) {

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDObjectSkills { struct CDObjectSkills {
uint32_t objectTemplate; //!< The LOT of the item uint32_t objectTemplate; //!< The LOT of the item
uint32_t skillID; //!< The Skill ID of the object uint32_t skillID; //!< The Skill ID of the object

View File

@@ -1,7 +1,7 @@
#include "CDObjectsTable.h" #include "CDObjectsTable.h"
namespace { namespace {
CDObjects m_default; CDObjects ObjDefault;
}; };
void CDObjectsTable::LoadValuesFromDatabase() { void CDObjectsTable::LoadValuesFromDatabase() {
@@ -20,8 +20,10 @@ void CDObjectsTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Objects"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Objects");
auto& entries = GetEntriesMutable(); auto& entries = GetEntriesMutable();
while (!tableData.eof()) { while (!tableData.eof()) {
CDObjects entry; const uint32_t lot = tableData.getIntField("id", 0);
entry.id = tableData.getIntField("id", -1);
auto& entry = entries[lot];
entry.id = lot;
entry.name = tableData.getStringField("name", ""); entry.name = tableData.getStringField("name", "");
UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);) UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);)
entry.type = tableData.getStringField("type", ""); entry.type = tableData.getStringField("type", "");
@@ -36,35 +38,34 @@ void CDObjectsTable::LoadValuesFromDatabase() {
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");) UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");)
UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);) UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);)
entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize(); ObjDefault.id = 0;
m_default.id = 0;
} }
const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) { const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
auto& entries = GetEntriesMutable(); auto& entries = GetEntriesMutable();
const auto& it = entries.find(LOT); const auto& it = entries.find(lot);
if (it != entries.end()) { if (it != entries.end()) {
return it->second; return it->second;
} }
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;"); auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;");
query.bind(1, static_cast<int32_t>(LOT)); query.bind(1, static_cast<int32_t>(lot));
auto tableData = query.execQuery(); auto tableData = query.execQuery();
if (tableData.eof()) { if (tableData.eof()) {
entries.insert(std::make_pair(LOT, m_default)); entries.emplace(lot, ObjDefault);
return m_default; return ObjDefault;
} }
// Now get the data // Now get the data
while (!tableData.eof()) { while (!tableData.eof()) {
CDObjects entry; const uint32_t lot = tableData.getIntField("id", 0);
entry.id = tableData.getIntField("id", -1);
auto& entry = entries[lot];
entry.id = lot;
entry.name = tableData.getStringField("name", ""); entry.name = tableData.getStringField("name", "");
UNUSED(entry.placeable = tableData.getIntField("placeable", -1)); UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
entry.type = tableData.getStringField("type", ""); entry.type = tableData.getStringField("type", "");
@@ -79,17 +80,15 @@ const CDObjects& CDObjectsTable::GetByID(uint32_t LOT) {
UNUSED(entry.gate_version = tableData.getStringField("gate_version", "")); UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
UNUSED(entry.HQ_valid = tableData.getIntField("HQ_valid", -1)); UNUSED(entry.HQ_valid = tableData.getIntField("HQ_valid", -1));
entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize(); tableData.finalize();
const auto& it2 = entries.find(LOT); const auto& it2 = entries.find(lot);
if (it2 != entries.end()) { if (it2 != entries.end()) {
return it2->second; return it2->second;
} }
return m_default; return ObjDefault;
} }

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDObjects { struct CDObjects {
uint32_t id; //!< The LOT of the object uint32_t id; //!< The LOT of the object
std::string name; //!< The internal name of the object std::string name; //!< The internal name of the object
@@ -24,6 +26,6 @@ class CDObjectsTable : public CDTable<CDObjectsTable, std::map<uint32_t, CDObjec
public: public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
// Gets an entry by ID // Gets an entry by ID
const CDObjects& GetByID(uint32_t LOT); const CDObjects& GetByID(const uint32_t lot);
}; };

View File

@@ -19,12 +19,11 @@ void CDPackageComponentTable::LoadValuesFromDatabase() {
// Now get the data // Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PackageComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PackageComponent");
while (!tableData.eof()) { while (!tableData.eof()) {
CDPackageComponent entry; auto& entry = entries.emplace_back();
entry.id = tableData.getIntField("id", -1); entry.id = tableData.getIntField("id", -1);
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1); entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
entry.packageType = tableData.getIntField("packageType", -1); entry.packageType = tableData.getIntField("packageType", -1);
entries.push_back(entry);
tableData.nextRow(); tableData.nextRow();
} }

View File

@@ -3,6 +3,8 @@
// Custom Classes // Custom Classes
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
struct CDPackageComponent { struct CDPackageComponent {
uint32_t id; uint32_t id;
uint32_t LootMatrixIndex; uint32_t LootMatrixIndex;

View File

@@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() {
} }
void CDPetComponentTable::LoadValuesFromDefaults() { void CDPetComponentTable::LoadValuesFromDefaults() {
GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry)); GetEntriesMutable().emplace(defaultEntry.id, defaultEntry);
} }
CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) {

View File

@@ -4,32 +4,31 @@ void CDPhysicsComponentTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent"); auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM PhysicsComponent");
auto& entries = GetEntriesMutable(); auto& entries = GetEntriesMutable();
while (!tableData.eof()) { while (!tableData.eof()) {
CDPhysicsComponent entry; const uint32_t componentID = tableData.getIntField("id", -1);
entry.id = tableData.getIntField("id", -1);
auto& entry = entries[componentID];
entry.id = componentID;
entry.bStatic = tableData.getIntField("static", -1) != 0; entry.bStatic = tableData.getIntField("static", -1) != 0;
entry.physicsAsset = tableData.getStringField("physics_asset", ""); entry.physicsAsset = tableData.getStringField("physics_asset", "");
UNUSED(entry->jump = tableData.getIntField("jump", -1) != 0); UNUSED_COLUMN(entry.jump = tableData.getIntField("jump", -1) != 0;)
UNUSED(entry->doublejump = tableData.getIntField("doublejump", -1) != 0); UNUSED_COLUMN(entry.doubleJump = tableData.getIntField("doublejump", -1) != 0;)
entry.speed = tableData.getFloatField("speed", -1); entry.speed = static_cast<float>(tableData.getFloatField("speed", -1));
UNUSED(entry->rotSpeed = tableData.getFloatField("rotSpeed", -1)); UNUSED_COLUMN(entry.rotSpeed = tableData.getFloatField("rotSpeed", -1);)
entry.playerHeight = tableData.getFloatField("playerHeight"); entry.playerHeight = static_cast<float>(tableData.getFloatField("playerHeight"));
entry.playerRadius = tableData.getFloatField("playerRadius"); entry.playerRadius = static_cast<float>(tableData.getFloatField("playerRadius"));
entry.pcShapeType = tableData.getIntField("pcShapeType"); entry.pcShapeType = tableData.getIntField("pcShapeType");
entry.collisionGroup = tableData.getIntField("collisionGroup"); entry.collisionGroup = tableData.getIntField("collisionGroup");
UNUSED(entry->airSpeed = tableData.getFloatField("airSpeed")); UNUSED_COLUMN(entry.airSpeed = tableData.getFloatField("airSpeed");)
UNUSED(entry->boundaryAsset = tableData.getStringField("boundaryAsset")); UNUSED_COLUMN(entry.boundaryAsset = tableData.getStringField("boundaryAsset");)
UNUSED(entry->jumpAirSpeed = tableData.getFloatField("jumpAirSpeed")); UNUSED_COLUMN(entry.jumpAirSpeed = tableData.getFloatField("jumpAirSpeed");)
UNUSED(entry->friction = tableData.getFloatField("friction")); UNUSED_COLUMN(entry.friction = tableData.getFloatField("friction");)
UNUSED(entry->gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset")); UNUSED_COLUMN(entry.gravityVolumeAsset = tableData.getStringField("gravityVolumeAsset");)
entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow(); tableData.nextRow();
} }
tableData.finalize();
} }
CDPhysicsComponent* CDPhysicsComponentTable::GetByID(uint32_t componentID) { CDPhysicsComponent* CDPhysicsComponentTable::GetByID(const uint32_t componentID) {
auto& entries = GetEntriesMutable(); auto& entries = GetEntriesMutable();
auto itr = entries.find(componentID); auto itr = entries.find(componentID);
return itr != entries.end() ? &itr->second : nullptr; return itr != entries.end() ? &itr->second : nullptr;

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "CDTable.h" #include "CDTable.h"
#include <cstdint>
#include <string> #include <string>
struct CDPhysicsComponent { struct CDPhysicsComponent {
@@ -7,7 +8,7 @@ struct CDPhysicsComponent {
bool bStatic; bool bStatic;
std::string physicsAsset; std::string physicsAsset;
UNUSED(bool jump); UNUSED(bool jump);
UNUSED(bool doublejump); UNUSED(bool doubleJump);
float speed; float speed;
UNUSED(float rotSpeed); UNUSED(float rotSpeed);
float playerHeight; float playerHeight;
@@ -26,5 +27,5 @@ public:
void LoadValuesFromDatabase(); void LoadValuesFromDatabase();
static const std::string GetTableName() { return "PhysicsComponent"; }; static const std::string GetTableName() { return "PhysicsComponent"; };
CDPhysicsComponent* GetByID(uint32_t componentID); CDPhysicsComponent* GetByID(const uint32_t componentID);
}; };

View File

@@ -0,0 +1,35 @@
#include "CDTamingBuildPuzzleTable.h"
void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() {
// First, get the size of the table
uint32_t size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
// Reserve the size
auto& entries = GetEntriesMutable();
entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles");
while (!tableData.eof()) {
const auto lot = static_cast<LOT>(tableData.getIntField("NPCLot", LOT_NULL));
entries.emplace(lot, CDTamingBuildPuzzle{
.puzzleModelLot = lot,
.validPieces{ tableData.getStringField("ValidPiecesLXF") },
.timeLimit = static_cast<float>(tableData.getFloatField("Timelimit", 30.0f)),
.numValidPieces = tableData.getIntField("NumValidPieces", 6),
.imaginationCost = tableData.getIntField("imagCostPerBuild", 10)
});
tableData.nextRow();
}
}
const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const {
const auto& entries = GetEntries();
const auto itr = entries.find(lot);
return itr != entries.cend() ? &itr->second : nullptr;
}

View File

@@ -0,0 +1,60 @@
#pragma once
#include "CDTable.h"
/**
* Information for the minigame to be completed
*/
struct CDTamingBuildPuzzle {
UNUSED_COLUMN(uint32_t id = 0;)
// The LOT of the object that is to be created
LOT puzzleModelLot = LOT_NULL;
// The LOT of the NPC
UNUSED_COLUMN(LOT npcLot = LOT_NULL;)
// The .lxfml file that contains the bricks required to build the model
std::string validPieces{};
// The .lxfml file that contains the bricks NOT required to build the model
UNUSED_COLUMN(std::string invalidPieces{};)
// Difficulty value
UNUSED_COLUMN(int32_t difficulty = 1;)
// The time limit to complete the build
float timeLimit = 30.0f;
// The number of pieces required to complete the minigame
int32_t numValidPieces = 6;
// Number of valid pieces
UNUSED_COLUMN(int32_t totalNumPieces = 16;)
// Model name
UNUSED_COLUMN(std::string modelName{};)
// The .lxfml file that contains the full model
UNUSED_COLUMN(std::string fullModel{};)
// The duration of the pet taming minigame
UNUSED_COLUMN(float duration = 45.0f;)
// The imagination cost for the tamer to start the minigame
int32_t imaginationCost = 10;
};
class CDTamingBuildPuzzleTable : public CDTable<CDTamingBuildPuzzleTable, std::unordered_map<LOT, CDTamingBuildPuzzle>> {
public:
/**
* Load values from the CD client database
*/
void LoadValuesFromDatabase();
/**
* Gets the pet ability table corresponding to the pet LOT
* @returns A pointer to the corresponding table, or nullptr if one cannot be found
*/
[[nodiscard]]
const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const;
};

View File

@@ -36,5 +36,8 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp"
"CDRewardsTable.cpp" "CDRewardsTable.cpp"
"CDScriptComponentTable.cpp" "CDScriptComponentTable.cpp"
"CDSkillBehaviorTable.cpp" "CDSkillBehaviorTable.cpp"
"CDTamingBuildPuzzleTable.cpp"
"CDVendorComponentTable.cpp" "CDVendorComponentTable.cpp"
"CDZoneTableTable.cpp" PARENT_SCOPE) "CDZoneTableTable.cpp"
"CDDeletionRestrictionsTable.cpp"
PARENT_SCOPE)

View File

@@ -27,12 +27,11 @@ Character::Character(uint32_t id, User* parentUser) {
m_ID = id; m_ID = id;
m_ParentUser = parentUser; m_ParentUser = parentUser;
m_OurEntity = nullptr; m_OurEntity = nullptr;
m_Doc = nullptr; m_GMLevel = eGameMasterLevel::CIVILIAN;
m_PermissionMap = static_cast<ePermissionMap>(0);
} }
Character::~Character() { Character::~Character() {
if (m_Doc) delete m_Doc;
m_Doc = nullptr;
m_OurEntity = nullptr; m_OurEntity = nullptr;
m_ParentUser = nullptr; m_ParentUser = nullptr;
} }
@@ -55,8 +54,6 @@ void Character::UpdateInfoFromDatabase() {
m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused. m_ZoneInstanceID = 0; //These values don't really matter, these are only used on the char select screen and seem unused.
m_ZoneCloneID = 0; m_ZoneCloneID = 0;
m_Doc = nullptr;
//Quickly and dirtly parse the xmlData to get the info we need: //Quickly and dirtly parse the xmlData to get the info we need:
DoQuickXMLDataParse(); DoQuickXMLDataParse();
@@ -70,18 +67,13 @@ void Character::UpdateInfoFromDatabase() {
} }
void Character::UpdateFromDatabase() { void Character::UpdateFromDatabase() {
if (m_Doc) delete m_Doc;
UpdateInfoFromDatabase(); UpdateInfoFromDatabase();
} }
void Character::DoQuickXMLDataParse() { void Character::DoQuickXMLDataParse() {
if (m_XMLData.size() == 0) return; if (m_XMLData.size() == 0) return;
delete m_Doc; if (m_Doc.Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
m_Doc = new tinyxml2::XMLDocument();
if (!m_Doc) return;
if (m_Doc->Parse(m_XMLData.c_str(), m_XMLData.size()) == 0) {
LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID); LOG("Loaded xmlData for character %s (%i)!", m_Name.c_str(), m_ID);
} else { } else {
LOG("Failed to load xmlData!"); LOG("Failed to load xmlData!");
@@ -89,7 +81,7 @@ void Character::DoQuickXMLDataParse() {
return; return;
} }
tinyxml2::XMLElement* mf = m_Doc->FirstChildElement("obj")->FirstChildElement("mf"); tinyxml2::XMLElement* mf = m_Doc.FirstChildElement("obj")->FirstChildElement("mf");
if (!mf) { if (!mf) {
LOG("Failed to find mf tag!"); LOG("Failed to find mf tag!");
return; return;
@@ -108,7 +100,7 @@ void Character::DoQuickXMLDataParse() {
mf->QueryAttribute("ess", &m_Eyes); mf->QueryAttribute("ess", &m_Eyes);
mf->QueryAttribute("ms", &m_Mouth); mf->QueryAttribute("ms", &m_Mouth);
tinyxml2::XMLElement* inv = m_Doc->FirstChildElement("obj")->FirstChildElement("inv"); tinyxml2::XMLElement* inv = m_Doc.FirstChildElement("obj")->FirstChildElement("inv");
if (!inv) { if (!inv) {
LOG("Char has no inv!"); LOG("Char has no inv!");
return; return;
@@ -141,7 +133,7 @@ void Character::DoQuickXMLDataParse() {
} }
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
if (character) { if (character) {
character->QueryAttribute("cc", &m_Coins); character->QueryAttribute("cc", &m_Coins);
int32_t gm_level = 0; int32_t gm_level = 0;
@@ -205,7 +197,7 @@ void Character::DoQuickXMLDataParse() {
character->QueryAttribute("lzrw", &m_OriginalRotation.w); character->QueryAttribute("lzrw", &m_OriginalRotation.w);
} }
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (flags) { if (flags) {
auto* currentChild = flags->FirstChildElement(); auto* currentChild = flags->FirstChildElement();
while (currentChild) { while (currentChild) {
@@ -239,12 +231,10 @@ void Character::SetBuildMode(bool buildMode) {
} }
void Character::SaveXMLToDatabase() { void Character::SaveXMLToDatabase() {
if (!m_Doc) return;
//For metrics, we'll record the time it took to save: //For metrics, we'll record the time it took to save:
auto start = std::chrono::system_clock::now(); auto start = std::chrono::system_clock::now();
tinyxml2::XMLElement* character = m_Doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = m_Doc.FirstChildElement("obj")->FirstChildElement("char");
if (character) { if (character) {
character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel)); character->SetAttribute("gm", static_cast<uint32_t>(m_GMLevel));
character->SetAttribute("cc", m_Coins); character->SetAttribute("cc", m_Coins);
@@ -266,11 +256,11 @@ void Character::SaveXMLToDatabase() {
} }
auto emotes = character->FirstChildElement("ue"); auto emotes = character->FirstChildElement("ue");
if (!emotes) emotes = m_Doc->NewElement("ue"); if (!emotes) emotes = m_Doc.NewElement("ue");
emotes->DeleteChildren(); emotes->DeleteChildren();
for (int emoteID : m_UnlockedEmotes) { for (int emoteID : m_UnlockedEmotes) {
auto emote = m_Doc->NewElement("e"); auto emote = m_Doc.NewElement("e");
emote->SetAttribute("id", emoteID); emote->SetAttribute("id", emoteID);
emotes->LinkEndChild(emote); emotes->LinkEndChild(emote);
@@ -280,15 +270,15 @@ void Character::SaveXMLToDatabase() {
} }
//Export our flags: //Export our flags:
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) { if (!flags) {
flags = m_Doc->NewElement("flag"); //Create a flags tag if we don't have one flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one
m_Doc->FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time
} }
flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes
for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) { for (std::pair<uint32_t, uint64_t> flag : m_PlayerFlags) {
auto* f = m_Doc->NewElement("f"); auto* f = m_Doc.NewElement("f");
f->SetAttribute("id", flag.first); f->SetAttribute("id", flag.first);
//Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute. //Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute.
@@ -301,7 +291,7 @@ void Character::SaveXMLToDatabase() {
// Prevents the news feed from showing up on world transfers // Prevents the news feed from showing up on world transfers
if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) { if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) {
auto* s = m_Doc->NewElement("s"); auto* s = m_Doc.NewElement("s");
s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE); s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE);
flags->LinkEndChild(s); flags->LinkEndChild(s);
} }
@@ -326,7 +316,7 @@ void Character::SaveXMLToDatabase() {
void Character::SetIsNewLogin() { void Character::SetIsNewLogin() {
// If we dont have a flag element, then we cannot have a s element as a child of flag. // If we dont have a flag element, then we cannot have a s element as a child of flag.
auto* flags = m_Doc->FirstChildElement("obj")->FirstChildElement("flag"); auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag");
if (!flags) return; if (!flags) return;
auto* currentChild = flags->FirstChildElement(); auto* currentChild = flags->FirstChildElement();
@@ -344,7 +334,7 @@ void Character::SetIsNewLogin() {
void Character::WriteToDatabase() { void Character::WriteToDatabase() {
//Dump our xml into m_XMLData: //Dump our xml into m_XMLData:
tinyxml2::XMLPrinter printer(0, true, 0); tinyxml2::XMLPrinter printer(0, true, 0);
m_Doc->Print(&printer); m_Doc.Print(&printer);
//Finally, save to db: //Finally, save to db:
Database::Get()->UpdateCharacterXml(m_ID, printer.CStr()); Database::Get()->UpdateCharacterXml(m_ID, printer.CStr());
@@ -421,15 +411,15 @@ void Character::SetRetroactiveFlags() {
void Character::SaveXmlRespawnCheckpoints() { void Character::SaveXmlRespawnCheckpoints() {
//Export our respawn points: //Export our respawn points:
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res"); auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
if (!points) { if (!points) {
points = m_Doc->NewElement("res"); points = m_Doc.NewElement("res");
m_Doc->FirstChildElement("obj")->LinkEndChild(points); m_Doc.FirstChildElement("obj")->LinkEndChild(points);
} }
points->DeleteChildren(); points->DeleteChildren();
for (const auto& point : m_WorldRespawnCheckpoints) { for (const auto& point : m_WorldRespawnCheckpoints) {
auto* r = m_Doc->NewElement("r"); auto* r = m_Doc.NewElement("r");
r->SetAttribute("w", point.first); r->SetAttribute("w", point.first);
r->SetAttribute("x", point.second.x); r->SetAttribute("x", point.second.x);
@@ -443,7 +433,7 @@ void Character::SaveXmlRespawnCheckpoints() {
void Character::LoadXmlRespawnCheckpoints() { void Character::LoadXmlRespawnCheckpoints() {
m_WorldRespawnCheckpoints.clear(); m_WorldRespawnCheckpoints.clear();
auto* points = m_Doc->FirstChildElement("obj")->FirstChildElement("res"); auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res");
if (!points) { if (!points) {
return; return;
} }

View File

@@ -37,7 +37,7 @@ public:
void LoadXmlRespawnCheckpoints(); void LoadXmlRespawnCheckpoints();
const std::string& GetXMLData() const { return m_XMLData; } const std::string& GetXMLData() const { return m_XMLData; }
tinyxml2::XMLDocument* GetXMLDoc() const { return m_Doc; } const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; }
/** /**
* Out of abundance of safety and clarity of what this saves, this is its own function. * Out of abundance of safety and clarity of what this saves, this is its own function.
@@ -464,22 +464,22 @@ private:
/** /**
* The ID of this character. First 32 bits of the ObjectID. * The ID of this character. First 32 bits of the ObjectID.
*/ */
uint32_t m_ID; uint32_t m_ID{};
/** /**
* The 64-bit unique ID used in the game. * The 64-bit unique ID used in the game.
*/ */
LWOOBJID m_ObjectID; LWOOBJID m_ObjectID{ LWOOBJID_EMPTY };
/** /**
* The user that owns this character. * The user that owns this character.
*/ */
User* m_ParentUser; User* m_ParentUser{};
/** /**
* If the character is in game, this is the entity that it represents, else nullptr. * If the character is in game, this is the entity that it represents, else nullptr.
*/ */
Entity* m_OurEntity; Entity* m_OurEntity{};
/** /**
* 0-9, the Game Master level of this character. * 0-9, the Game Master level of this character.
@@ -506,17 +506,17 @@ private:
/** /**
* Whether the custom name of this character is rejected * Whether the custom name of this character is rejected
*/ */
bool m_NameRejected; bool m_NameRejected{};
/** /**
* The current amount of coins of this character * The current amount of coins of this character
*/ */
int64_t m_Coins; int64_t m_Coins{};
/** /**
* Whether the character is building * Whether the character is building
*/ */
bool m_BuildMode; bool m_BuildMode{};
/** /**
* The items equipped by the character on world load * The items equipped by the character on world load
@@ -583,7 +583,7 @@ private:
/** /**
* The ID of the properties of this character * The ID of the properties of this character
*/ */
uint32_t m_PropertyCloneID; uint32_t m_PropertyCloneID{};
/** /**
* The XML data for this character, stored as string * The XML data for this character, stored as string
@@ -613,7 +613,7 @@ private:
/** /**
* The last time this character logged in * The last time this character logged in
*/ */
uint64_t m_LastLogin; uint64_t m_LastLogin{};
/** /**
* The gameplay flags this character has (not just true values) * The gameplay flags this character has (not just true values)
@@ -623,7 +623,7 @@ private:
/** /**
* The character XML belonging to this character * The character XML belonging to this character
*/ */
tinyxml2::XMLDocument* m_Doc; tinyxml2::XMLDocument m_Doc;
/** /**
* Title of an announcement this character made (reserved for GMs) * Title of an announcement this character made (reserved for GMs)

View File

@@ -476,8 +476,7 @@ void Entity::Initialize() {
} }
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) {
auto* xmlDoc = m_Character ? m_Character->GetXMLDoc() : nullptr; AddComponent<InventoryComponent>();
AddComponent<InventoryComponent>(xmlDoc);
} }
// if this component exists, then we initialize it. it's value is always 0 // if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) { if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
@@ -731,15 +730,21 @@ void Entity::Initialize() {
// if we have a moving platform path, then we need a moving platform component // if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) { if (path->pathType == PathType::MovingPlatform) {
AddComponent<MovingPlatformComponent>(pathName); AddComponent<MovingPlatformComponent>(pathName);
// else if we are a movement path } else if (path->pathType == PathType::Movement) {
} /*else if (path->pathType == PathType::Movement) { auto movementAIcomponent = GetComponent<MovementAIComponent>();
auto movementAIcomp = GetComponent<MovementAIComponent>(); if (movementAIcomponent && combatAiId == 0) {
if (movementAIcomp){ movementAIcomponent->SetPath(pathName);
// TODO: set path in existing movementAIComp
} else { } else {
// TODO: create movementAIcomp and set path MovementAIInfo moveInfo = MovementAIInfo();
moveInfo.movementType = "";
moveInfo.wanderChance = 0;
moveInfo.wanderRadius = 16;
moveInfo.wanderSpeed = 2.5f;
moveInfo.wanderDelayMax = 5;
moveInfo.wanderDelayMin = 2;
AddComponent<MovementAIComponent>(moveInfo);
} }
}*/ }
} else { } else {
// else we still need to setup moving platform if it has a moving platform comp but no path // else we still need to setup moving platform if it has a moving platform comp but no path
int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1); int32_t movingPlatformComponentId = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MOVING_PLATFORM, -1);
@@ -1238,7 +1243,7 @@ void Entity::WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType
outBitStream.Write0(); outBitStream.Write0();
} }
void Entity::UpdateXMLDoc(tinyxml2::XMLDocument* doc) { void Entity::UpdateXMLDoc(tinyxml2::XMLDocument& doc) {
//This function should only ever be called from within Character, meaning doc should always exist when this is called. //This function should only ever be called from within Character, meaning doc should always exist when this is called.
//Naturally, we don't include any non-player components in this update function. //Naturally, we don't include any non-player components in this update function.
@@ -1529,7 +1534,7 @@ void Entity::Kill(Entity* murderer, const eKillType killType) {
bool waitForDeathAnimation = false; bool waitForDeathAnimation = false;
if (destroyableComponent) { if (destroyableComponent) {
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT; waitForDeathAnimation = !destroyableComponent->GetIsSmashable() && destroyableComponent->GetDeathBehavior() == 0 && killType != eKillType::SILENT;
} }
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction! // Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
@@ -1629,10 +1634,8 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>(); CDObjectSkillsTable* skillsTable = CDClientManager::GetTable<CDObjectSkillsTable>();
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); }); std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
for (CDObjectSkills skill : skills) { for (CDObjectSkills skill : skills) {
CDSkillBehaviorTable* skillBehTable = CDClientManager::GetTable<CDSkillBehaviorTable>();
auto* skillComponent = GetComponent<SkillComponent>(); auto* skillComponent = GetComponent<SkillComponent>();
if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID()); if (skillComponent) skillComponent->CastSkill(skill.skillID, GetObjectID(), GetObjectID(), skill.castOnType, NiQuaternion(0, 0, 0, 0));
auto* missionComponent = GetComponent<MissionComponent>(); auto* missionComponent = GetComponent<MissionComponent>();
@@ -1837,6 +1840,12 @@ const NiPoint3& Entity::GetPosition() const {
return vehicel->GetPosition(); return vehicel->GetPosition();
} }
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
return rigidBodyPhantomPhysicsComponent->GetPosition();
}
return NiPoint3Constant::ZERO; return NiPoint3Constant::ZERO;
} }
@@ -1865,6 +1874,12 @@ const NiQuaternion& Entity::GetRotation() const {
return vehicel->GetRotation(); return vehicel->GetRotation();
} }
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
return rigidBodyPhantomPhysicsComponent->GetRotation();
}
return NiQuaternionConstant::IDENTITY; return NiQuaternionConstant::IDENTITY;
} }
@@ -1893,6 +1908,12 @@ void Entity::SetPosition(const NiPoint3& position) {
vehicel->SetPosition(position); vehicel->SetPosition(position);
} }
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
rigidBodyPhantomPhysicsComponent->SetPosition(position);
}
Game::entityManager->SerializeEntity(this); Game::entityManager->SerializeEntity(this);
} }
@@ -1921,6 +1942,12 @@ void Entity::SetRotation(const NiQuaternion& rotation) {
vehicel->SetRotation(rotation); vehicel->SetRotation(rotation);
} }
auto* rigidBodyPhantomPhysicsComponent = GetComponent<RigidbodyPhantomPhysicsComponent>();
if (rigidBodyPhantomPhysicsComponent != nullptr) {
rigidBodyPhantomPhysicsComponent->SetRotation(rotation);
}
Game::entityManager->SerializeEntity(this); Game::entityManager->SerializeEntity(this);
} }

View File

@@ -174,7 +174,7 @@ public:
void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType); void WriteBaseReplicaData(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType); void WriteComponents(RakNet::BitStream& outBitStream, eReplicaPacketType packetType);
void UpdateXMLDoc(tinyxml2::XMLDocument* doc); void UpdateXMLDoc(tinyxml2::XMLDocument& doc);
void Update(float deltaTime); void Update(float deltaTime);
// Events // Events

View File

@@ -418,10 +418,16 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
} }
void EntityManager::SerializeEntity(Entity* entity) { void EntityManager::SerializeEntity(Entity* entity) {
if (!entity || entity->GetNetworkId() == 0) return; if (!entity) return;
EntityManager::SerializeEntity(*entity);
}
if (std::find(m_EntitiesToSerialize.begin(), m_EntitiesToSerialize.end(), entity->GetObjectID()) == m_EntitiesToSerialize.end()) { void EntityManager::SerializeEntity(const Entity& entity) {
m_EntitiesToSerialize.push_back(entity->GetObjectID()); if (entity.GetNetworkId() == 0) return;
if (std::find(m_EntitiesToSerialize.cbegin(), m_EntitiesToSerialize.cend(), entity.GetObjectID()) == m_EntitiesToSerialize.cend()) {
m_EntitiesToSerialize.push_back(entity.GetObjectID());
} }
} }

View File

@@ -45,6 +45,7 @@ public:
void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false); void ConstructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS, bool skipChecks = false);
void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS); void DestructEntity(Entity* entity, const SystemAddress& sysAddr = UNASSIGNED_SYSTEM_ADDRESS);
void SerializeEntity(Entity* entity); void SerializeEntity(Entity* entity);
void SerializeEntity(const Entity& entity);
void ConstructAllEntities(const SystemAddress& sysAddr); void ConstructAllEntities(const SystemAddress& sysAddr);
void DestructAllEntities(const SystemAddress& sysAddr); void DestructAllEntities(const SystemAddress& sysAddr);

View File

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

View File

@@ -26,7 +26,7 @@
#include "eCharacterCreationResponse.h" #include "eCharacterCreationResponse.h"
#include "eRenameResponse.h" #include "eRenameResponse.h"
#include "eConnectionType.h" #include "eConnectionType.h"
#include "eChatInternalMessageType.h" #include "eChatMessageType.h"
#include "BitStreamUtils.h" #include "BitStreamUtils.h"
#include "CheatDetection.h" #include "CheatDetection.h"
@@ -83,7 +83,7 @@ void UserManager::Initialize() {
auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt"); auto chatListStream = Game::assetManager->GetFile("chatplus_en_us.txt");
if (!chatListStream) { if (!chatListStream) {
LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str()); LOG("Failed to load %s", (Game::assetManager->GetResPath() / "chatplus_en_us.txt").string().c_str());
throw std::runtime_error("Aborting initialization due to missing chat whitelist file."); throw std::runtime_error("Aborting initialization due to missing chat allowlist file.");
} }
while (std::getline(chatListStream, line, '\n')) { while (std::getline(chatListStream, line, '\n')) {
@@ -422,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
Database::Get()->DeleteCharacter(charID); Database::Get()->DeleteCharacter(charID);
CBITSTREAM; CBITSTREAM;
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION); BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::UNEXPECTED_DISCONNECT);
bitStream.Write(objectID); bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false); Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
@@ -536,13 +536,13 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) { uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
try { try {
auto stmt = CDClientDatabase::CreatePreppedStmt( auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?" "select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ? AND icc.decal == ?"
); );
stmt.bind(1, "character create shirt"); stmt.bind(1, "character create shirt");
stmt.bind(2, static_cast<int>(shirtColor)); stmt.bind(2, static_cast<int>(shirtColor));
stmt.bind(3, static_cast<int>(shirtStyle)); stmt.bind(3, static_cast<int>(shirtStyle));
auto tableData = stmt.execQuery(); auto tableData = stmt.execQuery();
auto shirtLOT = tableData.getIntField(0, 4069); auto shirtLOT = tableData.getIntField("objectId", 4069);
tableData.finalize(); tableData.finalize();
return shirtLOT; return shirtLOT;
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
@@ -555,12 +555,12 @@ uint32_t FindCharShirtID(uint32_t shirtColor, uint32_t shirtStyle) {
uint32_t FindCharPantsID(uint32_t pantsColor) { uint32_t FindCharPantsID(uint32_t pantsColor) {
try { try {
auto stmt = CDClientDatabase::CreatePreppedStmt( auto stmt = CDClientDatabase::CreatePreppedStmt(
"select obj.id from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?" "select obj.id as objectId from Objects as obj JOIN (select * from ComponentsRegistry as cr JOIN ItemComponent as ic on ic.id = cr.component_id where cr.component_type == 11) as icc on icc.id = obj.id where lower(obj._internalNotes) == ? AND icc.color1 == ?"
); );
stmt.bind(1, "cc pants"); stmt.bind(1, "cc pants");
stmt.bind(2, static_cast<int>(pantsColor)); stmt.bind(2, static_cast<int>(pantsColor));
auto tableData = stmt.execQuery(); auto tableData = stmt.execQuery();
auto pantsLOT = tableData.getIntField(0, 2508); auto pantsLOT = tableData.getIntField("objectId", 2508);
tableData.finalize(); tableData.finalize();
return pantsLOT; return pantsLOT;
} catch (const std::exception& ex) { } catch (const std::exception& ex) {

View File

@@ -222,7 +222,7 @@ void BasicAttackBehavior::DoBehaviorCalculation(BehaviorContext* context, RakNet
if (healthDamageDealt >= 1) { if (healthDamageDealt >= 1) {
successState = eBasicAttackSuccessTypes::SUCCESS; successState = eBasicAttackSuccessTypes::SUCCESS;
} else if (armorDamageDealt >= 1) { } else if (armorDamageDealt >= 1) {
successState = this->m_OnFailArmor->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR; successState = this->m_OnFailArmor->m_templateId == BehaviorTemplate::EMPTY ? eBasicAttackSuccessTypes::FAILIMMUNE : eBasicAttackSuccessTypes::FAILARMOR;
} }
bitStream.Write(armorDamageDealt); bitStream.Write(armorDamageDealt);

View File

@@ -5,7 +5,7 @@
#include "CDActivitiesTable.h" #include "CDActivitiesTable.h"
#include "Game.h" #include "Game.h"
#include "Logger.h" #include "Logger.h"
#include "BehaviorTemplates.h" #include "BehaviorTemplate.h"
#include "BehaviorBranchContext.h" #include "BehaviorBranchContext.h"
#include <unordered_map> #include <unordered_map>
@@ -110,176 +110,176 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
Behavior* behavior = nullptr; Behavior* behavior = nullptr;
switch (templateId) { switch (templateId) {
case BehaviorTemplates::BEHAVIOR_EMPTY: break; case BehaviorTemplate::EMPTY: break;
case BehaviorTemplates::BEHAVIOR_BASIC_ATTACK: case BehaviorTemplate::BASIC_ATTACK:
behavior = new BasicAttackBehavior(behaviorId); behavior = new BasicAttackBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_TAC_ARC: case BehaviorTemplate::TAC_ARC:
behavior = new TacArcBehavior(behaviorId); behavior = new TacArcBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_AND: case BehaviorTemplate::AND:
behavior = new AndBehavior(behaviorId); behavior = new AndBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PROJECTILE_ATTACK: case BehaviorTemplate::PROJECTILE_ATTACK:
behavior = new ProjectileAttackBehavior(behaviorId); behavior = new ProjectileAttackBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_HEAL: case BehaviorTemplate::HEAL:
behavior = new HealBehavior(behaviorId); behavior = new HealBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_MOVEMENT_SWITCH: case BehaviorTemplate::MOVEMENT_SWITCH:
behavior = new MovementSwitchBehavior(behaviorId); behavior = new MovementSwitchBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_AREA_OF_EFFECT: case BehaviorTemplate::AREA_OF_EFFECT:
behavior = new AreaOfEffectBehavior(behaviorId); behavior = new AreaOfEffectBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PLAY_EFFECT: case BehaviorTemplate::PLAY_EFFECT:
behavior = new PlayEffectBehavior(behaviorId); behavior = new PlayEffectBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_IMMUNITY: case BehaviorTemplate::IMMUNITY:
behavior = new ImmunityBehavior(behaviorId); behavior = new ImmunityBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_BUFF: break; case BehaviorTemplate::DAMAGE_BUFF: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_ABSORBTION: case BehaviorTemplate::DAMAGE_ABSORBTION:
behavior = new DamageAbsorptionBehavior(behaviorId); behavior = new DamageAbsorptionBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_OVER_TIME: case BehaviorTemplate::OVER_TIME:
behavior = new OverTimeBehavior(behaviorId); behavior = new OverTimeBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_IMAGINATION: case BehaviorTemplate::IMAGINATION:
behavior = new ImaginationBehavior(behaviorId); behavior = new ImaginationBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_TARGET_CASTER: case BehaviorTemplate::TARGET_CASTER:
behavior = new TargetCasterBehavior(behaviorId); behavior = new TargetCasterBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_STUN: case BehaviorTemplate::STUN:
behavior = new StunBehavior(behaviorId); behavior = new StunBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_DURATION: case BehaviorTemplate::DURATION:
behavior = new DurationBehavior(behaviorId); behavior = new DurationBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_KNOCKBACK: case BehaviorTemplate::KNOCKBACK:
behavior = new KnockbackBehavior(behaviorId); behavior = new KnockbackBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_ATTACK_DELAY: case BehaviorTemplate::ATTACK_DELAY:
behavior = new AttackDelayBehavior(behaviorId); behavior = new AttackDelayBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_CAR_BOOST: case BehaviorTemplate::CAR_BOOST:
behavior = new CarBoostBehavior(behaviorId); behavior = new CarBoostBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_FALL_SPEED: case BehaviorTemplate::FALL_SPEED:
behavior = new FallSpeedBehavior(behaviorId); behavior = new FallSpeedBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SHIELD: break; case BehaviorTemplate::SHIELD: break;
case BehaviorTemplates::BEHAVIOR_REPAIR_ARMOR: case BehaviorTemplate::REPAIR_ARMOR:
behavior = new RepairBehavior(behaviorId); behavior = new RepairBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SPEED: case BehaviorTemplate::SPEED:
behavior = new SpeedBehavior(behaviorId); behavior = new SpeedBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION: case BehaviorTemplate::DARK_INSPIRATION:
behavior = new DarkInspirationBehavior(behaviorId); behavior = new DarkInspirationBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF: case BehaviorTemplate::LOOT_BUFF:
behavior = new LootBuffBehavior(behaviorId); behavior = new LootBuffBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_VENTURE_VISION: case BehaviorTemplate::VENTURE_VISION:
behavior = new VentureVisionBehavior(behaviorId); behavior = new VentureVisionBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SPAWN_OBJECT: case BehaviorTemplate::SPAWN_OBJECT:
behavior = new SpawnBehavior(behaviorId); behavior = new SpawnBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_LAY_BRICK: break; case BehaviorTemplate::LAY_BRICK: break;
case BehaviorTemplates::BEHAVIOR_SWITCH: case BehaviorTemplate::SWITCH:
behavior = new SwitchBehavior(behaviorId); behavior = new SwitchBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_BUFF: case BehaviorTemplate::BUFF:
behavior = new BuffBehavior(behaviorId); behavior = new BuffBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_JETPACK: case BehaviorTemplate::JETPACK:
behavior = new JetPackBehavior(behaviorId); behavior = new JetPackBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SKILL_EVENT: case BehaviorTemplate::SKILL_EVENT:
behavior = new SkillEventBehavior(behaviorId); behavior = new SkillEventBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_CONSUME_ITEM: case BehaviorTemplate::CONSUME_ITEM:
behavior = new ConsumeItemBehavior(behaviorId); behavior = new ConsumeItemBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SKILL_CAST_FAILED: case BehaviorTemplate::SKILL_CAST_FAILED:
behavior = new SkillCastFailedBehavior(behaviorId); behavior = new SkillCastFailedBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_IMITATION_SKUNK_STINK: break; case BehaviorTemplate::IMITATION_SKUNK_STINK: break;
case BehaviorTemplates::BEHAVIOR_CHANGE_IDLE_FLAGS: case BehaviorTemplate::CHANGE_IDLE_FLAGS:
behavior = new ChangeIdleFlagsBehavior(behaviorId); behavior = new ChangeIdleFlagsBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_APPLY_BUFF: case BehaviorTemplate::APPLY_BUFF:
behavior = new ApplyBuffBehavior(behaviorId); behavior = new ApplyBuffBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_CHAIN: case BehaviorTemplate::CHAIN:
behavior = new ChainBehavior(behaviorId); behavior = new ChainBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_CHANGE_ORIENTATION: case BehaviorTemplate::CHANGE_ORIENTATION:
behavior = new ChangeOrientationBehavior(behaviorId); behavior = new ChangeOrientationBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_FORCE_MOVEMENT: case BehaviorTemplate::FORCE_MOVEMENT:
behavior = new ForceMovementBehavior(behaviorId); behavior = new ForceMovementBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_INTERRUPT: case BehaviorTemplate::INTERRUPT:
behavior = new InterruptBehavior(behaviorId); behavior = new InterruptBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_ALTER_COOLDOWN: break; case BehaviorTemplate::ALTER_COOLDOWN: break;
case BehaviorTemplates::BEHAVIOR_CHARGE_UP: case BehaviorTemplate::CHARGE_UP:
behavior = new ChargeUpBehavior(behaviorId); behavior = new ChargeUpBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SWITCH_MULTIPLE: case BehaviorTemplate::SWITCH_MULTIPLE:
behavior = new SwitchMultipleBehavior(behaviorId); behavior = new SwitchMultipleBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_START: case BehaviorTemplate::START:
behavior = new StartBehavior(behaviorId); behavior = new StartBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_END: case BehaviorTemplate::END:
behavior = new EndBehavior(behaviorId); behavior = new EndBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_ALTER_CHAIN_DELAY: break; case BehaviorTemplate::ALTER_CHAIN_DELAY: break;
case BehaviorTemplates::BEHAVIOR_CAMERA: break; case BehaviorTemplate::CAMERA: break;
case BehaviorTemplates::BEHAVIOR_REMOVE_BUFF: case BehaviorTemplate::REMOVE_BUFF:
behavior = new RemoveBuffBehavior(behaviorId); behavior = new RemoveBuffBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_GRAB: break; case BehaviorTemplate::GRAB: break;
case BehaviorTemplates::BEHAVIOR_MODULAR_BUILD: break; case BehaviorTemplate::MODULAR_BUILD: break;
case BehaviorTemplates::BEHAVIOR_NPC_COMBAT_SKILL: case BehaviorTemplate::NPC_COMBAT_SKILL:
behavior = new NpcCombatSkillBehavior(behaviorId); behavior = new NpcCombatSkillBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_BLOCK: case BehaviorTemplate::BLOCK:
behavior = new BlockBehavior(behaviorId); behavior = new BlockBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_VERIFY: case BehaviorTemplate::VERIFY:
behavior = new VerifyBehavior(behaviorId); behavior = new VerifyBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_TAUNT: case BehaviorTemplate::TAUNT:
behavior = new TauntBehavior(behaviorId); behavior = new TauntBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_AIR_MOVEMENT: case BehaviorTemplate::AIR_MOVEMENT:
behavior = new AirMovementBehavior(behaviorId); behavior = new AirMovementBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_SPAWN_QUICKBUILD: case BehaviorTemplate::SPAWN_QUICKBUILD:
behavior = new SpawnBehavior(behaviorId); behavior = new SpawnBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PULL_TO_POINT: case BehaviorTemplate::PULL_TO_POINT:
behavior = new PullToPointBehavior(behaviorId); behavior = new PullToPointBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_ROTATE: break; case BehaviorTemplate::PROPERTY_ROTATE: break;
case BehaviorTemplates::BEHAVIOR_DAMAGE_REDUCTION: case BehaviorTemplate::DAMAGE_REDUCTION:
behavior = new DamageReductionBehavior(behaviorId); behavior = new DamageReductionBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_TELEPORT: case BehaviorTemplate::PROPERTY_TELEPORT:
behavior = new PropertyTeleportBehavior(behaviorId); behavior = new PropertyTeleportBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_PROPERTY_CLEAR_TARGET: case BehaviorTemplate::PROPERTY_CLEAR_TARGET:
behavior = new ClearTargetBehavior(behaviorId); behavior = new ClearTargetBehavior(behaviorId);
break; break;
case BehaviorTemplates::BEHAVIOR_TAKE_PICTURE: break; case BehaviorTemplate::TAKE_PICTURE: break;
case BehaviorTemplates::BEHAVIOR_MOUNT: break; case BehaviorTemplate::MOUNT: break;
case BehaviorTemplates::BEHAVIOR_SKILL_SET: break; case BehaviorTemplate::SKILL_SET: break;
default: default:
//LOG("Failed to load behavior with invalid template id (%i)!", templateId); //LOG("Failed to load behavior with invalid template id (%i)!", templateId);
break; break;
@@ -296,19 +296,19 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
return behavior; return behavior;
} }
BehaviorTemplates Behavior::GetBehaviorTemplate(const uint32_t behaviorId) { BehaviorTemplate Behavior::GetBehaviorTemplate(const uint32_t behaviorId) {
auto behaviorTemplateTable = CDClientManager::GetTable<CDBehaviorTemplateTable>(); auto behaviorTemplateTable = CDClientManager::GetTable<CDBehaviorTemplateTable>();
BehaviorTemplates templateID = BehaviorTemplates::BEHAVIOR_EMPTY; BehaviorTemplate templateID = BehaviorTemplate::EMPTY;
// Find behavior template by its behavior id. Default to 0. // Find behavior template by its behavior id. Default to 0.
if (behaviorTemplateTable) { if (behaviorTemplateTable) {
auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId); auto templateEntry = behaviorTemplateTable->GetByBehaviorID(behaviorId);
if (templateEntry.behaviorID == behaviorId) { if (templateEntry.behaviorID == behaviorId) {
templateID = static_cast<BehaviorTemplates>(templateEntry.templateID); templateID = static_cast<BehaviorTemplate>(templateEntry.templateID);
} }
} }
if (templateID == BehaviorTemplates::BEHAVIOR_EMPTY && behaviorId != 0) { if (templateID == BehaviorTemplate::EMPTY && behaviorId != 0) {
LOG("Failed to load behavior template with id (%i)!", behaviorId); LOG("Failed to load behavior template with id (%i)!", behaviorId);
} }
@@ -335,26 +335,22 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
const auto typeString = GeneralUtils::UTF16ToWTF8(type); const auto typeString = GeneralUtils::UTF16ToWTF8(type);
if (m_effectNames == nullptr) { const auto itr = m_effectNames.find(typeString);
m_effectNames = new std::unordered_map<std::string, std::string>();
} else {
const auto pair = m_effectNames->find(typeString);
if (type.empty()) { if (type.empty()) {
type = GeneralUtils::ASCIIToUTF16(*m_effectType); type = GeneralUtils::ASCIIToUTF16(m_effectType);
} }
if (pair != m_effectNames->end()) { if (itr != m_effectNames.end()) {
if (renderComponent == nullptr) { if (renderComponent == nullptr) {
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, pair->second, secondary, 1, 1, true); GameMessages::SendPlayFXEffect(targetEntity, effectId, type, itr->second, secondary, 1, 1, true);
return;
}
renderComponent->PlayEffect(effectId, type, pair->second, secondary);
return; return;
} }
renderComponent->PlayEffect(effectId, type, itr->second, secondary);
return;
} }
// The SQlite result object becomes invalid if the query object leaves scope. // The SQlite result object becomes invalid if the query object leaves scope.
@@ -381,19 +377,19 @@ void Behavior::PlayFx(std::u16string type, const LWOOBJID target, const LWOOBJID
return; return;
} }
const auto name = std::string(result.getStringField(0)); const auto name = std::string(result.getStringField("effectName"));
if (type.empty()) { if (type.empty()) {
const auto typeResult = result.getStringField(1); const auto typeResult = result.getStringField("effectType");
type = GeneralUtils::ASCIIToUTF16(typeResult); type = GeneralUtils::ASCIIToUTF16(typeResult);
m_effectType = new std::string(typeResult); m_effectType = typeResult;
} }
result.finalize(); result.finalize();
m_effectNames->insert_or_assign(typeString, name); m_effectNames.insert_or_assign(typeString, name);
if (renderComponent == nullptr) { if (renderComponent == nullptr) {
GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true); GameMessages::SendPlayFXEffect(targetEntity, effectId, type, name, secondary, 1, 1, true);
@@ -423,8 +419,7 @@ Behavior::Behavior(const uint32_t behaviorId) {
if (behaviorId == 0) { if (behaviorId == 0) {
this->m_effectId = 0; this->m_effectId = 0;
this->m_effectHandle = nullptr; this->m_templateId = BehaviorTemplate::EMPTY;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
} }
// Make sure we do not proceed if we are trying to load an invalid behavior // Make sure we do not proceed if we are trying to load an invalid behavior
@@ -432,17 +427,16 @@ Behavior::Behavior(const uint32_t behaviorId) {
LOG("Failed to load behavior with id (%i)!", behaviorId); LOG("Failed to load behavior with id (%i)!", behaviorId);
this->m_effectId = 0; this->m_effectId = 0;
this->m_effectHandle = nullptr; this->m_templateId = BehaviorTemplate::EMPTY;
this->m_templateId = BehaviorTemplates::BEHAVIOR_EMPTY;
return; return;
} }
this->m_templateId = static_cast<BehaviorTemplates>(templateInDatabase.templateID); this->m_templateId = static_cast<BehaviorTemplate>(templateInDatabase.templateID);
this->m_effectId = templateInDatabase.effectID; this->m_effectId = templateInDatabase.effectID;
this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr; this->m_effectHandle = *templateInDatabase.effectHandle;
} }
@@ -507,9 +501,3 @@ void Behavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream,
void Behavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void Behavior::SyncCalculation(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
} }
Behavior::~Behavior() {
delete m_effectNames;
delete m_effectType;
delete m_effectHandle;
}

View File

@@ -6,7 +6,7 @@
#include <unordered_map> #include <unordered_map>
#include "BitStream.h" #include "BitStream.h"
#include "BehaviorTemplates.h" #include "BehaviorTemplate.h"
#include "dCommonVars.h" #include "dCommonVars.h"
struct BehaviorContext; struct BehaviorContext;
@@ -26,7 +26,7 @@ public:
static Behavior* CreateBehavior(uint32_t behaviorId); static Behavior* CreateBehavior(uint32_t behaviorId);
static BehaviorTemplates GetBehaviorTemplate(uint32_t behaviorId); static BehaviorTemplate GetBehaviorTemplate(uint32_t behaviorId);
/* /*
* Utilities * Utilities
@@ -39,11 +39,11 @@ public:
*/ */
uint32_t m_behaviorId; uint32_t m_behaviorId;
BehaviorTemplates m_templateId; BehaviorTemplate m_templateId;
uint32_t m_effectId; uint32_t m_effectId;
std::string* m_effectHandle = nullptr; std::string m_effectHandle;
std::unordered_map<std::string, std::string>* m_effectNames = nullptr; std::unordered_map<std::string, std::string> m_effectNames;
std::string* m_effectType = nullptr; std::string m_effectType;
/* /*
* Behavior parameters * Behavior parameters
@@ -88,5 +88,11 @@ public:
*/ */
explicit Behavior(uint32_t behaviorId); explicit Behavior(uint32_t behaviorId);
virtual ~Behavior(); virtual ~Behavior() = default;
Behavior(const Behavior& other) = default;
Behavior(Behavior&& other) = default;
Behavior& operator=(const Behavior& other) = default;
Behavior& operator=(Behavior&& other) = default;
}; };

View File

@@ -105,7 +105,7 @@ void BehaviorContext::ExecuteUpdates() {
this->scheduledUpdates.clear(); this->scheduledUpdates.clear();
} }
void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) { bool BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bitStream) {
BehaviorSyncEntry entry; BehaviorSyncEntry entry;
auto found = false; auto found = false;
@@ -128,7 +128,7 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (!found) { if (!found) {
LOG("Failed to find behavior sync entry with sync id (%i)!", syncId); LOG("Failed to find behavior sync entry with sync id (%i)!", syncId);
return; return false;
} }
auto* behavior = entry.behavior; auto* behavior = entry.behavior;
@@ -137,10 +137,11 @@ void BehaviorContext::SyncBehavior(const uint32_t syncId, RakNet::BitStream& bit
if (behavior == nullptr) { if (behavior == nullptr) {
LOG("Invalid behavior for sync id (%i)!", syncId); LOG("Invalid behavior for sync id (%i)!", syncId);
return; return false;
} }
behavior->Sync(this, bitStream, branch); behavior->Sync(this, bitStream, branch);
return true;
} }
@@ -224,6 +225,16 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
for (auto i = 0u; i < this->syncEntries.size(); ++i) { for (auto i = 0u; i < this->syncEntries.size(); ++i) {
auto entry = this->syncEntries.at(i); auto entry = this->syncEntries.at(i);
if (entry.behavior->m_templateId == BehaviorTemplate::ATTACK_DELAY) {
auto* self = Game::entityManager->GetEntity(originator);
if (self) {
auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if (destroyableComponent && destroyableComponent->GetHealth() <= 0) {
continue;
}
}
}
if (entry.time > 0) { if (entry.time > 0) {
entry.time -= deltaTime; entry.time -= deltaTime;
@@ -333,7 +344,7 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
} }
// handle targeting the caster // handle targeting the caster
if (candidate == caster){ if (candidate == caster) {
// if we aren't targeting self, erase, otherise increment and continue // if we aren't targeting self, erase, otherise increment and continue
if (!targetSelf) index = targets.erase(index); if (!targetSelf) index = targets.erase(index);
else index++; else index++;
@@ -356,24 +367,24 @@ void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_
} }
// if they are dead, then earse and continue // if they are dead, then earse and continue
if (candidateDestroyableComponent->GetIsDead()){ if (candidateDestroyableComponent->GetIsDead()) {
index = targets.erase(index); index = targets.erase(index);
continue; continue;
} }
// if their faction is explicitly included, increment and continue // if their faction is explicitly included, increment and continue
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs(); auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
if (CheckFactionList(includeFactionList, candidateFactions)){ if (CheckFactionList(includeFactionList, candidateFactions)) {
index++; index++;
continue; continue;
} }
// check if they are a team member // check if they are a team member
if (targetTeam){ if (targetTeam) {
auto* team = TeamManager::Instance()->GetTeam(this->caster); auto* team = TeamManager::Instance()->GetTeam(this->caster);
if (team){ if (team) {
// if we find a team member keep it and continue to skip enemy checks // if we find a team member keep it and continue to skip enemy checks
if(std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()){ if (std::find(team->members.begin(), team->members.end(), candidate->GetObjectID()) != team->members.end()) {
index++; index++;
continue; continue;
} }
@@ -419,8 +430,8 @@ bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
// returns true if any of the object factions are in the faction list // returns true if any of the object factions are in the faction list
bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const { bool BehaviorContext::CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const {
if (factionList.empty() || objectsFactions.empty()) return false; if (factionList.empty() || objectsFactions.empty()) return false;
for (auto faction : factionList){ for (auto faction : factionList) {
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true; if (std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
} }
return false; return false;
} }

View File

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

View File

@@ -0,0 +1,70 @@
#pragma once
enum class BehaviorTemplate : unsigned int {
EMPTY, // Not a real behavior, indicates invalid behaviors
BASIC_ATTACK,
TAC_ARC,
AND,
PROJECTILE_ATTACK,
HEAL,
MOVEMENT_SWITCH,
AREA_OF_EFFECT,
PLAY_EFFECT,
IMMUNITY,
DAMAGE_BUFF,
DAMAGE_ABSORBTION,
OVER_TIME,
IMAGINATION,
TARGET_CASTER,
STUN,
DURATION,
KNOCKBACK,
ATTACK_DELAY,
CAR_BOOST,
FALL_SPEED,
SHIELD,
REPAIR_ARMOR,
SPEED,
DARK_INSPIRATION,
LOOT_BUFF,
VENTURE_VISION,
SPAWN_OBJECT,
LAY_BRICK,
SWITCH,
BUFF,
JETPACK,
SKILL_EVENT,
CONSUME_ITEM,
SKILL_CAST_FAILED,
IMITATION_SKUNK_STINK,
CHANGE_IDLE_FLAGS,
APPLY_BUFF,
CHAIN,
CHANGE_ORIENTATION,
FORCE_MOVEMENT,
INTERRUPT,
ALTER_COOLDOWN,
CHARGE_UP,
SWITCH_MULTIPLE,
START,
END,
ALTER_CHAIN_DELAY,
CAMERA,
REMOVE_BUFF,
GRAB,
MODULAR_BUILD,
NPC_COMBAT_SKILL,
BLOCK,
VERIFY,
TAUNT,
AIR_MOVEMENT,
SPAWN_QUICKBUILD,
PULL_TO_POINT,
PROPERTY_ROTATE,
DAMAGE_REDUCTION,
PROPERTY_TELEPORT,
PROPERTY_CLEAR_TARGET,
TAKE_PICTURE,
MOUNT,
SKILL_SET
};

View File

@@ -1 +0,0 @@
#include "BehaviorTemplates.h"

View File

@@ -1,70 +0,0 @@
#pragma once
enum class BehaviorTemplates : unsigned int {
BEHAVIOR_EMPTY, // Not a real behavior, indicates invalid behaviors
BEHAVIOR_BASIC_ATTACK,
BEHAVIOR_TAC_ARC,
BEHAVIOR_AND,
BEHAVIOR_PROJECTILE_ATTACK,
BEHAVIOR_HEAL,
BEHAVIOR_MOVEMENT_SWITCH,
BEHAVIOR_AREA_OF_EFFECT,
BEHAVIOR_PLAY_EFFECT,
BEHAVIOR_IMMUNITY,
BEHAVIOR_DAMAGE_BUFF,
BEHAVIOR_DAMAGE_ABSORBTION,
BEHAVIOR_OVER_TIME,
BEHAVIOR_IMAGINATION,
BEHAVIOR_TARGET_CASTER,
BEHAVIOR_STUN,
BEHAVIOR_DURATION,
BEHAVIOR_KNOCKBACK,
BEHAVIOR_ATTACK_DELAY,
BEHAVIOR_CAR_BOOST,
BEHAVIOR_FALL_SPEED,
BEHAVIOR_SHIELD,
BEHAVIOR_REPAIR_ARMOR,
BEHAVIOR_SPEED,
BEHAVIOR_DARK_INSPIRATION,
BEHAVIOR_LOOT_BUFF,
BEHAVIOR_VENTURE_VISION,
BEHAVIOR_SPAWN_OBJECT,
BEHAVIOR_LAY_BRICK,
BEHAVIOR_SWITCH,
BEHAVIOR_BUFF,
BEHAVIOR_JETPACK,
BEHAVIOR_SKILL_EVENT,
BEHAVIOR_CONSUME_ITEM,
BEHAVIOR_SKILL_CAST_FAILED,
BEHAVIOR_IMITATION_SKUNK_STINK,
BEHAVIOR_CHANGE_IDLE_FLAGS,
BEHAVIOR_APPLY_BUFF,
BEHAVIOR_CHAIN,
BEHAVIOR_CHANGE_ORIENTATION,
BEHAVIOR_FORCE_MOVEMENT,
BEHAVIOR_INTERRUPT,
BEHAVIOR_ALTER_COOLDOWN,
BEHAVIOR_CHARGE_UP,
BEHAVIOR_SWITCH_MULTIPLE,
BEHAVIOR_START,
BEHAVIOR_END,
BEHAVIOR_ALTER_CHAIN_DELAY,
BEHAVIOR_CAMERA,
BEHAVIOR_REMOVE_BUFF,
BEHAVIOR_GRAB,
BEHAVIOR_MODULAR_BUILD,
BEHAVIOR_NPC_COMBAT_SKILL,
BEHAVIOR_BLOCK,
BEHAVIOR_VERIFY,
BEHAVIOR_TAUNT,
BEHAVIOR_AIR_MOVEMENT,
BEHAVIOR_SPAWN_QUICKBUILD,
BEHAVIOR_PULL_TO_POINT,
BEHAVIOR_PROPERTY_ROTATE,
BEHAVIOR_DAMAGE_REDUCTION,
BEHAVIOR_PROPERTY_TELEPORT,
BEHAVIOR_PROPERTY_CLEAR_TARGET,
BEHAVIOR_TAKE_PICTURE,
BEHAVIOR_MOUNT,
BEHAVIOR_SKILL_SET
};

View File

@@ -7,7 +7,6 @@ set(DGAME_DBEHAVIORS_SOURCES "AirMovementBehavior.cpp"
"Behavior.cpp" "Behavior.cpp"
"BehaviorBranchContext.cpp" "BehaviorBranchContext.cpp"
"BehaviorContext.cpp" "BehaviorContext.cpp"
"BehaviorTemplates.cpp"
"BlockBehavior.cpp" "BlockBehavior.cpp"
"BuffBehavior.cpp" "BuffBehavior.cpp"
"CarBoostBehavior.cpp" "CarBoostBehavior.cpp"

View File

@@ -7,7 +7,7 @@
#include "Logger.h" #include "Logger.h"
void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { void ForceMovementBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) {
if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) {
return; return;
} }
@@ -38,7 +38,7 @@ void ForceMovementBehavior::Sync(BehaviorContext* context, RakNet::BitStream& bi
} }
void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void ForceMovementBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
if (this->m_hitAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { if (this->m_hitAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitEnemyAction->m_templateId == BehaviorTemplate::EMPTY && this->m_hitFactionAction->m_templateId == BehaviorTemplate::EMPTY) {
return; return;
} }

View File

@@ -8,36 +8,50 @@
void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
if (branch.target != context->originator) { LWOOBJID usedTarget = m_target ? branch.target : context->originator;
bool unknown = false;
if (!bitStream.Read(unknown)) { if (usedTarget != context->originator) {
LOG("Unable to read unknown1 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); bool isTargetImmuneStuns = false;
if (!bitStream.Read(isTargetImmuneStuns)) {
LOG("Unable to read isTargetImmune from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return; return;
}; };
if (unknown) return; if (isTargetImmuneStuns) return;
} }
if (!this->m_interruptBlock) { if (!this->m_interruptBlock) {
bool unknown = false; bool isBlockingInterrupts = false;
if (!bitStream.Read(isBlockingInterrupts)) {
if (!bitStream.Read(unknown)) { LOG("Unable to read isBlockingInterrupts from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
LOG("Unable to read unknown2 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return; return;
}; };
if (unknown) return; if (isBlockingInterrupts) return;
} }
if (this->m_target) // Guess... bool hasInterruptedStatusEffects = false;
{ if (!bitStream.Read(hasInterruptedStatusEffects)) {
bool unknown = false; LOG("Unable to read hasInterruptedStatusEffects from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return;
};
if (!bitStream.Read(unknown)) { if (hasInterruptedStatusEffects) {
LOG("Unable to read unknown3 from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); bool hasMoreInterruptedStatusEffects = false;
return; int32_t loopLimit = 0;
}; while (bitStream.Read(hasMoreInterruptedStatusEffects) && hasMoreInterruptedStatusEffects) {
int32_t statusEffectID = 0;
bitStream.Read(statusEffectID);
// nothing happens with this data yes. I have no idea why or what it was used for, but the client literally just reads it and does nothing with it.
// 0x004faca4 for a reference. it also has a hard loop limit of 100 soo,
loopLimit++;
if (loopLimit > 100) {
// if this is hit you have a problem
LOG("Loop limit reached for interrupted status effects, aborting Handle due to bad bitstream! %i", bitStream.GetNumberOfUnreadBits());
break;
}
LOG_DEBUG("Interrupted status effect ID: %i", statusEffectID);
}
} }
if (branch.target == context->originator) return; if (branch.target == context->originator) return;
@@ -55,7 +69,8 @@ void InterruptBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitS
void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void InterruptBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
if (branch.target != context->originator) { LWOOBJID usedTarget = m_target ? branch.target : context->originator;
if (usedTarget != context->originator) {
bitStream.Write(false); bitStream.Write(false);
} }

View File

@@ -6,13 +6,13 @@
void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) {
uint32_t movementType{}; uint32_t movementType{};
if (!bitStream.Read(movementType)) { if (!bitStream.Read(movementType)) {
if (this->m_groundAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && if (this->m_groundAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_jumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_jumpAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_fallingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_fallingAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_doubleJumpAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_doubleJumpAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_airAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_airAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_jetpackAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY && this->m_jetpackAction->m_templateId == BehaviorTemplate::EMPTY &&
this->m_movingAction->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { this->m_movingAction->m_templateId == BehaviorTemplate::EMPTY) {
return; return;
} }
LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); LOG("Unable to read movementType from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
@@ -47,7 +47,7 @@ void MovementSwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream&
Behavior* MovementSwitchBehavior::LoadMovementType(std::string movementType) { Behavior* MovementSwitchBehavior::LoadMovementType(std::string movementType) {
float actionValue = GetFloat(movementType, -1.0f); float actionValue = GetFloat(movementType, -1.0f);
auto loadedBehavior = GetAction(actionValue != -1.0f ? actionValue : 0.0f); auto loadedBehavior = GetAction(actionValue != -1.0f ? actionValue : 0.0f);
if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplates::BEHAVIOR_EMPTY) { if (actionValue == -1.0f && loadedBehavior->m_templateId == BehaviorTemplate::EMPTY) {
loadedBehavior = this->m_groundAction; loadedBehavior = this->m_groundAction;
} }
return loadedBehavior; return loadedBehavior;

View File

@@ -9,17 +9,16 @@ void SkillEventBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bit
auto* target = Game::entityManager->GetEntity(branch.target); auto* target = Game::entityManager->GetEntity(branch.target);
auto* caster = Game::entityManager->GetEntity(context->originator); auto* caster = Game::entityManager->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) {
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle); target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle);
} }
} }
void void SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
SkillEventBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
auto* target = Game::entityManager->GetEntity(branch.target); auto* target = Game::entityManager->GetEntity(branch.target);
auto* caster = Game::entityManager->GetEntity(context->originator); auto* caster = Game::entityManager->GetEntity(context->originator);
if (caster != nullptr && target != nullptr && this->m_effectHandle != nullptr && !this->m_effectHandle->empty()) { if (caster != nullptr && target != nullptr && !this->m_effectHandle.empty()) {
target->GetScript()->OnSkillEventFired(target, caster, *this->m_effectHandle); target->GetScript()->OnSkillEventFired(target, caster, this->m_effectHandle);
} }
} }

View File

@@ -7,9 +7,9 @@
#include "BuffComponent.h" #include "BuffComponent.h"
void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) { void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStream, const BehaviorBranchContext branch) {
auto state = true; bool state = true;
if (this->m_imagination > 0 || !this->m_isEnemyFaction) { if (m_imagination > 0 || m_targetHasBuff > 0 || m_Distance > -1.0f) {
if (!bitStream.Read(state)) { if (!bitStream.Read(state)) {
LOG("Unable to read state from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits()); LOG("Unable to read state from bitStream, aborting Handle! %i", bitStream.GetNumberOfUnreadBits());
return; return;
@@ -18,49 +18,59 @@ void SwitchBehavior::Handle(BehaviorContext* context, RakNet::BitStream& bitStre
auto* entity = Game::entityManager->GetEntity(context->originator); auto* entity = Game::entityManager->GetEntity(context->originator);
if (entity == nullptr) { if (!entity) return;
return;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>(); auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) { if (destroyableComponent) {
return; if (m_isEnemyFaction) {
auto* target = Game::entityManager->GetEntity(branch.target);
if (target) state = destroyableComponent->IsEnemy(target);
}
LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination());
} }
LOG_DEBUG("[%i] State: (%d), imagination: (%i) / (%f)", entity->GetLOT(), state, destroyableComponent->GetImagination(), destroyableComponent->GetMaxImagination()); auto* behaviorToCall = state ? m_actionTrue : m_actionFalse;
behaviorToCall->Handle(context, bitStream, branch);
if (state) {
this->m_actionTrue->Handle(context, bitStream, branch);
} else {
this->m_actionFalse->Handle(context, bitStream, branch);
}
} }
void SwitchBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) { void SwitchBehavior::Calculate(BehaviorContext* context, RakNet::BitStream& bitStream, BehaviorBranchContext branch) {
auto state = true; bool state = true;
if (m_imagination > 0 || m_targetHasBuff > 0 || m_Distance > -1.0f) {
if (this->m_imagination > 0 || !this->m_isEnemyFaction) {
auto* entity = Game::entityManager->GetEntity(branch.target); auto* entity = Game::entityManager->GetEntity(branch.target);
state = entity != nullptr; state = entity != nullptr;
if (state && m_targetHasBuff != 0) { if (state) {
auto* buffComponent = entity->GetComponent<BuffComponent>(); if (m_targetHasBuff != 0) {
auto* buffComponent = entity->GetComponent<BuffComponent>();
if (buffComponent != nullptr && !buffComponent->HasBuff(m_targetHasBuff)) { if (buffComponent != nullptr && !buffComponent->HasBuff(m_targetHasBuff)) {
state = false; state = false;
}
} else if (m_imagination > 0) {
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent && destroyableComponent->GetImagination() < m_imagination) {
state = false;
}
} else if (m_Distance > -1.0f) {
auto* originator = Game::entityManager->GetEntity(context->originator);
if (originator) {
const auto distance = (originator->GetPosition() - entity->GetPosition()).Length();
state = distance <= m_Distance;
}
} }
} }
bitStream.Write(state); bitStream.Write(state);
} }
if (state) { auto* behaviorToCall = state ? m_actionTrue : m_actionFalse;
this->m_actionTrue->Calculate(context, bitStream, branch); behaviorToCall->Calculate(context, bitStream, branch);
} else {
this->m_actionFalse->Calculate(context, bitStream, branch);
}
} }
void SwitchBehavior::Load() { void SwitchBehavior::Load() {
@@ -72,5 +82,7 @@ void SwitchBehavior::Load() {
this->m_isEnemyFaction = GetBoolean("isEnemyFaction"); this->m_isEnemyFaction = GetBoolean("isEnemyFaction");
this->m_targetHasBuff = GetInt("target_has_buff"); this->m_targetHasBuff = GetInt("target_has_buff", -1);
this->m_Distance = GetFloat("distance", -1.0f);
} }

View File

@@ -14,6 +14,8 @@ public:
int32_t m_targetHasBuff; int32_t m_targetHasBuff;
float m_Distance;
/* /*
* Inherited * Inherited
*/ */

View File

@@ -47,11 +47,11 @@ void SwitchMultipleBehavior::Load() {
auto result = query.execQuery(); auto result = query.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto behavior_id = static_cast<uint32_t>(result.getFloatField(1)); const auto behavior_id = static_cast<uint32_t>(result.getFloatField("behavior"));
auto* behavior = CreateBehavior(behavior_id); auto* behavior = CreateBehavior(behavior_id);
auto value = result.getFloatField(2); auto value = result.getFloatField("value");
this->m_behaviors.emplace_back(value, behavior); this->m_behaviors.emplace_back(value, behavior);

View File

@@ -9,11 +9,15 @@
#include "UserManager.h" #include "UserManager.h"
#include "CDMissionsTable.h" #include "CDMissionsTable.h"
AchievementVendorComponent::AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {
RefreshInventory(true);
};
bool AchievementVendorComponent::SellsItem(Entity* buyer, const LOT lot) { bool AchievementVendorComponent::SellsItem(Entity* buyer, const LOT lot) {
auto* missionComponent = buyer->GetComponent<MissionComponent>(); auto* missionComponent = buyer->GetComponent<MissionComponent>();
if (!missionComponent) return false; if (!missionComponent) return false;
if (m_PlayerPurchasableItems[buyer->GetObjectID()].contains(lot)){ if (m_PlayerPurchasableItems[buyer->GetObjectID()].contains(lot)) {
return true; return true;
} }
@@ -35,7 +39,7 @@ void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM); int itemCompID = compRegistryTable->GetByIDAndType(lot, eReplicaComponentType::ITEM);
CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID); CDItemComponent itemComp = itemComponentTable->GetItemComponentByID(itemCompID);
uint32_t costLOT = itemComp.commendationLOT; uint32_t costLOT = itemComp.commendationLOT;
if (costLOT == -1 || !SellsItem(buyer, lot)) { if (costLOT == -1 || !SellsItem(buyer, lot)) {
auto* user = UserManager::Instance()->GetUser(buyer->GetSystemAddress()); auto* user = UserManager::Instance()->GetUser(buyer->GetSystemAddress());
CheatDetection::ReportCheat(user, buyer->GetSystemAddress(), "Attempted to buy item %i from achievement vendor %i that is not purchasable", lot, m_Parent->GetLOT()); CheatDetection::ReportCheat(user, buyer->GetSystemAddress(), "Attempted to buy item %i from achievement vendor %i that is not purchasable", lot, m_Parent->GetLOT());
@@ -44,7 +48,7 @@ void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
} }
auto* inventoryComponent = buyer->GetComponent<InventoryComponent>(); auto* inventoryComponent = buyer->GetComponent<InventoryComponent>();
if (!inventoryComponent) { if (!inventoryComponent) {
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_FAIL);
return; return;
} }
@@ -69,4 +73,9 @@ void AchievementVendorComponent::Buy(Entity* buyer, LOT lot, uint32_t count) {
inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR); inventoryComponent->AddItem(lot, count, eLootSourceType::VENDOR);
GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS); GameMessages::SendVendorTransactionResult(buyer, buyer->GetSystemAddress(), eVendorTransactionResult::PURCHASE_SUCCESS);
} }
void AchievementVendorComponent::RefreshInventory(bool isCreation) {
SetHasStandardCostItems(true);
Game::entityManager->SerializeEntity(m_Parent);
}

View File

@@ -11,7 +11,9 @@ class Entity;
class AchievementVendorComponent final : public VendorComponent { class AchievementVendorComponent final : public VendorComponent {
public: public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR; static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::ACHIEVEMENT_VENDOR;
AchievementVendorComponent(Entity* parent) : VendorComponent(parent) {}; AchievementVendorComponent(Entity* parent);
void RefreshInventory(bool isCreation = false) override;
bool SellsItem(Entity* buyer, const LOT lot); bool SellsItem(Entity* buyer, const LOT lot);
void Buy(Entity* buyer, LOT lot, uint32_t count); void Buy(Entity* buyer, LOT lot, uint32_t count);

View File

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

View File

@@ -29,7 +29,8 @@
BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) { BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id): Component(parent) {
m_Target = LWOOBJID_EMPTY; m_Target = LWOOBJID_EMPTY;
SetAiState(AiState::spawn); m_DirtyStateOrTarget = true;
m_State = AiState::spawn;
m_Timer = 1.0f; m_Timer = 1.0f;
m_StartPosition = parent->GetPosition(); m_StartPosition = parent->GetPosition();
m_MovementAI = nullptr; m_MovementAI = nullptr;
@@ -45,20 +46,20 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto componentResult = componentQuery.execQuery(); auto componentResult = componentQuery.execQuery();
if (!componentResult.eof()) { if (!componentResult.eof()) {
if (!componentResult.fieldIsNull(0)) if (!componentResult.fieldIsNull("aggroRadius"))
m_AggroRadius = componentResult.getFloatField(0); m_AggroRadius = componentResult.getFloatField("aggroRadius");
if (!componentResult.fieldIsNull(1)) if (!componentResult.fieldIsNull("tetherSpeed"))
m_TetherSpeed = componentResult.getFloatField(1); m_TetherSpeed = componentResult.getFloatField("tetherSpeed");
if (!componentResult.fieldIsNull(2)) if (!componentResult.fieldIsNull("pursuitSpeed"))
m_PursuitSpeed = componentResult.getFloatField(2); m_PursuitSpeed = componentResult.getFloatField("pursuitSpeed");
if (!componentResult.fieldIsNull(3)) if (!componentResult.fieldIsNull("softTetherRadius"))
m_SoftTetherRadius = componentResult.getFloatField(3); m_SoftTetherRadius = componentResult.getFloatField("softTetherRadius");
if (!componentResult.fieldIsNull(4)) if (!componentResult.fieldIsNull("hardTetherRadius"))
m_HardTetherRadius = componentResult.getFloatField(4); m_HardTetherRadius = componentResult.getFloatField("hardTetherRadius");
} }
componentResult.finalize(); componentResult.finalize();
@@ -82,11 +83,11 @@ BaseCombatAIComponent::BaseCombatAIComponent(Entity* parent, const uint32_t id):
auto result = skillQuery.execQuery(); auto result = skillQuery.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto skillId = static_cast<uint32_t>(result.getIntField(0)); const auto skillId = static_cast<uint32_t>(result.getIntField("skillID"));
const auto abilityCooldown = static_cast<float>(result.getFloatField(1)); const auto abilityCooldown = static_cast<float>(result.getFloatField("cooldown"));
const auto behaviorId = static_cast<uint32_t>(result.getIntField(2)); const auto behaviorId = static_cast<uint32_t>(result.getIntField("behaviorID"));
auto* behavior = Behavior::CreateBehavior(behaviorId); auto* behavior = Behavior::CreateBehavior(behaviorId);
@@ -150,19 +151,18 @@ void BaseCombatAIComponent::Update(const float deltaTime) {
m_dpEntityEnemy->SetPosition(m_Parent->GetPosition()); m_dpEntityEnemy->SetPosition(m_Parent->GetPosition());
//Process enter events //Process enter events
for (auto en : m_dpEntity->GetNewObjects()) { for (const auto id : m_dpEntity->GetNewObjects()) {
m_Parent->OnCollisionPhantom(en->GetObjectID()); m_Parent->OnCollisionPhantom(id);
} }
//Process exit events //Process exit events
for (auto en : m_dpEntity->GetRemovedObjects()) { for (const auto id : m_dpEntity->GetRemovedObjects()) {
m_Parent->OnCollisionLeavePhantom(en->GetObjectID()); m_Parent->OnCollisionLeavePhantom(id);
} }
// Check if we should stop the tether effect // Check if we should stop the tether effect
if (m_TetherEffectActive) { if (m_TetherEffectActive) {
m_TetherTime -= deltaTime; m_TetherTime -= deltaTime;
const auto& info = m_MovementAI->GetInfo();
if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared( if (m_Target != LWOOBJID_EMPTY || (NiPoint3::DistanceSquared(
m_StartPosition, m_StartPosition,
m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0) m_Parent->GetPosition()) < 20 * 20 && m_TetherTime <= 0)

View File

@@ -326,9 +326,9 @@ Entity* BuffComponent::GetParent() const {
return m_Parent; return m_Parent;
} }
void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void BuffComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
// Load buffs // Load buffs
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element. // Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff"); auto* buffElement = dest->FirstChildElement("buff");
@@ -386,15 +386,15 @@ void BuffComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
} }
void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void BuffComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
// Save buffs // Save buffs
auto* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
// Make sure we have a clean buff element. // Make sure we have a clean buff element.
auto* buffElement = dest->FirstChildElement("buff"); auto* buffElement = dest->FirstChildElement("buff");
if (buffElement == nullptr) { if (buffElement == nullptr) {
buffElement = doc->NewElement("buff"); buffElement = doc.NewElement("buff");
dest->LinkEndChild(buffElement); dest->LinkEndChild(buffElement);
} else { } else {
@@ -402,7 +402,7 @@ void BuffComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
for (const auto& [id, buff] : m_Buffs) { for (const auto& [id, buff] : m_Buffs) {
auto* buffEntry = doc->NewElement("b"); auto* buffEntry = doc.NewElement("b");
// TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout. // TODO: change this if to if (buff.cancelOnZone || buff.cancelOnLogout) handling at some point. No current way to differentiate between zone transfer and logout.
if (buff.cancelOnZone) continue; if (buff.cancelOnZone) continue;
@@ -450,7 +450,7 @@ const std::vector<BuffParameter>& BuffComponent::GetBuffParameters(int32_t buffI
param.value = result.getFloatField("NumberValue"); param.value = result.getFloatField("NumberValue");
param.effectId = result.getIntField("EffectID"); param.effectId = result.getIntField("EffectID");
if (!result.fieldIsNull(3)) { if (!result.fieldIsNull("StringValue")) {
std::istringstream stream(result.getStringField("StringValue")); std::istringstream stream(result.getStringField("StringValue"));
std::string token; std::string token;

View File

@@ -57,9 +57,9 @@ public:
Entity* GetParent() const; Entity* GetParent() const;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -186,9 +186,9 @@ void CharacterComponent::SetGMLevel(eGameMasterLevel gmlevel) {
m_GMLevel = gmlevel; m_GMLevel = gmlevel;
} }
void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while loading XML!"); LOG("Failed to find char tag while loading XML!");
return; return;
@@ -299,8 +299,8 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
} }
void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void CharacterComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* minifig = doc->FirstChildElement("obj")->FirstChildElement("mf"); tinyxml2::XMLElement* minifig = doc.FirstChildElement("obj")->FirstChildElement("mf");
if (!minifig) { if (!minifig) {
LOG("Failed to find mf tag while updating XML!"); LOG("Failed to find mf tag while updating XML!");
return; return;
@@ -320,7 +320,7 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// done with minifig // done with minifig
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while updating XML!"); LOG("Failed to find char tag while updating XML!");
return; return;
@@ -338,11 +338,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
// Set the zone statistics of the form <zs><s/> ... <s/></zs> // Set the zone statistics of the form <zs><s/> ... <s/></zs>
auto zoneStatistics = character->FirstChildElement("zs"); auto zoneStatistics = character->FirstChildElement("zs");
if (!zoneStatistics) zoneStatistics = doc->NewElement("zs"); if (!zoneStatistics) zoneStatistics = doc.NewElement("zs");
zoneStatistics->DeleteChildren(); zoneStatistics->DeleteChildren();
for (auto pair : m_ZoneStatistics) { for (auto pair : m_ZoneStatistics) {
auto zoneStatistic = doc->NewElement("s"); auto zoneStatistic = doc.NewElement("s");
zoneStatistic->SetAttribute("map", pair.first); zoneStatistic->SetAttribute("map", pair.first);
zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected); zoneStatistic->SetAttribute("ac", pair.second.m_AchievementsCollected);

View File

@@ -70,8 +70,8 @@ public:
CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress); CharacterComponent(Entity* parent, Character* character, const SystemAddress& systemAddress);
~CharacterComponent() override; ~CharacterComponent() override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

View File

@@ -21,11 +21,11 @@ void Component::OnUse(Entity* originator) {
} }
void Component::UpdateXml(tinyxml2::XMLDocument* doc) { void Component::UpdateXml(tinyxml2::XMLDocument& doc) {
} }
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) { void Component::LoadFromXml(const tinyxml2::XMLDocument& doc) {
} }

View File

@@ -34,13 +34,13 @@ public:
* Save data from this componennt to character XML * Save data from this componennt to character XML
* @param doc the document to write data to * @param doc the document to write data to
*/ */
virtual void UpdateXml(tinyxml2::XMLDocument* doc); virtual void UpdateXml(tinyxml2::XMLDocument& doc);
/** /**
* Load base data for this component from character XML * Load base data for this component from character XML
* @param doc the document to read data from * @param doc the document to read data from
*/ */
virtual void LoadFromXml(tinyxml2::XMLDocument* doc); virtual void LoadFromXml(const tinyxml2::XMLDocument& doc);
virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction); virtual void Serialize(RakNet::BitStream& outBitStream, bool isConstruction);

View File

@@ -26,7 +26,6 @@ ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Phy
m_SpeedMultiplier = 1; m_SpeedMultiplier = 1;
m_GravityScale = 1; m_GravityScale = 1;
m_DirtyCheats = false; m_DirtyCheats = false;
m_IgnoreMultipliers = false;
m_DirtyEquippedItemInfo = true; m_DirtyEquippedItemInfo = true;
m_PickupRadius = 0.0f; m_PickupRadius = 0.0f;
@@ -92,31 +91,31 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
outBitStream.Write(m_ImmuneToStunInteractCount); outBitStream.Write(m_ImmuneToStunInteractCount);
} }
if (m_IgnoreMultipliers) m_DirtyCheats = false; outBitStream.Write(m_DirtyCheats || bIsInitialUpdate);
if (m_DirtyCheats || bIsInitialUpdate) {
outBitStream.Write(m_DirtyCheats);
if (m_DirtyCheats) {
outBitStream.Write(m_GravityScale); outBitStream.Write(m_GravityScale);
outBitStream.Write(m_SpeedMultiplier); outBitStream.Write(m_SpeedMultiplier);
m_DirtyCheats = false; if (!bIsInitialUpdate) m_DirtyCheats = false;
} }
outBitStream.Write(m_DirtyEquippedItemInfo); outBitStream.Write(m_DirtyEquippedItemInfo || bIsInitialUpdate);
if (m_DirtyEquippedItemInfo) { if (m_DirtyEquippedItemInfo || bIsInitialUpdate) {
outBitStream.Write(m_PickupRadius); outBitStream.Write(m_PickupRadius);
outBitStream.Write(m_InJetpackMode); outBitStream.Write(m_InJetpackMode);
m_DirtyEquippedItemInfo = false;
if (!bIsInitialUpdate) m_DirtyEquippedItemInfo = false;
} }
outBitStream.Write(m_DirtyBubble); outBitStream.Write(m_DirtyBubble || bIsInitialUpdate);
if (m_DirtyBubble) { if (m_DirtyBubble || bIsInitialUpdate) {
outBitStream.Write(m_IsInBubble); outBitStream.Write(m_IsInBubble);
if (m_IsInBubble) { if (m_IsInBubble) {
outBitStream.Write(m_BubbleType); outBitStream.Write(m_BubbleType);
outBitStream.Write(m_SpecialAnims); outBitStream.Write(m_SpecialAnims);
} }
m_DirtyBubble = false;
if (!bIsInitialUpdate) m_DirtyBubble = false;
} }
outBitStream.Write(m_DirtyPosition || bIsInitialUpdate); outBitStream.Write(m_DirtyPosition || bIsInitialUpdate);
@@ -149,7 +148,8 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
outBitStream.Write(m_AngularVelocity.z); outBitStream.Write(m_AngularVelocity.z);
} }
outBitStream.Write0(); outBitStream.Write0(); // local_space_info, always zero for now.
if (!bIsInitialUpdate) { if (!bIsInitialUpdate) {
m_DirtyPosition = false; m_DirtyPosition = false;
outBitStream.Write(m_IsTeleporting); outBitStream.Write(m_IsTeleporting);
@@ -158,8 +158,8 @@ void ControllablePhysicsComponent::Serialize(RakNet::BitStream& outBitStream, bo
} }
} }
void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); auto* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag!"); LOG("Failed to find char tag!");
return; return;
@@ -178,8 +178,8 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyPosition = true; m_DirtyPosition = true;
} }
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char"); tinyxml2::XMLElement* character = doc.FirstChildElement("obj")->FirstChildElement("char");
if (!character) { if (!character) {
LOG("Failed to find char tag while updating XML!"); LOG("Failed to find char tag while updating XML!");
return; return;

View File

@@ -28,8 +28,8 @@ public:
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Sets the position of this entity, also ensures this update is serialized next tick. * Sets the position of this entity, also ensures this update is serialized next tick.
@@ -174,18 +174,6 @@ public:
*/ */
const float GetGravityScale() const { return m_GravityScale; } const float GetGravityScale() const { return m_GravityScale; }
/**
* Sets the ignore multipliers value, allowing you to skip the serialization of speed and gravity multipliers
* @param value whether or not to ignore multipliers
*/
void SetIgnoreMultipliers(bool value) { m_IgnoreMultipliers = value; }
/**
* Returns the current ignore multipliers value
* @return the current ignore multipliers value
*/
const bool GetIgnoreMultipliers() const { return m_IgnoreMultipliers; }
/** /**
* Can make an entity static, making it unable to move around * Can make an entity static, making it unable to move around
* @param value whether or not the entity is static * @param value whether or not the entity is static
@@ -353,11 +341,6 @@ private:
*/ */
bool m_DirtyCheats; bool m_DirtyCheats;
/**
* Makes it so that the speed multiplier and gravity scale are no longer serialized if false
*/
bool m_IgnoreMultipliers;
/** /**
* Whether this entity is static, making it unable to move * Whether this entity is static, making it unable to move
*/ */

View File

@@ -185,8 +185,8 @@ void DestroyableComponent::Update(float deltaTime) {
m_DamageCooldownTimer -= deltaTime; m_DamageCooldownTimer -= deltaTime;
} }
void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void DestroyableComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); auto* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) { if (!dest) {
LOG("Failed to find dest tag!"); LOG("Failed to find dest tag!");
return; return;
@@ -207,8 +207,8 @@ void DestroyableComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyHealth = true; m_DirtyHealth = true;
} }
void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void DestroyableComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* dest = doc->FirstChildElement("obj")->FirstChildElement("dest"); tinyxml2::XMLElement* dest = doc.FirstChildElement("obj")->FirstChildElement("dest");
if (!dest) { if (!dest) {
LOG("Failed to find dest tag!"); LOG("Failed to find dest tag!");
return; return;
@@ -389,9 +389,9 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
if (result.eof()) return; if (result.eof()) return;
if (result.fieldIsNull(0)) return; if (result.fieldIsNull("enemyList")) return;
const auto* list_string = result.getStringField(0); const auto* list_string = result.getStringField("enemyList");
std::stringstream ss(list_string); std::stringstream ss(list_string);
std::string token; std::string token;

View File

@@ -26,8 +26,8 @@ public:
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Initializes the component using a different LOT * Initializes the component using a different LOT

View File

@@ -38,7 +38,7 @@
#include "CDObjectSkillsTable.h" #include "CDObjectSkillsTable.h"
#include "CDSkillBehaviorTable.h" #include "CDSkillBehaviorTable.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) { InventoryComponent::InventoryComponent(Entity* parent) : Component(parent) {
this->m_Dirty = true; this->m_Dirty = true;
this->m_Equipped = {}; this->m_Equipped = {};
this->m_Pushed = {}; this->m_Pushed = {};
@@ -48,7 +48,8 @@ InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* do
const auto lot = parent->GetLOT(); const auto lot = parent->GetLOT();
if (lot == 1) { if (lot == 1) {
LoadXml(document); auto* character = m_Parent->GetCharacter();
if (character) LoadXml(character->GetXMLDoc());
CheckProxyIntegrity(); CheckProxyIntegrity();
@@ -472,10 +473,10 @@ bool InventoryComponent::HasSpaceForLoot(const std::unordered_map<LOT, int32_t>&
return true; return true;
} }
void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) { void InventoryComponent::LoadXml(const tinyxml2::XMLDocument& document) {
LoadPetXml(document); LoadPetXml(document);
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) { if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!"); LOG("Failed to find 'inv' xml element!");
@@ -557,19 +558,9 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
itemElement->QueryAttribute("parent", &parent); itemElement->QueryAttribute("parent", &parent);
// End custom xml // End custom xml
std::vector<LDFBaseData*> config; auto* item = new Item(id, lot, inventory, slot, count, bound, {}, parent, subKey);
auto* extraInfo = itemElement->FirstChildElement("x"); item->LoadConfigXml(*itemElement);
if (extraInfo) {
std::string modInfo = extraInfo->Attribute("ma");
LDFBaseData* moduleAssembly = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(modInfo.substr(2, modInfo.size() - 1)));
config.push_back(moduleAssembly);
}
const auto* item = new Item(id, lot, inventory, slot, count, bound, config, parent, subKey);
if (equipped) { if (equipped) {
const auto info = Inventory::FindItemComponent(lot); const auto info = Inventory::FindItemComponent(lot);
@@ -594,10 +585,10 @@ void InventoryComponent::LoadXml(tinyxml2::XMLDocument* document) {
} }
} }
void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) { void InventoryComponent::UpdateXml(tinyxml2::XMLDocument& document) {
UpdatePetXml(document); UpdatePetXml(document);
auto* inventoryElement = document->FirstChildElement("obj")->FirstChildElement("inv"); auto* inventoryElement = document.FirstChildElement("obj")->FirstChildElement("inv");
if (inventoryElement == nullptr) { if (inventoryElement == nullptr) {
LOG("Failed to find 'inv' xml element!"); LOG("Failed to find 'inv' xml element!");
@@ -631,7 +622,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
bags->DeleteChildren(); bags->DeleteChildren();
for (const auto* inventory : inventoriesToSave) { for (const auto* inventory : inventoriesToSave) {
auto* bag = document->NewElement("b"); auto* bag = document.NewElement("b");
bag->SetAttribute("t", inventory->GetType()); bag->SetAttribute("t", inventory->GetType());
bag->SetAttribute("m", static_cast<unsigned int>(inventory->GetSize())); bag->SetAttribute("m", static_cast<unsigned int>(inventory->GetSize()));
@@ -654,14 +645,14 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
continue; continue;
} }
auto* bagElement = document->NewElement("in"); auto* bagElement = document.NewElement("in");
bagElement->SetAttribute("t", inventory->GetType()); bagElement->SetAttribute("t", inventory->GetType());
for (const auto& pair : inventory->GetItems()) { for (const auto& pair : inventory->GetItems()) {
auto* item = pair.second; auto* item = pair.second;
auto* itemElement = document->NewElement("i"); auto* itemElement = document.NewElement("i");
itemElement->SetAttribute("l", item->GetLot()); itemElement->SetAttribute("l", item->GetLot());
itemElement->SetAttribute("id", item->GetId()); itemElement->SetAttribute("id", item->GetId());
@@ -675,17 +666,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
itemElement->SetAttribute("parent", item->GetParent()); itemElement->SetAttribute("parent", item->GetParent());
// End custom xml // End custom xml
for (auto* data : item->GetConfig()) { item->SaveConfigXml(*itemElement);
if (data->GetKey() != u"assemblyPartLOTs") {
continue;
}
auto* extraInfo = document->NewElement("x");
extraInfo->SetAttribute("ma", data->GetString(false).c_str());
itemElement->LinkEndChild(extraInfo);
}
bagElement->LinkEndChild(itemElement); bagElement->LinkEndChild(itemElement);
} }
@@ -894,8 +875,6 @@ void InventoryComponent::UnEquipItem(Item* item) {
RemoveSlot(item->GetInfo().equipLocation); RemoveSlot(item->GetInfo().equipLocation);
PurgeProxies(item);
UnequipScripts(item); UnequipScripts(item);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
@@ -905,6 +884,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); PropertyManagementComponent::Instance()->GetParent()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent); Game::zoneManager->GetZoneControlObject()->OnZonePropertyModelRemovedWhileEquipped(m_Parent);
} }
PurgeProxies(item);
} }
@@ -1093,7 +1074,7 @@ void InventoryComponent::CheckItemSet(const LOT lot) {
auto result = query.execQuery(); auto result = query.execQuery();
while (!result.eof()) { while (!result.eof()) {
const auto id = result.getIntField(0); const auto id = result.getIntField("setID");
bool found = false; bool found = false;
@@ -1524,10 +1505,10 @@ void InventoryComponent::PurgeProxies(Item* item) {
const auto root = item->GetParent(); const auto root = item->GetParent();
if (root != LWOOBJID_EMPTY) { if (root != LWOOBJID_EMPTY) {
item = FindItemById(root); Item* itemRoot = FindItemById(root);
if (item != nullptr) { if (itemRoot != nullptr) {
UnEquipItem(item); UnEquipItem(itemRoot);
} }
return; return;
@@ -1542,8 +1523,8 @@ void InventoryComponent::PurgeProxies(Item* item) {
} }
} }
void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) { void InventoryComponent::LoadPetXml(const tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet"); auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) { if (petInventoryElement == nullptr) {
m_Pets.clear(); m_Pets.clear();
@@ -1574,19 +1555,19 @@ void InventoryComponent::LoadPetXml(tinyxml2::XMLDocument* document) {
} }
} }
void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) { void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument& document) {
auto* petInventoryElement = document->FirstChildElement("obj")->FirstChildElement("pet"); auto* petInventoryElement = document.FirstChildElement("obj")->FirstChildElement("pet");
if (petInventoryElement == nullptr) { if (petInventoryElement == nullptr) {
petInventoryElement = document->NewElement("pet"); petInventoryElement = document.NewElement("pet");
document->FirstChildElement("obj")->LinkEndChild(petInventoryElement); document.FirstChildElement("obj")->LinkEndChild(petInventoryElement);
} }
petInventoryElement->DeleteChildren(); petInventoryElement->DeleteChildren();
for (const auto& pet : m_Pets) { for (const auto& pet : m_Pets) {
auto* petElement = document->NewElement("p"); auto* petElement = document.NewElement("p");
petElement->SetAttribute("id", pet.first); petElement->SetAttribute("id", pet.first);
petElement->SetAttribute("l", pet.second.lot); petElement->SetAttribute("l", pet.second.lot);
@@ -1599,18 +1580,18 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) {
} }
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){ bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId) {
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid; BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary; if (slot == 1) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand; else if (slot == 2) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck; else if (slot == 3) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head; else if (slot == 4) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable; else if (slot == 5) behaviorSlot = BehaviorSlot::Consumable;
else return false; else return false;
return SetSkill(behaviorSlot, skillId); return SetSkill(behaviorSlot, skillId);
} }
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId) {
if (skillId == 0) return false; if (skillId == 0) return false;
const auto index = m_Skills.find(slot); const auto index = m_Skills.find(slot);
if (index != m_Skills.end()) { if (index != m_Skills.end()) {
@@ -1622,4 +1603,3 @@ bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
m_Skills.insert_or_assign(slot, skillId); m_Skills.insert_or_assign(slot, skillId);
return true; return true;
} }

View File

@@ -38,12 +38,12 @@ enum class eItemType : int32_t;
class InventoryComponent final : public Component { class InventoryComponent final : public Component {
public: public:
static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY; static constexpr eReplicaComponentType ComponentType = eReplicaComponentType::INVENTORY;
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr); InventoryComponent(Entity* parent);
void Update(float deltaTime) override; void Update(float deltaTime) override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
void LoadXml(tinyxml2::XMLDocument* document); void LoadXml(const tinyxml2::XMLDocument& document);
void UpdateXml(tinyxml2::XMLDocument* document) override; void UpdateXml(tinyxml2::XMLDocument& document) override;
/** /**
* Returns an inventory of the specified type, if it exists * Returns an inventory of the specified type, if it exists
@@ -470,13 +470,13 @@ private:
* Saves all the pet information stored in inventory items to the database * Saves all the pet information stored in inventory items to the database
* @param document the xml doc to save to * @param document the xml doc to save to
*/ */
void LoadPetXml(tinyxml2::XMLDocument* document); void LoadPetXml(const tinyxml2::XMLDocument& document);
/** /**
* Loads all the pet information from an xml doc into items * Loads all the pet information from an xml doc into items
* @param document the xml doc to load from * @param document the xml doc to load from
*/ */
void UpdatePetXml(tinyxml2::XMLDocument* document); void UpdatePetXml(tinyxml2::XMLDocument& document);
}; };
#endif #endif

View File

@@ -13,8 +13,8 @@ LevelProgressionComponent::LevelProgressionComponent(Entity* parent) : Component
m_CharacterVersion = eCharacterVersion::LIVE; m_CharacterVersion = eCharacterVersion::LIVE;
} }
void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); tinyxml2::XMLElement* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) { if (!level) {
LOG("Failed to find lvl tag while updating XML!"); LOG("Failed to find lvl tag while updating XML!");
return; return;
@@ -24,8 +24,8 @@ void LevelProgressionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
level->SetAttribute("cv", static_cast<uint32_t>(m_CharacterVersion)); level->SetAttribute("cv", static_cast<uint32_t>(m_CharacterVersion));
} }
void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void LevelProgressionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
tinyxml2::XMLElement* level = doc->FirstChildElement("obj")->FirstChildElement("lvl"); auto* level = doc.FirstChildElement("obj")->FirstChildElement("lvl");
if (!level) { if (!level) {
LOG("Failed to find lvl tag while loading XML!"); LOG("Failed to find lvl tag while loading XML!");
return; return;

View File

@@ -27,13 +27,13 @@ public:
* Save data from this componennt to character XML * Save data from this componennt to character XML
* @param doc the document to write data to * @param doc the document to write data to
*/ */
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Load base data for this component from character XML * Load base data for this component from character XML
* @param doc the document to read data from * @param doc the document to read data from
*/ */
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
/** /**
* Gets the current level of the entity * Gets the current level of the entity

View File

@@ -466,8 +466,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
return false; return false;
} }
if (!result.fieldIsNull(0)) { if (!result.fieldIsNull("type")) {
const auto type = std::string(result.getStringField(0)); const auto type = std::string(result.getStringField("type"));
result.finalize(); result.finalize();
@@ -504,10 +504,8 @@ bool MissionComponent::RequiresItem(const LOT lot) {
} }
void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { void MissionComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) {
if (doc == nullptr) return; auto* mis = doc.FirstChildElement("obj")->FirstChildElement("mis");
auto* mis = doc->FirstChildElement("obj")->FirstChildElement("mis");
if (mis == nullptr) return; if (mis == nullptr) return;
@@ -523,7 +521,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* mission = new Mission(this, missionId); auto* mission = new Mission(this, missionId);
mission->LoadFromXml(doneM); mission->LoadFromXml(*doneM);
doneM = doneM->NextSiblingElement(); doneM = doneM->NextSiblingElement();
@@ -540,7 +538,7 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
auto* mission = new Mission(this, missionId); auto* mission = new Mission(this, missionId);
mission->LoadFromXml(currentM); mission->LoadFromXml(*currentM);
if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) { if (currentM->QueryAttribute("o", &missionOrder) == tinyxml2::XML_SUCCESS && mission->IsMission()) {
mission->SetUniqueMissionOrderID(missionOrder); mission->SetUniqueMissionOrderID(missionOrder);
@@ -554,25 +552,23 @@ void MissionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
} }
void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) { void MissionComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
if (doc == nullptr) return;
auto shouldInsertMis = false; auto shouldInsertMis = false;
auto* obj = doc->FirstChildElement("obj"); auto* obj = doc.FirstChildElement("obj");
auto* mis = obj->FirstChildElement("mis"); auto* mis = obj->FirstChildElement("mis");
if (mis == nullptr) { if (mis == nullptr) {
mis = doc->NewElement("mis"); mis = doc.NewElement("mis");
shouldInsertMis = true; shouldInsertMis = true;
} }
mis->DeleteChildren(); mis->DeleteChildren();
auto* done = doc->NewElement("done"); auto* done = doc.NewElement("done");
auto* cur = doc->NewElement("cur"); auto* cur = doc.NewElement("cur");
for (const auto& pair : m_Missions) { for (const auto& pair : m_Missions) {
auto* mission = pair.second; auto* mission = pair.second;
@@ -580,10 +576,10 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
if (mission) { if (mission) {
const auto complete = mission->IsComplete(); const auto complete = mission->IsComplete();
auto* m = doc->NewElement("m"); auto* m = doc.NewElement("m");
if (complete) { if (complete) {
mission->UpdateXml(m); mission->UpdateXml(*m);
done->LinkEndChild(m); done->LinkEndChild(m);
@@ -591,7 +587,7 @@ void MissionComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
} }
if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID()); if (mission->IsMission()) m->SetAttribute("o", mission->GetUniqueMissionOrderID());
mission->UpdateXml(m); mission->UpdateXml(*m);
cur->LinkEndChild(m); cur->LinkEndChild(m);
} }

View File

@@ -31,8 +31,8 @@ public:
explicit MissionComponent(Entity* parent); explicit MissionComponent(Entity* parent);
~MissionComponent() override; ~MissionComponent() override;
void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags); void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void LoadFromXml(tinyxml2::XMLDocument* doc) override; void LoadFromXml(const tinyxml2::XMLDocument& doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override; void UpdateXml(tinyxml2::XMLDocument& doc) override;
/** /**
* Returns all the missions for this entity, mapped by mission ID * Returns all the missions for this entity, mapped by mission ID

View File

@@ -50,9 +50,44 @@ MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) :
m_CurrentSpeed = 0; m_CurrentSpeed = 0;
m_MaxSpeed = 0; m_MaxSpeed = 0;
m_LockRotation = false; m_LockRotation = false;
m_Path = nullptr;
m_SourcePosition = m_Parent->GetPosition();
m_Paused = false;
m_SavedVelocity = NiPoint3Constant::ZERO;
if (!m_Parent->GetComponent<BaseCombatAIComponent>()) SetPath(m_Parent->GetVarAsString(u"attached_path"));
}
void MovementAIComponent::SetPath(const std::string pathName) {
m_Path = Game::zoneManager->GetZone()->GetPath(pathName);
if (!pathName.empty()) LOG("WARNING: %s path %s", m_Path ? "Found" : "Failed to find", pathName.c_str());
if (!m_Path) return;
SetMaxSpeed(1);
SetCurrentSpeed(m_BaseSpeed);
SetPath(m_Path->pathWaypoints);
}
void MovementAIComponent::Pause() {
if (m_Paused) return;
m_Paused = true;
SetPosition(ApproximateLocation());
m_SavedVelocity = GetVelocity();
SetVelocity(NiPoint3Constant::ZERO);
Game::entityManager->SerializeEntity(m_Parent);
}
void MovementAIComponent::Resume() {
if (!m_Paused) return;
m_Paused = false;
SetVelocity(m_SavedVelocity);
m_SavedVelocity = NiPoint3Constant::ZERO;
SetRotation(NiQuaternion::LookAt(m_Parent->GetPosition(), m_NextWaypoint));
Game::entityManager->SerializeEntity(m_Parent);
} }
void MovementAIComponent::Update(const float deltaTime) { void MovementAIComponent::Update(const float deltaTime) {
if (m_Paused) return;
if (m_PullingToPoint) { if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
@@ -81,64 +116,65 @@ void MovementAIComponent::Update(const float deltaTime) {
} }
m_TimeTravelled += deltaTime; m_TimeTravelled += deltaTime;
SetPosition(ApproximateLocation());
if (m_TimeTravelled < m_TimeToTravel) return; if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f; m_TimeTravelled = 0.0f;
const auto source = GetCurrentWaypoint(); const auto source = GetCurrentWaypoint();
SetPosition(source); SetPosition(source);
m_SourcePosition = source;
NiPoint3 velocity = NiPoint3Constant::ZERO;
if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek? if (m_Acceleration > 0 && m_BaseSpeed > 0 && AdvanceWaypointIndex()) // Do we have another waypoint to seek?
{ {
m_NextWaypoint = GetCurrentWaypoint(); m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) { if (m_NextWaypoint == source) {
m_TimeToTravel = 0.0f; m_TimeToTravel = 0.0f;
} else {
m_CurrentSpeed = std::min(m_CurrentSpeed + m_Acceleration, m_MaxSpeed);
goto nextAction; const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
const auto delta = m_NextWaypoint - source;
// Normalize the vector
const auto length = delta.Length();
if (length > 0.0f) {
SetVelocity((delta / length) * speed);
}
// Calclute the time it will take to reach the next waypoint with the current speed
m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} }
if (m_CurrentSpeed < m_MaxSpeed) {
m_CurrentSpeed += m_Acceleration;
}
if (m_CurrentSpeed > m_MaxSpeed) {
m_CurrentSpeed = m_MaxSpeed;
}
const auto speed = m_CurrentSpeed * m_BaseSpeed; // scale speed based on base speed
const auto delta = m_NextWaypoint - source;
// Normalize the vector
const auto length = delta.Length();
if (length > 0) {
velocity = (delta / length) * speed;
}
// Calclute the time it will take to reach the next waypoint with the current speed
m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} else { } else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint // Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (m_CurrentPath.empty()) { if (m_CurrentPath.empty()) {
Stop(); if (m_Path) {
if (m_Path->pathBehavior == PathBehavior::Loop) {
return; SetPath(m_Path->pathWaypoints);
} else if (m_Path->pathBehavior == PathBehavior::Bounce) {
std::vector<PathWaypoint> waypoints = m_Path->pathWaypoints;
std::reverse(waypoints.begin(), waypoints.end());
SetPath(waypoints);
} else if (m_Path->pathBehavior == PathBehavior::Once) {
Stop();
return;
}
} else {
Stop();
return;
}
} }
SetDestination(m_CurrentPath.top()); SetDestination(m_CurrentPath.top().position);
m_CurrentPath.pop(); m_CurrentPath.pop();
} }
nextAction:
SetVelocity(velocity);
Game::entityManager->SerializeEntity(m_Parent); Game::entityManager->SerializeEntity(m_Parent);
} }
@@ -161,7 +197,7 @@ NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
} }
NiPoint3 MovementAIComponent::ApproximateLocation() const { NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = m_Parent->GetPosition(); auto source = m_SourcePosition;
if (AtFinalWaypoint()) return source; if (AtFinalWaypoint()) return source;
@@ -227,13 +263,13 @@ void MovementAIComponent::PullToPoint(const NiPoint3& point) {
m_PullPoint = point; m_PullPoint = point;
} }
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) { void MovementAIComponent::SetPath(std::vector<PathWaypoint> path) {
if (path.empty()) return; if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) { std::for_each(path.rbegin(), path.rend() - 1, [this](const PathWaypoint& point) {
this->m_CurrentPath.push(point); this->m_CurrentPath.push(point);
}); });
SetDestination(path.front()); SetDestination(path.front().position);
} }
float MovementAIComponent::GetBaseSpeed(LOT lot) { float MovementAIComponent::GetBaseSpeed(LOT lot) {
@@ -278,6 +314,23 @@ void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (!m_LockRotation) m_Parent->SetRotation(value); if (!m_LockRotation) m_Parent->SetRotation(value);
} }
NiPoint3 MovementAIComponent::GetVelocity() const {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
return controllablePhysicsComponent->GetVelocity();
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
return simplePhysicsComponent->GetVelocity();
}
return NiPoint3Constant::ZERO;
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) { void MovementAIComponent::SetVelocity(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>(); auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
@@ -294,7 +347,7 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) {
} }
} }
void MovementAIComponent::SetDestination(const NiPoint3& destination) { void MovementAIComponent::SetDestination(const NiPoint3 destination) {
if (m_PullingToPoint) return; if (m_PullingToPoint) return;
const auto location = ApproximateLocation(); const auto location = ApproximateLocation();
@@ -303,6 +356,8 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
SetPosition(location); SetPosition(location);
} }
m_SourcePosition = location;
std::vector<NiPoint3> computedPath; std::vector<NiPoint3> computedPath;
if (dpWorld::IsLoaded()) { if (dpWorld::IsLoaded()) {
computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed); computedPath = dpWorld::GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
@@ -319,8 +374,7 @@ void MovementAIComponent::SetDestination(const NiPoint3& destination) {
auto step = delta / 10.0f; auto step = delta / 10.0f;
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
// TODO: Replace this with += when the NiPoint3::operator+= is fixed start += step;
start = start + step;
computedPath.push_back(start); computedPath.push_back(start);
} }

View File

@@ -14,11 +14,14 @@
#include "Logger.h" #include "Logger.h"
#include "Component.h" #include "Component.h"
#include "eReplicaComponentType.h" #include "eReplicaComponentType.h"
#include "Zone.h"
#include <vector> #include <vector>
class ControllablePhysicsComponent; class ControllablePhysicsComponent;
class BaseCombatAIComponent; class BaseCombatAIComponent;
struct Path;
/** /**
* Information that describes the different variables used to make an entity move around * Information that describes the different variables used to make an entity move around
*/ */
@@ -61,6 +64,8 @@ public:
MovementAIComponent(Entity* parentEntity, MovementAIInfo info); MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
void SetPath(const std::string pathName);
void Update(float deltaTime) override; void Update(float deltaTime) override;
/** /**
@@ -73,7 +78,7 @@ public:
* Set a destination point for the entity to move towards * Set a destination point for the entity to move towards
* @param value the destination point to move towards * @param value the destination point to move towards
*/ */
void SetDestination(const NiPoint3& value); void SetDestination(const NiPoint3 value);
/** /**
* Returns the current rotation this entity is moving towards * Returns the current rotation this entity is moving towards
@@ -189,7 +194,13 @@ public:
* Sets a path to follow for the AI * Sets a path to follow for the AI
* @param path the path to follow * @param path the path to follow
*/ */
void SetPath(std::vector<NiPoint3> path); void SetPath(std::vector<PathWaypoint> path);
void Pause();
void Resume();
NiPoint3 GetVelocity() const;
/** /**
* Returns the base speed from the DB for a given LOT * Returns the base speed from the DB for a given LOT
@@ -301,7 +312,15 @@ private:
/** /**
* The path from the current position to the destination. * The path from the current position to the destination.
*/ */
std::stack<NiPoint3> m_CurrentPath; std::stack<PathWaypoint> m_CurrentPath;
const Path* m_Path = nullptr;
NiPoint3 m_SourcePosition;
bool m_Paused;
NiPoint3 m_SavedVelocity;
}; };
#endif // MOVEMENTAICOMPONENT_H #endif // MOVEMENTAICOMPONENT_H

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