Compare commits

..

54 Commits

Author SHA1 Message Date
David Markowitz
a8a4da0679 Component work
Further component ripping

finish switch rip

Fix bad names

Get it running

Fix issues

Components: order is complete, need epilogue code now.

finished port of thingy

Update ComponentFactories.py

Update DoObjectPreLoad.py
2023-10-31 14:09:40 -07:00
David Markowitz
623da4bed9 Entity: Add missing destroyable case
Adds a missing edge case on the client where you may have the is_smashable flag set, but dont actually have a destroyable component.  In this case you should create only a destroyable component and add it to the component and serialize it last after all other data.  We also needed to add the set_faction var reading and setting as this is how Oliver Sudden actually gets their faction since they have no actual information in the database, it is all stored in ldf keys.

Tested that Oliver Sudden no longer logs Unable to unserialize logs when serialized or constructed.
2023-10-19 03:25:13 -07:00
David Markowitz
50921cce2d Loot: Fix rarity sorting (#1226)
Missed this step from the previous speedup.  rollloot numbers match within margin of error to numbers pre-cache improvement.
2023-10-18 09:18:47 -05:00
David Markowitz
3806891db0 Teams: Default team loot to ffa (#1224) 2023-10-18 09:18:22 -05:00
David Markowitz
ba91058736 Destroyable: Fix animated deaths (#1227)
Fixes #1222

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

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

Tested that while on moonbase, the players gravity is no longer reset to 1 when they change their cheat info.
2023-10-16 07:55:57 -05:00
David Markowitz
c6087ce77a perf: Loot memory savings (#1165)
* Move away from constructor queries

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

* Huge loot namespace rework

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

* cache rebuild matrix

* Database: move reading to own function

Also change name of cache to PascalCase

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

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

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

yep

* Abstract to PhysicsComponent

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

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

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

* Update PhantomPhysicsComponent.cpp

* Add SimplePhysicsTest

* Add construction test

* Update SimplePhysicsComponentTests.cpp

* remove flags and fix override

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

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

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

* remove debuging longs and remove oopsie

* address feedback

* make log more useful

* make filter more flat

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

* fix typing

* Add filter target to TacArc and update filter target

* fix double declaration

* Some debugging logs

* Update TacArc reading

* make log clearer

* logs

* Update TacArcBehavior.cpp

* banana

* fix max targets

* remove extreanous parenthesesuuesdsds

* make behavior slot use a real type

---------

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

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

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

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

* Modified OnPlayerLoaded to fix Vehicle Inventory issue

* Change 3

---------

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

* Add config option for ip loggin

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

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

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

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

* remove un-needed function defintions in header

* make reading and writing more efficient

* p

p

* quotes

* remove unneeded default

---------

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

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

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

* Increase height because Nexus Tower is tall

* Update height checker

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

Update dNavMesh.cpp

Update dNavMesh.cpp

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

---------

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

Fix up other large tables to have proper backup lookups

Revert "idk im just dumb ig"

This reverts commit 5d5be5df53b8959b42b291613d7db749a65a3585.

idk im just dumb ig

* Fix slow components registry lookup

* add define for cdclient cache all

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

* Fix reading in rental time and period from property path

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

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

* fix serialize and make update virutal

* Update VendorComponent.h

* Remove flag var

* Update SoundTriggerComponent.h

---------

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

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

* more cleanup, and better defaults

* fixes and tested

* update initializor for guid and when to load sound guids

* make racing sound trigger it's own component

* fix type

* Remove global
move serializes

---------

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

* WIP

* progress missions

* address feedback

* fix newline

* Cleanup

* oops

* fix default for nexus tower jawbox
cleanup some logs

* remove log

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

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

* move stuff around

finally fixed you docker

i love this

Update Dockerfile

Update Dockerfile

change type

Update AuthPackets.cpp

Update AuthServer.cpp

* Update CMakeVariables.txt

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

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

* update

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

* more

* fix broken function

* Further naming fixes

t

Revert "Further naming fixes"

This reverts commit 057189982ba56788d48f9265d815e6c562ba6328.

* next step

* undo all testing changes

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

* void, oops

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

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

* Fix compiling

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

* address feedback

* fix newline

* oops

* remove header

* fix default and const for

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

* fix operator +=
2023-07-31 00:22:56 -07:00
e299bf9b62 fix: read and store the zone transition width properly (#1169) 2023-07-30 23:49:43 -07:00
227 changed files with 6531 additions and 3053 deletions

View File

@@ -14,4 +14,4 @@ EXTERNAL_IP=localhost
MARIADB_USER=darkflame
MARIADB_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_ROOT_PASSWORD=SECRET_VALUE_CHANGE_ME
MARIADB_DATABASE=darkflame
MARIADB_DATABASE=darkflame

View File

@@ -1,12 +1,8 @@
PROJECT_VERSION_MAJOR=1
PROJECT_VERSION_MINOR=1
PROJECT_VERSION_PATCH=0
PROJECT_VERSION_PATCH=1
# LICENSE
LICENSE=AGPL-3.0
# The network version.
# 171023 - Darkflame Universe client
# 171022 - Unmodded client
NET_VERSION=171022
# Debugging
# Set __dynamic to 1 to enable the -rdynamic flag for the linker, yielding some symbols in crashlogs.
__dynamic=1
@@ -22,3 +18,5 @@ __maria_db_connector_compile_jobs__=1
__enable_testing__=1
# The path to OpenSSL. Change this if your OpenSSL install path is different than the default.
OPENSSL_ROOT_DIR=/usr/local/opt/openssl@3/
# Uncomment the below line to cache the entire CDClient into memory
# CDCLIENT_CACHE_ALL=1

264
ComponentDefinitions.py Normal file
View File

@@ -0,0 +1,264 @@
components = {
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS': 1,
'COMPONENT_TYPE_RENDER': 2,
'COMPONENT_TYPE_SIMPLE_PHYSICS': 3,
'COMPONENT_TYPE_CHARACTER': 4,
'COMPONENT_TYPE_SCRIPT': 5,
'COMPONENT_TYPE_BOUNCER': 6,
'COMPONENT_TYPE_DESTROYABLE': 7,
'COMPONENT_TYPE_GHOST': 8,
'COMPONENT_TYPE_SKILL': 9,
'COMPONENT_TYPE_SPAWNER': 10,
'COMPONENT_TYPE_ITEM': 11,
'COMPONENT_TYPE_MODULAR_BUILD': 12,
'COMPONENT_TYPE_BUILD_CONTROLLER': 13,
'COMPONENT_TYPE_REBUILD_ACTIVATOR': 14,
'COMPONENT_TYPE_ICON_ONLY': 15,
'COMPONENT_TYPE_VENDOR': 16,
'COMPONENT_TYPE_INVENTORY': 17,
'COMPONENT_TYPE_PROJECTILE_PHYSICS': 18,
'COMPONENT_TYPE_SHOOTING_GALLERY': 19,
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS': 20,
'COMPONENT_TYPE_DROP_EFFECT': 21,
'COMPONENT_TYPE_CHEST': 22,
'COMPONENT_TYPE_COLLECTIBLE': 23,
'COMPONENT_TYPE_BLUEPRINT': 24,
'COMPONENT_TYPE_MOVING_PLATFORM': 25,
'COMPONENT_TYPE_PET': 26,
'COMPONENT_TYPE_PLATFORM_BOUNDARY': 27,
'COMPONENT_TYPE_MODULE': 28,
'COMPONENT_TYPE_ARCADE': 29,
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS': 30,
'COMPONENT_TYPE_MOVEMENT_AI': 31,
'COMPONENT_TYPE_EXHIBIT': 32,
'COMPONENT_TYPE_OVERHEAD_ICON': 33,
'COMPONENT_TYPE_PET_CONTROL': 34,
'COMPONENT_TYPE_MINIFIG': 35,
'COMPONENT_TYPE_PROPERTY': 36,
'COMPONENT_TYPE_PET_CREATOR': 37,
'COMPONENT_TYPE_MODEL_BUILDER': 38,
'COMPONENT_TYPE_SCRIPTED_ACTIVITY': 39,
'COMPONENT_TYPE_PHANTOM_PHYSICS': 40,
'COMPONENT_TYPE_SPRING_PAD': 41,
'COMPONENT_TYPE_MODEL': 42,
'COMPONENT_TYPE_PROPERTY_ENTRANCE': 43,
'COMPONENT_TYPE_FX': 44,
'COMPONENT_TYPE_PROPERTY_MANAGEMENT': 45,
'COMPONENT_TYPE_VEHICLE_PHYSICS': 46,
'COMPONENT_TYPE_PHYSICS_SYSTEM': 47,
'COMPONENT_TYPE_REBUILD': 48,
'COMPONENT_TYPE_SWITCH': 49,
'COMPONENT_TYPE_ZONE_CONTROL': 50,
'COMPONENT_TYPE_CHANGELING': 51,
'COMPONENT_TYPE_CHOICE_BUILD': 52,
'COMPONENT_TYPE_PACKAGE': 53,
'COMPONENT_TYPE_SOUND_REPEATER': 54,
'COMPONENT_TYPE_SOUND_AMBIENT_2D': 55,
'COMPONENT_TYPE_SOUND_AMBIENT_3D': 56,
'COMPONENT_TYPE_PRECONDITION': 57,
'COMPONENT_TYPE_PLAYER_FLAG': 58,
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY': 59,
'COMPONENT_TYPE_BASE_COMBAT_AI': 60,
'COMPONENT_TYPE_MODULE_ASSEMBLY': 61,
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER': 62,
'COMPONENT_TYPE_RACING_MODULE': 63,
'COMPONENT_TYPE_GENERIC_ACTIVATOR': 64,
'COMPONENT_TYPE_PROPERTY_VENDOR': 65,
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET': 66,
'COMPONENT_TYPE_ROCKET_LAUNCH': 67,
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT': 68,
'COMPONENT_TYPE_TRIGGER': 69,
'COMPONENT_TYPE_DROPPED_LOOT': 70,
'COMPONENT_TYPE_RACING_CONTROL': 71,
'COMPONENT_TYPE_FACTION_TRIGGER': 72,
'COMPONENT_TYPE_MISSION_OFFER': 73,
'COMPONENT_TYPE_RACING_STATS': 74,
'COMPONENT_TYPE_LUP_EXHIBIT': 75,
'COMPONENT_TYPE_BBB': 76,
'COMPONENT_TYPE_SOUND_TRIGGER': 77,
'COMPONENT_TYPE_PROXIMITY_MONITOR': 78,
'COMPONENT_TYPE_RACING_SOUND_TRIGGER': 79,
'COMPONENT_TYPE_CHAT': 80,
'COMPONENT_TYPE_FRIENDS_LIST': 81,
'COMPONENT_TYPE_GUILD': 82,
'COMPONENT_TYPE_LOCAL_SYSTEM': 83,
'COMPONENT_TYPE_MISSION': 84,
'COMPONENT_TYPE_MUTUABLE_MODEL_BEHAVIORS': 85,
'COMPONENT_TYPE_PATHFINDING_CONTROL': 86,
'COMPONENT_TYPE_PLAYER_PET_TAMING': 87,
'COMPONENT_TYPE_PROPERTY_EDITOR': 88,
'COMPONENT_TYPE_SKINNED_RENDER': 89,
'COMPONENT_TYPE_SLASH_COMMAND': 90,
'COMPONENT_TYPE_STATUS_EFFECT': 91,
'COMPONENT_TYPE_TEAMS': 92,
'COMPONENT_TYPE_TEXT_EFFECT': 93,
'COMPONENT_TYPE_TRADE': 94,
'COMPONENT_TYPE_USER_CONTROL': 95,
'COMPONENT_TYPE_IGNORE_LIST': 96,
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP': 97,
'COMPONENT_TYPE_BUFF': 98,
'COMPONENT_TYPE_INTERACTION_MANAGER': 99,
'COMPONENT_TYPE_DONATION_VENDOR': 100,
'COMPONENT_TYPE_COMBAT_MEDIATOR': 101,
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR': 102,
'COMPONENT_TYPE_GATE_RUSH': 103,
'COMPONENT_TYPE_RAIL_ACTIVATOR': 104,
'COMPONENT_TYPE_ROLLER': 105,
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT': 106,
'COMPONENT_TYPE_CRAFTING': 107,
'COMPONENT_TYPE_POSSESSABLE': 108,
'COMPONENT_TYPE_LEVEL_PROGRESSION': 109,
'COMPONENT_TYPE_POSSESSOR': 110,
'COMPONENT_TYPE_MOUNT_CONTROL': 111,
'UNKNOWN_112': 112,
'COMPONENT_TYPE_PROPERTY_PLAQUE': 113,
'COMPONENT_TYPE_BUILD_BORDER': 114,
'UNKNOWN_115': 115,
'COMPONENT_TYPE_CULLING_PLANE': 116,
'INVALID': 4294967295
}
component_initialization_order = [
'COMPONENT_TYPE_POSSESSABLE',
'COMPONENT_TYPE_BLUEPRINT',
'COMPONENT_TYPE_MODULE_ASSEMBLY',
'COMPONENT_TYPE_CONTROLLABLE_PHYSICS',
'COMPONENT_TYPE_PROJECTILE_PHYSICS',
'COMPONENT_TYPE_PHYSICS_SYSTEM',
'COMPONENT_TYPE_VEHICLE_PHYSICS',
'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS',
'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS',
'COMPONENT_TYPE_SIMPLE_PHYSICS',
'COMPONENT_TYPE_PHANTOM_PHYSICS',
'COMPONENT_TYPE_DESTROYABLE',
'COMPONENT_TYPE_MINIFIG',
'COMPONENT_TYPE_CHARACTER',
'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT',
'COMPONENT_TYPE_POSSESSOR',
'COMPONENT_TYPE_MOUNT_CONTROL',
'COMPONENT_TYPE_PET',
'COMPONENT_TYPE_INVENTORY',
'COMPONENT_TYPE_PROXIMITY_MONITOR',
'COMPONENT_TYPE_MOVEMENT_AI',
'COMPONENT_TYPE_SCRIPT',
'COMPONENT_TYPE_SKILL',
'COMPONENT_TYPE_BASE_COMBAT_AI',
'COMPONENT_TYPE_SPAWNER',
'COMPONENT_TYPE_ITEM',
'COMPONENT_TYPE_REBUILD',
'COMPONENT_TYPE_BUILD_BORDER',
'COMPONENT_TYPE_MODULE',
'COMPONENT_TYPE_BUILD_CONTROLLER',
'COMPONENT_TYPE_REBUILD_ACTIVATOR',
'COMPONENT_TYPE_MODULAR_BUILD',
'COMPONENT_TYPE_MISSION_OFFER',
'COMPONENT_TYPE_VENDOR',
'COMPONENT_TYPE_DONATION_VENDOR',
'COMPONENT_TYPE_ACHIEVEMENT_VENDOR',
'COMPONENT_TYPE_SHOOTING_GALLERY',
'COMPONENT_TYPE_ROLLER',
'COMPONENT_TYPE_SCRIPTED_ACTIVITY',
'COMPONENT_TYPE_COLLECTIBLE',
'COMPONENT_TYPE_MOVING_PLATFORM',
'COMPONENT_TYPE_CHANGELING',
'COMPONENT_TYPE_CHOICE_BUILD',
'COMPONENT_TYPE_PACKAGE',
'COMPONENT_TYPE_PLATFORM_BOUNDARY',
'COMPONENT_TYPE_HF_LIGHT_DIRECTION_GADGET',
'COMPONENT_TYPE_CULLING_PLANE',
'COMPONENT_TYPE_EXHIBIT',
'COMPONENT_TYPE_LUP_EXHIBIT',
'COMPONENT_TYPE_PROPERTY',
'COMPONENT_TYPE_PROPERTY_PLAQUE',
'COMPONENT_TYPE_MODEL',
'COMPONENT_TYPE_PROPERTY_ENTRANCE',
'COMPONENT_TYPE_ROCKET_LAUNCH_LUP',
'COMPONENT_TYPE_PROPERTY_MANAGEMENT',
'COMPONENT_TYPE_RAIL_ACTIVATOR',
'COMPONENT_TYPE_BOUNCER',
'COMPONENT_TYPE_SPRING_PAD',
'COMPONENT_TYPE_SWITCH',
'COMPONENT_TYPE_ZONE_CONTROL',
'COMPONENT_TYPE_MODEL_BUILDER',
'COMPONENT_TYPE_SOUND_REPEATER',
'COMPONENT_TYPE_CHEST',
'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER',
'COMPONENT_TYPE_ARCADE',
'COMPONENT_TYPE_RENDER',
'COMPONENT_TYPE_SOUND_AMBIENT_2D',
'COMPONENT_TYPE_SOUND_AMBIENT_3D',
'COMPONENT_TYPE_SOUND_TRIGGER',
'COMPONENT_TYPE_RACING_SOUND_TRIGGER',
'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY',
'COMPONENT_TYPE_GENERIC_ACTIVATOR',
'COMPONENT_TYPE_PROPERTY_VENDOR',
'COMPONENT_TYPE_ROCKET_LAUNCH',
'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT',
'COMPONENT_TYPE_FACTION_TRIGGER',
'COMPONENT_TYPE_RACING_CONTROL',
'COMPONENT_TYPE_GATE_RUSH',
'COMPONENT_TYPE_BBB',
'COMPONENT_TYPE_CRAFTING'
]
component_order_as_ints = {}
component_int_to_name = {}
for component in component_initialization_order:
component_order_as_ints[components[component]] = component
for component in components:
component_int_to_name[components[component]] = component
class GameObject:
type = ""
class ObjectLoadState:
createdGameObject = GameObject()
components = []
hasPhysicsComponent = False
hasDestroyable = False
hasPathfindingComponent = False
hasProximityMonitorComponent = False
hasScriptComponent = False
hasDroppedLootComponent = False
hasModelbehaviors = False
somethingOrNothing = False
physicsComponentId = -1
# Begin custom attributes
serializedOrder = []
hasMissionOffer = False
class NiPoint3:
x = 0.0
y = 0.0
z = 0.0
class NiQuaternion:
w = 0.0
x = 0.0
y = 0.0
z = 0.0
class LoadObject:
objId = 0
owner = 0
lotToSpawn = 0
isLocalPlayer = False
hasDropEffect = False
bIsSmashable = False
dynamicPhysics = False
spawnPosition = NiPoint3()
spawnRotation = NiQuaternion()
scale = 0.0
legoBrickID = 0
legoColorID = 0
configData = {}
isHFPaletteObject = False
objType = 0
useSimplePhysics = False
bDelayedLoad = False
# Don't know the name yet of this
field8_0x20 = True

408
ComponentFactories.py Normal file
View File

@@ -0,0 +1,408 @@
import ComponentDefinitions as cd
def ApplyComponentWhitelist():
# Dont care to actually do this, just want the call for now.
pass
def ControllablePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
if not loadObject.hasDropEffect:
if not loadObject.useSimplePhysics:
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
else:
loadObject.physicsComponentId = componentId
def RenderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if loadObject.lotToSpawn == 176:
return
gmlevel = loadObject.configData.get('gmlevel', 0)
if gmlevel > 0 and gmlevel < 6:
componentId = 9940 # Override the componentId of the render component to be 9940, the Mythran Render Component
renderDisabled = loadObject.configData.get('renderDisabled', False)
if renderDisabled:
return
if objectLoadState.createdGameObject.type == 'BlockingVolume':
return
rejectedLots = [5937, 5938, 9741, 9742, 9862, 9863]
if loadObject.lotToSpawn in rejectedLots:
return
if loadObject.lotToSpawn == 6368:
FxFactory(loadObject, objectLoadState, componentId)
return
FxFactory(loadObject, objectLoadState, componentId)
defaultWrapperAsset = ""
hasNifAsset = False
hasKfmAsset = False
allowRenderWrapping = False
nifName = loadObject.configData.get('nif_name', '')
if nifName == '':
print('TODO query cdc for this data in the Render component')
nifName = 'mesh/something.nif' # if empty get nifname as the render_asset column of the RenderComponent table from the database
if nifName[-1] == 'f':
hasNifAsset = True
elif nifName[-1] == 'm':
hasKfmAsset = True
if defaultWrapperAsset != '':
if defaultWrapperAsset[-1] == 'm':
allowRenderWrapping = not loadObject.configData.get('preventRenderWrapping', False)
if allowRenderWrapping or hasKfmAsset or (not defaultWrapperAsset == 'PrimitiveModels' and not hasNifAsset and not loadObject.configData.get('CreateNULLRender', False)):
objectLoadState.components.append('SKINNED_RENDER')
else:
objectLoadState.components.append('RENDER')
def FxFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('FX')
def SimplePhysFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
objectLoadState.physicsComponentId = componentId
return
if not loadObject.hasDropEffect:
return
markedAsPhantom = loadObject.configData.get('markedAsPhantom', False)
if markedAsPhantom:
objectLoadState.components.append('PHANTOM_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def CharacterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSOR')
objectLoadState.components.append('MOUNT_CONTROL')
objectLoadState.components.append('PET_CREATOR')
objectLoadState.components.append('LEVEL_PROGRESSION')
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
objectLoadState.components.append('CHARACTER')
def ScriptFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
custom_script_client = loadObject.configData.get('custom_script_client', '')
custom_script_server = loadObject.configData.get('custom_script_server', '')
if componentId == 0 and custom_script_client == '' and custom_script_server == '':
return
objectLoadState.components.append('SCRIPT')
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
def BouncerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BOUNCER')
def DestroyableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('STATUS_EFFECT')
objectLoadState.components.append('BUFF')
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
def SkillFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SKILL')
def SpawnerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SPAWNER')
# If game state is happy flower
spawntemplate = loadObject.configData.get('spawntemplate', '')
print('TODO query cdc for this data in the Spawner component')
render_asset = loadObject.configData.get('render_asset', 'test.nif') # Get this from RenderComponent table
render_id = loadObject.configData.get('render_id', '') # Get this from RenderComponent table
if render_asset[-1] == 'f':
objectLoadState.components.append('SKINNED_RENDER')
else:
objectLoadState.components.append('RENDER')
def ItemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ITEM')
def ModularBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULAR_BUILD')
def BuildControllerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BUILD_CONTROLLER')
def VendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('VENDOR')
def InventoryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('INVENTORY')
def ProjectilePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not loadObject.hasDropEffect:
objectLoadState.components.append('PROJECTILE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def ShootingGalleryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SHOOTING_GALLERY')
def RigibBodyPhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RIGID_BODY_PHANTOM_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def ChestFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHEST')
def CollectibleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('COLLECTIBLE')
if not objectLoadState.hasDestroyable:
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
def BlueprintFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BLUEPRINT')
def MovingPlatformFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MOVING_PLATFORM')
def PetFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
inInventory = loadObject.configData.get('inInventory', False)
if inInventory:
return
if not objectLoadState.hasPathfindingComponent:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
objectLoadState.components.append('PET')
def PlatformBoundaryFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PLATFORM_BOUNDARY')
def ModuleFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULE')
def ArcadeFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ARCADE')
def HavokVehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
use_simple_physics = loadObject.configData.get('use_simple_physics', False)
if use_simple_physics:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
else:
objectWorldState = loadObject.configData.get('objectWorldState', '')
if objectWorldState == 0 or objectWorldState != 2:
objectLoadState.components.append('HAVOK_VEHICLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def MovementAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MOVEMENT_AI')
def ExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Sorry aron, doesnt do anything :(
pass
def MinifigFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MINIFIG')
def PropertyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY')
def ModelBuilderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODEL_BUILDER')
def ScriptedActivityFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SCRIPTED_ACTIVITY')
def PhantomPhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not loadObject.hasDropEffect:
objectLoadState.components.append('PHANTOM_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def SpringPadFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SPRING_PAD')
def ModelFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
modelType = loadObject.configData.get('modelType', 0)
if not objectLoadState.hasModelbehaviors:
if not objectLoadState.hasPhysicsComponent:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
elif not objectLoadState.hasPhysicsComponent:
if modelType == 0:
objectLoadState.components.append('CONTROLLABLE_PHYSICS')
else:
objectLoadState.components.append('SIMPLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
if modelType != 0 and not objectLoadState.hasPathfindingComponent:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
hasPropertyObjectID = loadObject.configData.get('propertyObjectID', 0) != 0
inInventory = loadObject.configData.get('inInventory', False)
if hasPropertyObjectID and inInventory:
objectLoadState.components.append('MODEL_BEHAVIORS')
else:
objectLoadState.components.append('MUTABLE_MODEL_BEHAVIORS')
def PropertyEntranceFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_ENTRANCE')
def PropertyManagementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_MANAGEMENT')
def VehiclePhysicsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('VEHICLE_PHYSICS')
objectLoadState.hasPhysicsComponent = True
def PhysicsSystemFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasModelbehaviors:
if not loadObject.hasDropEffect:
objectLoadState.components.append('PHYSICS_SYSTEM')
objectLoadState.hasPhysicsComponent = True
def RebuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('REBUILD')
if not objectLoadState.hasDestroyable:
objectLoadState.components.append('DESTROYABLE')
objectLoadState.hasDestroyable = True
loadObject.bIsSmashable = True
def BaseCombatAiFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int, add_pathfinding_control: bool):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if not objectLoadState.hasPathfindingComponent and add_pathfinding_control:
objectLoadState.components.append('PATHFINDING_CONTROL')
objectLoadState.hasPathfindingComponent = True
objectLoadState.components.append('BASE_COMBAT_AI')
def ProximityMonitorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
print('TODO query cdc for this data in prox mon')
# There must be a row for this component in the ProximityMonitorComponent table to get this component
LoadOnClient = loadObject.configData.get('LoadOnClient', False) # Get this from ProximityMonitorComponent table if you know there is a column for it
if LoadOnClient:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
def DonationVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('DONATION_VENDOR')
def AchievmentVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
objectLoadState.components.append('ACHIEVEMENT_VENDOR')
def CraftingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Game verson must be above 999 for this to work!
gameVersion = 1000
if gameVersion > 999:
objectLoadState.components.append('CRAFTING')
# Technically Possessable and Possessor are the exact same thing
def PossessableFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSABLE')
def PossessorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('POSSESSOR')
def PropertyPlaqueFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_PLAQUE')
def BuildBorderFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BUILD_BORDER')
def CullingPlaneFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CULLING_PLANE')
def GateRushFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('GATE_RUSH')
def RailActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RAIL_ACTIVATOR')
def RollerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROLLER')
def PlayerForcedMovementFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PLAYER_FORCED_MOVEMENT')
def RacingSoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_SOUND_TRIGGER')
def RocketLaunchLupFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LAUNCH_LUP')
def ModuleAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MODULE_ASSEMBLY')
def ShowcaseModelHandlerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SHOWCASE_MODEL_HANDLER')
def GenericActivatorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('GENERIC_ACTIVATOR')
def PropertyVendorFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('PROPERTY_VENDOR')
def RocketLaunchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LAUNCH')
def RocketLandingComponentFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ROCKET_LANDING_COMPONENT')
def RacingControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_CONTROL')
def FactionTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('FACTION_TRIGGER')
def MissionOfferFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('MISSION_OFFER')
objectLoadState.hasMissionOffer = True
def RacingStatsFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('RACING_STATS')
def LupExhibitFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('LUP_EXHIBIT')
def BBBFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('BBB')
def SoundTriggerFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_TRIGGER')
def SwitchFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SWITCH')
def ZoneControlFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('ZONE_CONTROL')
def ChanglingFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHANGELING')
def ChoiceBuildFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CHOICE_BUILD')
def PackageFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
# Literally returns nothing
pass
def SoundRepeaterFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_REPEATER')
def SoundAmbient2DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
if loadObject.isLocalPlayer:
objectLoadState.components.append('SOUND_AMBIENT_2D')
def SoundAmbient3DFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('SOUND_AMBIENT_3D')
def CustomBuildAssemblyFactory(loadObject: cd.LoadObject, objectLoadState: cd.ObjectLoadState, componentId: int):
objectLoadState.components.append('CUSTOM_BUILD_ASSEMBLY')

306
DoObjectPreLoad.py Normal file
View File

@@ -0,0 +1,306 @@
import ComponentDefinitions as cd
import ComponentFactories as cf
# If we have any of these components, we need pathfinding control
def CheckIfNeedsPathfindingControl(components_to_load: dict):
add_pathfinding_control = cd.components['COMPONENT_TYPE_CONTROLLABLE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_PROJECTILE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS'] in components_to_load
add_pathfinding_control |= cd.components['COMPONENT_TYPE_VEHICLE_PHYSICS'] in components_to_load
return add_pathfinding_control
def DoObjectPreLoad(components_to_load: dict):
isHfActive = False
cf.ApplyComponentWhitelist()
objectLoadState = cd.ObjectLoadState()
loadObject = cd.LoadObject()
for componentType in cd.component_order_as_ints.keys():
try:
componentId = components_to_load[componentType]
except KeyError:
continue
try:
match cd.component_int_to_name[componentType]:
case 'COMPONENT_TYPE_CONTROLLABLE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.ControllablePhysFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RENDER':
cf.RenderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SIMPLE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.SimplePhysFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHARACTER':
cf.CharacterFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SCRIPT':
cf.ScriptFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
case 'COMPONENT_TYPE_BOUNCER':
cf.BouncerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_DESTROYABLE':
cf.DestroyableFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SKILL':
cf.SkillFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SPAWNER':
cf.SpawnerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ITEM':
cf.ItemFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODULAR_BUILD':
cf.ModularBuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BUILD_CONTROLLER':
cf.BuildControllerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_VENDOR':
cf.VendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_INVENTORY':
cf.InventoryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROJECTILE_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.ProjectilePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SHOOTING_GALLERY':
cf.ShootingGalleryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RIGID_BODY_PHANTOM_PHYSICS':
cf.RigibBodyPhantomPhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHEST':
cf.ChestFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_COLLECTIBLE':
cf.CollectibleFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BLUEPRINT':
cf.BlueprintFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MOVING_PLATFORM':
cf.MovingPlatformFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PET':
cf.PetFactory(loadObject, objectLoadState, componentId)
print('Deleting COMPONENT_TYPE_MODEL and COMPONENT_TYPE_ITEM due to COMPONENT_TYPE_PET being loaded')
toDel = cd.components['COMPONENT_TYPE_MODEL']
del components_to_load[toDel]
toDel = cd.components['COMPONENT_TYPE_ITEM']
del components_to_load[toDel]
case 'COMPONENT_TYPE_PLATFORM_BOUNDARY':
cf.PlatformBoundaryFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODULE':
cf.ModuleFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ARCADE':
cf.ArcadeFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_HAVOK_VEHICLE_PHYSICS':
cf.HavokVehiclePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MOVEMENT_AI':
cf.MovementAiFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_EXHIBIT':
cf.ExhibitFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MINIFIG':
cf.MinifigFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY':
cf.PropertyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODEL_BUILDER':
cf.ModelBuilderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SCRIPTED_ACTIVITY':
cf.ScriptedActivityFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PHANTOM_PHYSICS':
if objectLoadState.hasDroppedLootComponent: continue
cf.PhantomPhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SPRING_PAD':
cf.SpringPadFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MODEL':
cf.ModelFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_ENTRANCE':
cf.PropertyEntranceFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_MANAGEMENT':
cf.PropertyManagementFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_VEHICLE_PHYSICS':
cf.VehiclePhysicsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PHYSICS_SYSTEM':
cf.PhysicsSystemFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_REBUILD':
cf.RebuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SWITCH':
cf.SwitchFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ZONE_CONTROL':
cf.ZoneControlFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHANGELING':
cf.ChanglingFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CHOICE_BUILD':
cf.ChoiceBuildFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PACKAGE':
cf.PackageFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_REPEATER':
cf.SoundRepeaterFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_AMBIENT_2D':
cf.SoundAmbient2DFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_AMBIENT_3D':
cf.SoundAmbient3DFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CUSTOM_BUILD_ASSEMBLY':
cf.CustomBuildAssemblyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BASE_COMBAT_AI':
cf.BaseCombatAiFactory(loadObject, objectLoadState, componentId, CheckIfNeedsPathfindingControl(components_to_load))
case 'COMPONENT_TYPE_MODULE_ASSEMBLY':
cf.ModuleAssemblyFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SHOWCASE_MODEL_HANDLER':
cf.ShowcaseModelHandlerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_GENERIC_ACTIVATOR':
cf.GenericActivatorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_VENDOR':
cf.PropertyVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LAUNCH':
cf.RocketLaunchFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LANDING_COMPONENT':
cf.RocketLandingComponentFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_CONTROL':
cf.RacingControlFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_FACTION_TRIGGER':
cf.FactionTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_MISSION_OFFER':
cf.MissionOfferFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_STATS':
cf.RacingStatsFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_LUP_EXHIBIT':
cf.LupExhibitFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BBB':
cf.BBBFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_SOUND_TRIGGER':
cf.SoundTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROXIMITY_MONITOR':
cf.ProximityMonitorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RACING_SOUND_TRIGGER':
cf.RacingSoundTriggerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROCKET_LAUNCH_LUP':
cf.RocketLaunchLupFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_DONATION_VENDOR':
cf.DonationVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ACHIEVEMENT_VENDOR':
cf.AchievmentVendorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_GATE_RUSH':
cf.GateRushFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_RAIL_ACTIVATOR':
cf.RailActivatorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_ROLLER':
cf.RollerFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PLAYER_FORCED_MOVEMENT':
cf.PlayerForcedMovementFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CRAFTING':
cf.CraftingFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_POSSESSABLE':
cf.PossessableFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_POSSESSOR':
cf.PossessorFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_PROPERTY_PLAQUE':
cf.PropertyPlaqueFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_BUILD_BORDER':
cf.BuildBorderFactory(loadObject, objectLoadState, componentId)
case 'COMPONENT_TYPE_CULLING_PLANE':
cf.CullingPlaneFactory(loadObject, objectLoadState, componentId)
except KeyError:
print(f'Unknown component: {componentType}')
if not 'is_smashable' in loadObject.configData and loadObject.bIsSmashable:
loadObject.configData['is_smashable'] = True
is_smashable = loadObject.configData['is_smashable']
if is_smashable and not objectLoadState.hasDestroyable and loadObject.lotToSpawn != 176:
objectLoadState.components.append('DESTROYABLE')
if objectLoadState.hasMissionOffer and not objectLoadState.hasProximityMonitorComponent:
objectLoadState.components.append('PROXIMITY_MONITOR')
objectLoadState.hasProximityMonitorComponent = True
if loadObject.field8_0x20 and loadObject.owner != 0:
objectLoadState.components.append('OVERHEAD_ICON')
trigger_id = loadObject.configData.get('trigger_id', 0)
if trigger_id != 0:
objectLoadState.components.append('TRIGGER')
return objectLoadState
components_to_load = {}
# Testing code to load all components
for i in range(110):
components_to_load[i] = i
objectLoadState = DoObjectPreLoad(components_to_load)
# Convert the components to ints
components_to_int = []
for component in objectLoadState.components:
components_to_int.append([cd.components[f'COMPONENT_TYPE_{component}'], component])
# Print out the components in a more readable format
for comp in components_to_int:
print(comp[0], comp[1])

View File

@@ -179,7 +179,7 @@ If you would like to build the server faster, append `-j<number>` where number i
### Notes
Depending on your operating system, you may need to adjust some pre-processor defines in [CMakeVariables.txt](./CMakeVariables.txt) before building:
* If you are on MacOS, ensure OPENSSL_ROOT_DIR is pointing to the openssl root directory.
* If you are using a Darkflame Universe client, ensure NET_VERSION is changed to 171023.
* If you are using a Darkflame Universe client, ensure `client_net_version` in `build/sharedconfig.ini` is changed to 171023.
## Configuring your server
This server has a few steps that need to be taken to configure the server for your use case.

Binary file not shown.

Binary file not shown.

View File

@@ -3,6 +3,7 @@
#include "Database.h"
#include <vector>
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "Game.h"
#include "dServer.h"
#include "GeneralUtils.h"
@@ -75,11 +76,11 @@ void ChatPacketHandler::HandleFriendlistRequest(Packet* packet) {
//Now, we need to send the friendlist to the server they came from:
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(playerID);
//portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::GET_FRIENDS_LIST_RESPONSE);
bitStream.Write<uint8_t>(0);
bitStream.Write<uint16_t>(1); //Length of packet -- just writing one as it doesn't matter, client skips it.
bitStream.Write((uint16_t)friends.size());
@@ -412,21 +413,21 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
const auto otherName = std::string(otherMember->playerName.c_str());
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(otherMember->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(otherMember->playerID);
bitStream.Write<uint8_t>(8);
bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(senderName, 33, &bitStream);
bitStream.Write(LUWString(senderName));
bitStream.Write(sender->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(otherName, 33, &bitStream);
bitStream.Write(LUWString(otherName));
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //teams?
PacketUtils::WritePacketWString(message, 512, &bitStream);
bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = otherMember->sysAddr;
SEND_PACKET;
@@ -434,7 +435,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) {
}
void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
LWOOBJID senderID = PacketUtils::ReadPacketS64(0x08, packet);
LWOOBJID senderID = PacketUtils::ReadS64(0x08, packet);
std::string receiverName = PacketUtils::ReadString(0x66, packet, true);
std::string message = PacketUtils::ReadString(0xAA, packet, true, 512);
@@ -451,21 +452,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the sender:
{
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonA->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(goonAName, 33, &bitStream);
bitStream.Write(LUWString(goonAName));
bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(goonBName, 33, &bitStream);
bitStream.Write(LUWString(goonBName));
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(0); //success
PacketUtils::WritePacketWString(message, 512, &bitStream);
bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = goonA->sysAddr;
SEND_PACKET;
@@ -474,21 +475,21 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) {
//To the receiver:
{
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(goonB->playerID);
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, eChatMessageType::PRIVATE_CHAT_MESSAGE);
bitStream.Write(goonA->playerID);
bitStream.Write<uint8_t>(7);
bitStream.Write<unsigned int>(69);
PacketUtils::WritePacketWString(goonAName, 33, &bitStream);
bitStream.Write(LUWString(goonAName));
bitStream.Write(goonA->playerID);
bitStream.Write<uint16_t>(0);
bitStream.Write<uint8_t>(0); //not mythran nametag
PacketUtils::WritePacketWString(goonBName, 33, &bitStream);
bitStream.Write(LUWString(goonBName));
bitStream.Write<uint8_t>(0); //not mythran for receiver
bitStream.Write<uint8_t>(3); //new whisper
PacketUtils::WritePacketWString(message, 512, &bitStream);
bitStream.Write(LUWString(message, 512));
SystemAddress sysAddr = goonB->sysAddr;
SEND_PACKET;
@@ -709,13 +710,13 @@ void ChatPacketHandler::HandleTeamStatusRequest(Packet* packet) {
void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::TEAM_INVITE);
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write(LUWString(sender->playerName.c_str()));
bitStream.Write(sender->playerID);
SystemAddress sysAddr = receiver->sysAddr;
@@ -724,7 +725,7 @@ void ChatPacketHandler::SendTeamInvite(PlayerData* receiver, PlayerData* sender)
void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeaderIsFreeTrial, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, uint8_t ucResponseCode, std::u16string wsLeaderName) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -751,7 +752,7 @@ void ChatPacketHandler::SendTeamInviteConfirm(PlayerData* receiver, bool bLeader
void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderID, LWOZONEID i64LeaderZoneID, uint8_t ucLootFlag, uint8_t ucNumOfOtherPlayers, std::u16string wsLeaderName) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -776,7 +777,7 @@ void ChatPacketHandler::SendTeamStatus(PlayerData* receiver, LWOOBJID i64LeaderI
void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64PlayerID) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -793,7 +794,7 @@ void ChatPacketHandler::SendTeamSetLeader(PlayerData* receiver, LWOOBJID i64Play
void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTrial, bool bLocal, bool bNoLootOnDeath, LWOOBJID i64PlayerID, std::u16string wsPlayerName, LWOZONEID zoneID) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -822,7 +823,7 @@ void ChatPacketHandler::SendTeamAddPlayer(PlayerData* receiver, bool bIsFreeTria
void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband, bool bIsKicked, bool bIsLeaving, bool bLocal, LWOOBJID i64LeaderID, LWOOBJID i64PlayerID, std::u16string wsPlayerName) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -848,7 +849,7 @@ void ChatPacketHandler::SendTeamRemovePlayer(PlayerData* receiver, bool bDisband
void ChatPacketHandler::SendTeamSetOffWorldFlag(PlayerData* receiver, LWOOBJID i64PlayerID, LWOZONEID zoneID) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
@@ -882,16 +883,16 @@ void ChatPacketHandler::SendFriendUpdate(PlayerData* friendData, PlayerData* pla
[bool] - is FTP*/
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(friendData->playerID);
//portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::UPDATE_FRIEND_NOTIFY);
bitStream.Write<uint8_t>(notifyType);
std::string playerName = playerData->playerName.c_str();
PacketUtils::WritePacketWString(playerName, 33, &bitStream);
bitStream.Write(LUWString(playerName));
bitStream.Write(playerData->zoneID.GetMapID());
bitStream.Write(playerData->zoneID.GetInstanceID());
@@ -921,12 +922,12 @@ void ChatPacketHandler::SendFriendRequest(PlayerData* receiver, PlayerData* send
}
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_REQUEST);
bitStream.Write(LUWString(sender->playerName.c_str()));
bitStream.Write<uint8_t>(0); // This is a BFF flag however this is unused in live and does not have an implementation client side.
SystemAddress sysAddr = receiver->sysAddr;
@@ -937,16 +938,16 @@ void ChatPacketHandler::SendFriendResponse(PlayerData* receiver, PlayerData* sen
if (!receiver || !sender) return;
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
// Portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::ADD_FRIEND_RESPONSE);
bitStream.Write(responseCode);
// For all requests besides accepted, write a flag that says whether or not we are already best friends with the receiver.
bitStream.Write<uint8_t>(responseCode != eAddFriendResponseType::ACCEPTED ? isBestFriendsAlready : sender->sysAddr != UNASSIGNED_SYSTEM_ADDRESS);
// Then write the player name
PacketUtils::WritePacketWString(sender->playerName.c_str(), 33, &bitStream);
bitStream.Write(LUWString(sender->playerName.c_str()));
// Then if this is an acceptance code, write the following extra info.
if (responseCode == eAddFriendResponseType::ACCEPTED) {
bitStream.Write(sender->playerID);
@@ -962,13 +963,13 @@ void ChatPacketHandler::SendRemoveFriend(PlayerData* receiver, std::string& pers
if (!receiver) return;
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::ROUTE_TO_PLAYER);
bitStream.Write(receiver->playerID);
//portion that will get routed:
PacketUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, eClientMessageType::REMOVE_FRIEND_RESPONSE);
bitStream.Write<uint8_t>(isSuccessful); //isOnline
PacketUtils::WritePacketWString(personToRemove, 33, &bitStream);
bitStream.Write(LUWString(personToRemove));
SystemAddress sysAddr = receiver->sysAddr;
SEND_PACKET;

View File

@@ -33,6 +33,7 @@ namespace Game {
dChatFilter* chatFilter = nullptr;
AssetManager* assetManager = nullptr;
bool shouldShutdown = false;
std::mt19937 randomEngine;
}
@@ -114,6 +115,8 @@ int main(int argc, char** argv) {
Game::server = new dServer(Game::config->GetValue("external_ip"), ourPort, 0, maxClients, false, true, Game::logger, masterIP, masterPort, ServerType::Chat, Game::config, &Game::shouldShutdown);
Game::chatFilter = new dChatFilter(Game::assetManager->GetResPath().string() + "/chatplus_en_us", bool(std::stoi(Game::config->GetValue("dont_generate_dcf"))));
Game::randomEngine = std::mt19937(time(0));
//Run it until server gets a kill message from Master:
auto t = std::chrono::high_resolution_clock::now();

View File

@@ -6,10 +6,12 @@
#include "dLogger.h"
#include "ChatPacketHandler.h"
#include "GeneralUtils.h"
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include "Database.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "ChatPackets.h"
#include "dConfig.h"
PlayerContainer::PlayerContainer() {
}
@@ -18,6 +20,10 @@ PlayerContainer::~PlayerContainer() {
mPlayers.clear();
}
TeamData::TeamData() {
lootFlag = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
void PlayerContainer::InsertPlayer(Packet* packet) {
CINSTREAM_SKIP_HEADER;
PlayerData* data = new PlayerData();
@@ -146,7 +152,7 @@ void PlayerContainer::CreateTeamServer(Packet* packet) {
void PlayerContainer::BroadcastMuteUpdate(LWOOBJID player, time_t time) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::MUTE_UPDATE);
bitStream.Write(player);
bitStream.Write(time);
@@ -207,6 +213,14 @@ TeamData* PlayerContainer::GetTeam(LWOOBJID playerID) {
}
void PlayerContainer::AddMember(TeamData* team, LWOOBJID playerID) {
if (team->memberIDs.size() >= 4){
Game::logger->Log("PlayerContainer", "Tried to add player to team that already had 4 players");
auto* player = GetPlayerData(playerID);
if (!player) return;
ChatPackets::SendSystemMessage(player->sysAddr, u"The teams is full! You have not been added to a team!");
return;
}
const auto index = std::find(team->memberIDs.begin(), team->memberIDs.end(), playerID);
if (index != team->memberIDs.end()) return;
@@ -345,7 +359,7 @@ void PlayerContainer::TeamStatusUpdate(TeamData* team) {
void PlayerContainer::UpdateTeamsOnWorld(TeamData* team, bool deleteTeam) {
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::TEAM_UPDATE);
bitStream.Write(team->teamID);
bitStream.Write(deleteTeam);

View File

@@ -18,6 +18,7 @@ struct PlayerData {
};
struct TeamData {
TeamData();
LWOOBJID teamID = LWOOBJID_EMPTY; // Internal use
LWOOBJID leaderID = LWOOBJID_EMPTY;
std::vector<LWOOBJID> memberIDs{};

View File

@@ -12,7 +12,7 @@ set(DCOMMON_SOURCES
"NiPoint3.cpp"
"NiQuaternion.cpp"
"SHA512.cpp"
"Type.cpp"
"Demangler.cpp"
"ZCompression.cpp"
"BrickByBrickFix.cpp"
"BinaryPathFinder.cpp"

29
dCommon/Demangler.cpp Normal file
View File

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

9
dCommon/Demangler.h Normal file
View File

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

View File

@@ -107,7 +107,7 @@ static void ErrorCallback(void* data, const char* msg, int errnum) {
}
#endif
#include "Type.h"
#include "Demangler.h"
void GenerateDump() {
std::string cmd = "sudo gcore " + std::to_string(getpid());
@@ -122,41 +122,43 @@ void CatchUnhandled(int sig) {
if (Diagnostics::GetProduceMemoryDump()) {
GenerateDump();
}
void* array[10];
constexpr uint8_t MaxStackTrace = 32;
void* array[MaxStackTrace];
size_t size;
// get void*'s for all entries on the stack
size = backtrace(array, 10);
size = backtrace(array, MaxStackTrace);
#if defined(__GNUG__) and defined(__dynamic)
# if defined(__GNUG__)
// Loop through the returned addresses, and get the symbols to be demangled
char** strings = backtrace_symbols(array, size);
// Print the stack trace
for (size_t i = 0; i < size; i++) {
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]' and extract the function name
// Take a string like './WorldServer(_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress+0x6187) [0x55869c44ecf7]'
// and extract '_ZN19SlashCommandHandler17HandleChatCommandERKSbIDsSt11char_traitsIDsESaIDsEEP6EntityRK13SystemAddress' from it to be demangled into a proper name
std::string functionName = strings[i];
std::string::size_type start = functionName.find('(');
std::string::size_type end = functionName.find('+');
if (start != std::string::npos && end != std::string::npos) {
std::string demangled = functionName.substr(start + 1, end - start - 1);
demangled = demangle(functionName.c_str());
demangled = Demangler::Demangle(demangled.c_str());
if (demangled.empty()) {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, demangled.c_str());
} else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
// If the demangled string is not empty, then we can replace the mangled string with the demangled one
if (!demangled.empty()) {
demangled.push_back('(');
demangled += functionName.substr(end);
functionName = demangled;
}
} else {
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
}
Game::logger->Log("Diagnostics", "[%02zu] %s", i, functionName.c_str());
}
#else
# else // defined(__GNUG__)
backtrace_symbols_fd(array, size, STDOUT_FILENO);
#endif
# endif // defined(__GNUG__)
FILE* file = fopen(fileName.c_str(), "w+");
if (file != NULL) {
@@ -166,7 +168,7 @@ void CatchUnhandled(int sig) {
fclose(file);
}
#else
#else // __include_backtrace__
struct backtrace_state* state = backtrace_create_state(
Diagnostics::GetProcessFileName().c_str(),
@@ -177,7 +179,7 @@ void CatchUnhandled(int sig) {
struct bt_ctx ctx = { state, 0 };
Bt(state);
#endif
#endif // __include_backtrace__
exit(EXIT_FAILURE);
}

View File

@@ -126,6 +126,11 @@ namespace GeneralUtils {
template <typename T>
T Parse(const char* value);
template <>
inline bool Parse(const char* value) {
return std::stoi(value);
}
template <>
inline int32_t Parse(const char* value) {
return std::stoi(value);

View File

@@ -61,35 +61,33 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
}
case LDF_TYPE_S32: {
try {
int32_t data = static_cast<int32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<int32_t>(key, data);
} catch (std::exception) {
int32_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid int32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<int32_t>(key, data);
break;
}
case LDF_TYPE_FLOAT: {
try {
float data = strtof(ldfTypeAndValue.second.data(), &storage);
returnValue = new LDFData<float>(key, data);
} catch (std::exception) {
float data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid float value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<float>(key, data);
break;
}
case LDF_TYPE_DOUBLE: {
try {
double data = strtod(ldfTypeAndValue.second.data(), &storage);
returnValue = new LDFData<double>(key, data);
} catch (std::exception) {
double data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid double value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<double>(key, data);
break;
}
@@ -102,9 +100,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = 0;
} else {
try {
data = static_cast<uint32_t>(strtoul(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint32 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
@@ -122,9 +118,7 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
} else if (ldfTypeAndValue.second == "false") {
data = false;
} else {
try {
data = static_cast<bool>(strtol(ldfTypeAndValue.second.data(), &storage, 10));
} catch (std::exception) {
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid bool value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
@@ -135,24 +129,22 @@ LDFBaseData* LDFBaseData::DataFromString(const std::string_view& format) {
}
case LDF_TYPE_U64: {
try {
uint64_t data = static_cast<uint64_t>(strtoull(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<uint64_t>(key, data);
} catch (std::exception) {
uint64_t data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid uint64 value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<uint64_t>(key, data);
break;
}
case LDF_TYPE_OBJID: {
try {
LWOOBJID data = static_cast<LWOOBJID>(strtoll(ldfTypeAndValue.second.data(), &storage, 10));
returnValue = new LDFData<LWOOBJID>(key, data);
} catch (std::exception) {
LWOOBJID data;
if (!GeneralUtils::TryParse(ldfTypeAndValue.second.data(), data)) {
Game::logger->Log("LDFFormat", "Warning: Attempted to process invalid LWOOBJID value (%s) from string (%s)", ldfTypeAndValue.second.data(), format.data());
return nullptr;
}
returnValue = new LDFData<LWOOBJID>(key, data);
break;
}

View File

@@ -129,10 +129,19 @@ NiPoint3 NiPoint3::operator+(const NiPoint3& point) const {
}
//! Operator for addition of vectors
NiPoint3 NiPoint3::operator+=(const NiPoint3& point) const {
return NiPoint3(this->x + point.x, this->y + point.y, this->z + point.z);
NiPoint3& NiPoint3::operator+=(const NiPoint3& point) {
this->x += point.x;
this->y += point.y;
this->z += point.z;
return *this;
}
NiPoint3& NiPoint3::operator*=(const float scalar) {
this->x *= scalar;
this->y *= scalar;
this->z *= scalar;
return *this;
}
//! Operator for subtraction of vectors
NiPoint3 NiPoint3::operator-(const NiPoint3& point) const {

View File

@@ -136,7 +136,9 @@ public:
NiPoint3 operator+(const NiPoint3& point) const;
//! Operator for addition of vectors
NiPoint3 operator+=(const NiPoint3& point) const;
NiPoint3& operator+=(const NiPoint3& point);
NiPoint3& operator*=(const float scalar);
//! Operator for subtraction of vectors
NiPoint3 operator-(const NiPoint3& point) const;

View File

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

View File

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

View File

@@ -42,7 +42,7 @@ struct AssetMemoryBuffer : std::streambuf {
}
void close() {
delete m_Base;
free(m_Base);
}
};

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -101,7 +101,7 @@ enum class eReplicaComponentType : uint32_t {
TRADE,
USER_CONTROL,
IGNORE_LIST,
ROCKET_LAUNCH_LUP,
MULTI_ZONE_ENTRANCE,
BUFF_REAL, // the real buff component, should just be name BUFF
INTERACTION_MANAGER,
DONATION_VENDOR,

View File

@@ -55,7 +55,7 @@ CDClientManager::CDClientManager() {
CDBehaviorParameterTable::Instance().LoadValuesFromDatabase();
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
CDComponentsRegistryTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDComponentsRegistryTable::Instance().LoadValuesFromDatabase());
CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
CDEmoteTableTable::Instance().LoadValuesFromDatabase();
@@ -65,8 +65,8 @@ CDClientManager::CDClientManager() {
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
CDItemSetsTable::Instance().LoadValuesFromDatabase();
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
CDLootMatrixTable::Instance().LoadValuesFromDatabase();
CDLootTableTable::Instance().LoadValuesFromDatabase();
CDCLIENT_DONT_CACHE_TABLE(CDLootMatrixTable::Instance().LoadValuesFromDatabase());
CDCLIENT_DONT_CACHE_TABLE(CDLootTableTable::Instance().LoadValuesFromDatabase());
CDMissionEmailTable::Instance().LoadValuesFromDatabase();
CDMissionNPCComponentTable::Instance().LoadValuesFromDatabase();
CDMissionTasksTable::Instance().LoadValuesFromDatabase();

View File

@@ -1,26 +1,30 @@
#include "CDBehaviorParameterTable.h"
#include "GeneralUtils.h"
uint64_t GetKey(const uint32_t behaviorID, const uint32_t parameterID) {
uint64_t key = behaviorID;
key <<= 31U;
key |= parameterID;
return key;
}
void CDBehaviorParameterTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter");
uint32_t uniqueParameterId = 0;
uint64_t hash = 0;
while (!tableData.eof()) {
CDBehaviorParameter entry;
entry.behaviorID = tableData.getIntField("behaviorID", -1);
uint32_t behaviorID = tableData.getIntField("behaviorID", -1);
auto candidateStringToAdd = std::string(tableData.getStringField("parameterID", ""));
auto parameter = m_ParametersList.find(candidateStringToAdd);
uint32_t parameterId;
if (parameter != m_ParametersList.end()) {
entry.parameterID = parameter;
parameterId = parameter->second;
} else {
entry.parameterID = m_ParametersList.insert(std::make_pair(candidateStringToAdd, uniqueParameterId)).first;
uniqueParameterId++;
parameterId = m_ParametersList.insert(std::make_pair(candidateStringToAdd, m_ParametersList.size())).first->second;
}
hash = entry.behaviorID;
hash = (hash << 31U) | entry.parameterID->second;
entry.value = tableData.getFloatField("value", -1.0f);
uint64_t hash = GetKey(behaviorID, parameterId);
float value = tableData.getFloatField("value", -1.0f);
m_Entries.insert(std::make_pair(hash, entry));
m_Entries.insert(std::make_pair(hash, value));
tableData.nextRow();
}
@@ -30,25 +34,21 @@ void CDBehaviorParameterTable::LoadValuesFromDatabase() {
float CDBehaviorParameterTable::GetValue(const uint32_t behaviorID, const std::string& name, const float defaultValue) {
auto parameterID = this->m_ParametersList.find(name);
if (parameterID == this->m_ParametersList.end()) return defaultValue;
uint64_t hash = behaviorID;
hash = (hash << 31U) | parameterID->second;
auto hash = GetKey(behaviorID, parameterID->second);
// Search for specific parameter
const auto& it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second.value : defaultValue;
auto it = m_Entries.find(hash);
return it != m_Entries.end() ? it->second : defaultValue;
}
std::map<std::string, float> CDBehaviorParameterTable::GetParametersByBehaviorID(uint32_t behaviorID) {
uint64_t hashBase = behaviorID;
std::map<std::string, float> returnInfo;
uint64_t hash;
for (auto& parameterCandidate : m_ParametersList) {
hash = (hashBase << 31U) | parameterCandidate.second;
for (auto& [parameterString, parameterId] : m_ParametersList) {
uint64_t hash = GetKey(hashBase, parameterId);
auto infoCandidate = m_Entries.find(hash);
if (infoCandidate != m_Entries.end()) {
returnInfo.insert(std::make_pair(infoCandidate->second.parameterID->first, infoCandidate->second.value));
returnInfo.insert(std::make_pair(parameterString, infoCandidate->second));
}
}
return returnInfo;

View File

@@ -5,15 +5,11 @@
#include <unordered_map>
#include <unordered_set>
struct CDBehaviorParameter {
unsigned int behaviorID; //!< The Behavior ID
std::unordered_map<std::string, uint32_t>::iterator parameterID; //!< The Parameter ID
float value; //!< The value of the behavior template
};
class CDBehaviorParameterTable : public CDTable<CDBehaviorParameterTable> {
private:
std::unordered_map<uint64_t, CDBehaviorParameter> m_Entries;
typedef uint64_t BehaviorParameterHash;
typedef float BehaviorParameterValue;
std::unordered_map<BehaviorParameterHash, BehaviorParameterValue> m_Entries;
std::unordered_map<std::string, uint32_t> m_ParametersList;
public:
void LoadValuesFromDatabase();

View File

@@ -11,6 +11,7 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
entry.component_id = tableData.getIntField("component_id", -1);
this->mappedEntries.insert_or_assign(((uint64_t)entry.component_type) << 32 | ((uint64_t)entry.id), entry.component_id);
this->mappedEntries.insert_or_assign(entry.id, 0);
tableData.nextRow();
}
@@ -19,19 +20,18 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
}
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
if (iter == this->mappedEntries.end()) {
return defaultValue;
auto exists = mappedEntries.find(id);
if (exists != mappedEntries.end()) {
auto iter = mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
return iter == mappedEntries.end() ? defaultValue : iter->second;
}
return iter->second;
// Now get the data
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ? AND component_type = ?;");
// Now get the data. Get all components of this entity so we dont do a query for each component
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;");
query.bind(1, static_cast<int32_t>(id));
query.bind(2, static_cast<int32_t>(componentType));
auto tableData = query.execQuery();
while (!tableData.eof()) {
CDComponentsRegistry entry;
entry.id = tableData.getIntField("id", -1);
@@ -43,7 +43,9 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
tableData.nextRow();
}
iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
mappedEntries.insert_or_assign(id, 0);
auto iter = this->mappedEntries.find(((uint64_t)componentType) << 32 | ((uint64_t)id));
return iter == this->mappedEntries.end() ? defaultValue : iter->second;
}

View File

@@ -13,7 +13,7 @@ struct CDComponentsRegistry {
class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable> {
private:
std::map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id
std::unordered_map<uint64_t, uint32_t> mappedEntries; //id, component_type, component_id
public:
void LoadValuesFromDatabase();

View File

@@ -1,5 +1,18 @@
#include "CDLootMatrixTable.h"
CDLootMatrix CDLootMatrixTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootMatrix entry{};
if (tableData.eof()) return entry;
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
return entry;
}
void CDLootMatrixTable::LoadValuesFromDatabase() {
// First, get the size of the table
@@ -11,8 +24,6 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
@@ -20,33 +31,28 @@ void CDLootMatrixTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootMatrix");
while (!tableData.eof()) {
CDLootMatrix entry;
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
entry.percent = tableData.getFloatField("percent", -1.0f);
entry.minToDrop = tableData.getIntField("minToDrop", -1);
entry.maxToDrop = tableData.getIntField("maxToDrop", -1);
entry.id = tableData.getIntField("id", -1);
entry.flagID = tableData.getIntField("flagID", -1);
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
uint32_t lootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
this->entries.push_back(entry);
this->entries[lootMatrixIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
}
const LootMatrixEntries& CDLootMatrixTable::GetMatrix(uint32_t matrixId) {
auto itr = this->entries.find(matrixId);
if (itr != this->entries.end()) {
return itr->second;
}
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootMatrix where LootMatrixIndex = ?;");
query.bind(1, static_cast<int32_t>(matrixId));
auto tableData = query.execQuery();
while (!tableData.eof()) {
this->entries[matrixId].push_back(ReadRow(tableData));
tableData.nextRow();
}
tableData.finalize();
}
std::vector<CDLootMatrix> CDLootMatrixTable::Query(std::function<bool(CDLootMatrix)> predicate) {
std::vector<CDLootMatrix> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
const std::vector<CDLootMatrix>& CDLootMatrixTable::GetEntries() const {
return this->entries;
return this->entries[matrixId];
}

View File

@@ -4,26 +4,26 @@
#include "CDTable.h"
struct CDLootMatrix {
unsigned int LootMatrixIndex; //!< The Loot Matrix Index
unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int RarityTableIndex; //!< The Rarity Table Index
float percent; //!< The percent that this matrix is used?
unsigned int minToDrop; //!< The minimum amount of loot from this matrix to drop
unsigned int maxToDrop; //!< The maximum amount of loot from this matrix to drop
unsigned int id; //!< The ID of the Loot Matrix
unsigned int flagID; //!< ???
UNUSED(std::string gate_version); //!< The Gate Version
};
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> {
private:
std::vector<CDLootMatrix> entries;
typedef uint32_t LootMatrixIndex;
typedef std::vector<CDLootMatrix> LootMatrixEntries;
class CDLootMatrixTable : public CDTable<CDLootMatrixTable> {
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDLootMatrix> Query(std::function<bool(CDLootMatrix)> predicate);
const std::vector<CDLootMatrix>& GetEntries() const;
// Gets a matrix by ID or inserts a blank one if none existed.
const LootMatrixEntries& GetMatrix(uint32_t matrixId);
private:
CDLootMatrix ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootMatrixIndex, LootMatrixEntries> entries;
};

View File

@@ -1,4 +1,42 @@
#include "CDLootTableTable.h"
#include "CDClientManager.h"
#include "CDComponentsRegistryTable.h"
#include "CDItemComponentTable.h"
#include "eReplicaComponentType.h"
// Sort the tables by their rarity so the highest rarity items are first.
void SortTable(LootTableEntries& table) {
auto* componentsRegistryTable = CDClientManager::Instance().GetTable<CDComponentsRegistryTable>();
auto* itemComponentTable = CDClientManager::Instance().GetTable<CDItemComponentTable>();
// We modify the table in place so the outer loop keeps track of what is sorted
// and the inner loop finds the highest rarity item and swaps it with the current position
// of the outer loop.
for (auto oldItrOuter = table.begin(); oldItrOuter != table.end(); oldItrOuter++) {
auto lootToInsert = oldItrOuter;
// Its fine if this starts at 0, even if this doesnt match lootToInsert as the actual highest will
// either be found and overwrite these values, or the original is somehow zero and is still the highest rarity.
uint32_t highestLootRarity = 0;
for (auto oldItrInner = oldItrOuter; oldItrInner != table.end(); oldItrInner++) {
uint32_t itemComponentId = componentsRegistryTable->GetByIDAndType(oldItrInner->itemid, eReplicaComponentType::ITEM);
uint32_t rarity = itemComponentTable->GetItemComponentByID(itemComponentId).rarity;
if (rarity > highestLootRarity) {
highestLootRarity = rarity;
lootToInsert = oldItrInner;
}
}
Game::logger->LogDebug("CDLootTableTable", "highest rarity %i item id %i", highestLootRarity, lootToInsert->itemid);
std::swap(*oldItrOuter, *lootToInsert);
}
}
CDLootTable CDLootTableTable::ReadRow(CppSQLite3Query& tableData) const {
CDLootTable entry{};
if (tableData.eof()) return entry;
entry.itemid = tableData.getIntField("itemid", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
return entry;
}
void CDLootTableTable::LoadValuesFromDatabase() {
@@ -11,8 +49,6 @@ void CDLootTableTable::LoadValuesFromDatabase() {
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
@@ -20,32 +56,32 @@ void CDLootTableTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM LootTable");
while (!tableData.eof()) {
CDLootTable entry;
entry.id = tableData.getIntField("id", -1);
entry.itemid = tableData.getIntField("itemid", -1);
entry.LootTableIndex = tableData.getIntField("LootTableIndex", -1);
entry.id = tableData.getIntField("id", -1);
entry.MissionDrop = tableData.getIntField("MissionDrop", -1) == 1 ? true : false;
entry.sortPriority = tableData.getIntField("sortPriority", -1);
uint32_t lootTableIndex = tableData.getIntField("LootTableIndex", -1);
this->entries.push_back(entry);
this->entries[lootTableIndex].push_back(ReadRow(tableData));
tableData.nextRow();
}
tableData.finalize();
for (auto& [id, table] : this->entries) {
SortTable(table);
}
}
//! Queries the table with a custom "where" clause
std::vector<CDLootTable> CDLootTableTable::Query(std::function<bool(CDLootTable)> predicate) {
const LootTableEntries& CDLootTableTable::GetTable(uint32_t tableId) {
auto itr = this->entries.find(tableId);
if (itr != this->entries.end()) {
return itr->second;
}
std::vector<CDLootTable> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM LootTable WHERE LootTableIndex = ?;");
query.bind(1, static_cast<int32_t>(tableId));
auto tableData = query.execQuery();
return data;
while (!tableData.eof()) {
CDLootTable entry;
this->entries[tableId].push_back(ReadRow(tableData));
tableData.nextRow();
}
SortTable(this->entries[tableId]);
return this->entries[tableId];
}
//! Gets all the entries in the table
const std::vector<CDLootTable>& CDLootTableTable::GetEntries() const {
return this->entries;
}

View File

@@ -6,20 +6,21 @@
struct CDLootTable {
unsigned int itemid; //!< The LOT of the item
unsigned int LootTableIndex; //!< The Loot Table Index
unsigned int id; //!< The ID
bool MissionDrop; //!< Whether or not this loot table is a mission drop
unsigned int sortPriority; //!< The sorting priority
};
typedef uint32_t LootTableIndex;
typedef std::vector<CDLootTable> LootTableEntries;
class CDLootTableTable : public CDTable<CDLootTableTable> {
private:
std::vector<CDLootTable> entries;
CDLootTable ReadRow(CppSQLite3Query& tableData) const;
std::unordered_map<LootTableIndex, LootTableEntries> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
std::vector<CDLootTable> Query(std::function<bool(CDLootTable)> predicate);
const std::vector<CDLootTable>& GetEntries() const;
const LootTableEntries& GetTable(uint32_t tableId);
};

View File

@@ -1,24 +1,34 @@
#include "CDRarityTableTable.h"
void CDRarityTableTable::LoadValuesFromDatabase() {
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable");
while (!tableData.eof()) {
CDRarityTable entry;
uint32_t id = tableData.getIntField("id", -1);
entry.randmax = tableData.getFloatField("randmax", -1);
entry.rarity = tableData.getIntField("rarity", -1);
entry.RarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
this->entries.insert_or_assign(id, entry);
tableData.nextRow();
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RarityTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableData.finalize();
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RarityTable order by randmax desc;");
while (!tableData.eof()) {
uint32_t rarityTableIndex = tableData.getIntField("RarityTableIndex", -1);
CDRarityTable entry;
entry.randmax = tableData.getFloatField("randmax", -1);
entry.rarity = tableData.getIntField("rarity", -1);
entries[rarityTableIndex].push_back(entry);
tableData.nextRow();
}
}
//! Queries the table with a custom "where" clause
const std::optional<CDRarityTable> CDRarityTableTable::Get(uint32_t id) {
auto it = this->entries.find(id);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt;
const std::vector<CDRarityTable>& CDRarityTableTable::GetRarityTable(uint32_t id) {
return entries[id];
}

View File

@@ -4,35 +4,20 @@
#include "CDTable.h"
struct CDRarityTable {
unsigned int id;
float randmax;
unsigned int rarity;
unsigned int RarityTableIndex;
friend bool operator> (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity > c2.rarity;
}
friend bool operator>= (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity >= c2.rarity;
}
friend bool operator< (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity < c2.rarity;
}
friend bool operator<= (const CDRarityTable& c1, const CDRarityTable& c2) {
return c1.rarity <= c2.rarity;
}
};
typedef std::vector<CDRarityTable> RarityTable;
class CDRarityTableTable : public CDTable<CDRarityTableTable> {
private:
std::unordered_map<uint32_t, CDRarityTable> entries;
typedef uint32_t RarityTableIndex;
std::unordered_map<RarityTableIndex, std::vector<CDRarityTable>> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
const std::optional<CDRarityTable> Get(uint32_t predicate);
const std::vector<CDRarityTable>& GetRarityTable(uint32_t predicate);
};

View File

@@ -1,11 +1,26 @@
#include "CDRebuildComponentTable.h"
void CDRebuildComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RebuildComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RebuildComponent");
while (!tableData.eof()) {
CDRebuildComponent entry;
uint32_t id = tableData.getIntField("id", -1);
entry.id = tableData.getIntField("id", -1);
entry.reset_time = tableData.getFloatField("reset_time", -1.0f);
entry.complete_time = tableData.getFloatField("complete_time", -1.0f);
entry.take_imagination = tableData.getIntField("take_imagination", -1);
@@ -16,12 +31,23 @@ void CDRebuildComponentTable::LoadValuesFromDatabase() {
entry.post_imagination_cost = tableData.getIntField("post_imagination_cost", -1);
entry.time_before_smash = tableData.getFloatField("time_before_smash", -1.0f);
this->entries.insert_or_assign(id, entry);
this->entries.push_back(entry);
tableData.nextRow();
}
tableData.finalize();
}
const std::optional<CDRebuildComponent> CDRebuildComponentTable::Get(uint32_t componentId) {
auto it = this->entries.find(componentId);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt;
std::vector<CDRebuildComponent> CDRebuildComponentTable::Query(std::function<bool(CDRebuildComponent)> predicate) {
std::vector<CDRebuildComponent> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
const std::vector<CDRebuildComponent>& CDRebuildComponentTable::GetEntries() const {
return this->entries;
}

View File

@@ -4,6 +4,7 @@
#include "CDTable.h"
struct CDRebuildComponent {
unsigned int id; //!< The component Id
float reset_time; //!< The reset time
float complete_time; //!< The complete time
unsigned int take_imagination; //!< The amount of imagination it costs
@@ -17,11 +18,13 @@ struct CDRebuildComponent {
class CDRebuildComponentTable : public CDTable<CDRebuildComponentTable> {
private:
std::unordered_map<uint32_t, CDRebuildComponent> entries;
std::vector<CDRebuildComponent> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
const std::optional<CDRebuildComponent> Get(uint32_t componentId);
std::vector<CDRebuildComponent> Query(std::function<bool(CDRebuildComponent)> predicate);
const std::vector<CDRebuildComponent>& GetEntries() const;
};

View File

@@ -4,14 +4,14 @@ void CDRewardsTable::LoadValuesFromDatabase() {
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Rewards");
while (!tableData.eof()) {
CDRewards entry;
uint32_t id = tableData.getIntField("id", -1);
entry.id = tableData.getIntField("id", -1);
entry.levelID = tableData.getIntField("LevelID", -1);
entry.missionID = tableData.getIntField("MissionID", -1);
entry.rewardType = tableData.getIntField("RewardType", -1);
entry.value = tableData.getIntField("value", -1);
entry.count = tableData.getIntField("count", -1);
m_entries.push_back(entry);
m_entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
@@ -19,9 +19,9 @@ void CDRewardsTable::LoadValuesFromDatabase() {
}
std::vector<CDRewards> CDRewardsTable::GetByLevelID(uint32_t levelID) {
std::vector<CDRewards> result;
for (const auto& levelData : m_entries) {
if (levelData.levelID == levelID) result.push_back(levelData);
std::vector<CDRewards> result{};
for (const auto& e : m_entries) {
if (e.second.levelID == levelID) result.push_back(e.second);
}
return result;

View File

@@ -3,6 +3,7 @@
#include <string>
struct CDRewards {
int32_t id;
int32_t levelID;
int32_t missionID;
int32_t rewardType;
@@ -13,7 +14,10 @@ struct CDRewards {
class CDRewardsTable : public CDTable<CDRewardsTable> {
public:
void LoadValuesFromDatabase();
static const std::string GetTableName() { return "Rewards"; };
std::vector<CDRewards> GetByLevelID(uint32_t levelID);
private:
std::vector<CDRewards> m_entries;
std::map<uint32_t, CDRewards> m_entries;
};

View File

@@ -1,21 +1,39 @@
#include "CDScriptComponentTable.h"
void CDScriptComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ScriptComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ScriptComponent");
while (!tableData.eof()) {
CDScriptComponent entry;
uint32_t id = tableData.getIntField("id", -1);
entry.id = tableData.getIntField("id", -1);
entry.script_name = tableData.getStringField("script_name", "");
entry.client_script_name = tableData.getStringField("client_script_name", "");
this->entries.insert_or_assign(id, entry);
this->entries.insert(std::make_pair(entry.id, entry));
tableData.nextRow();
}
tableData.finalize();
}
const std::optional<CDScriptComponent> CDScriptComponentTable::GetByID(unsigned int id) {
auto it = this->entries.find(id);
return (it != this->entries.end()) ? std::make_optional<CDScriptComponent>(it->second) : std::nullopt;
const CDScriptComponent& CDScriptComponentTable::GetByID(unsigned int id) {
std::map<unsigned int, CDScriptComponent>::iterator it = this->entries.find(id);
if (it != this->entries.end()) {
return it->second;
}
return m_ToReturnWhenNoneFound;
}

View File

@@ -4,16 +4,19 @@
#include "CDTable.h"
struct CDScriptComponent {
std::string script_name; //!< The script name
std::string client_script_name; //!< The client script name
unsigned int id; //!< The component ID
std::string script_name; //!< The script name
std::string client_script_name; //!< The client script name
};
class CDScriptComponentTable : public CDTable<CDScriptComponentTable> {
private:
std::unordered_map<unsigned int, CDScriptComponent> entries;
std::map<unsigned int, CDScriptComponent> entries;
CDScriptComponent m_ToReturnWhenNoneFound;
public:
void LoadValuesFromDatabase();
// Gets an entry by scriptID
const std::optional<CDScriptComponent> GetByID(unsigned int id);
const CDScriptComponent& GetByID(unsigned int id);
};

View File

@@ -1,10 +1,27 @@
#include "CDSkillBehaviorTable.h"
void CDSkillBehaviorTable::LoadValuesFromDatabase() {
m_empty = CDSkillBehavior();
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM SkillBehavior");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
//this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM SkillBehavior");
while (!tableData.eof()) {
CDSkillBehavior entry;
uint32_t skillID = tableData.getIntField("skillID", -1);
entry.skillID = tableData.getIntField("skillID", -1);
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
entry.behaviorID = tableData.getIntField("behaviorID", -1);
entry.imaginationcost = tableData.getIntField("imaginationcost", -1);
@@ -24,13 +41,20 @@ void CDSkillBehaviorTable::LoadValuesFromDatabase() {
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
UNUSED(entry.cancelType = tableData.getIntField("cancelType", -1));
this->entries.insert_or_assign(skillID, entry);
this->entries.insert(std::make_pair(entry.skillID, entry));
//this->entries.push_back(entry);
tableData.nextRow();
}
tableData.finalize();
}
const std::optional<CDSkillBehavior> CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) {
auto it = this->entries.find(skillID);
return it != this->entries.end() ? std::make_optional(it->second) : std::nullopt;
const CDSkillBehavior& CDSkillBehaviorTable::GetSkillByID(unsigned int skillID) {
std::map<unsigned int, CDSkillBehavior>::iterator it = this->entries.find(skillID);
if (it != this->entries.end()) {
return it->second;
}
return m_empty;
}

View File

@@ -4,6 +4,7 @@
#include "CDTable.h"
struct CDSkillBehavior {
unsigned int skillID; //!< The Skill ID of the skill
UNUSED(unsigned int locStatus); //!< ??
unsigned int behaviorID; //!< The Behavior ID of the skill
unsigned int imaginationcost; //!< The imagination cost of the skill
@@ -26,11 +27,13 @@ struct CDSkillBehavior {
class CDSkillBehaviorTable : public CDTable<CDSkillBehaviorTable> {
private:
std::unordered_map<uint32_t, CDSkillBehavior> entries;
std::map<unsigned int, CDSkillBehavior> entries;
CDSkillBehavior m_empty;
public:
void LoadValuesFromDatabase();
// Gets an entry by skillID
const std::optional<CDSkillBehavior> GetSkillByID(unsigned int skillID);
const CDSkillBehavior& GetSkillByID(unsigned int skillID);
};

View File

@@ -5,7 +5,6 @@
#include "DluAssert.h"
#include <functional>
#include <optional>
#include <string>
#include <vector>
#include <map>

View File

@@ -1,22 +1,50 @@
#include "CDVendorComponentTable.h"
void CDVendorComponentTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM VendorComponent");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Reserve the size
this->entries.reserve(size);
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM VendorComponent");
while (!tableData.eof()) {
CDVendorComponent entry;
uint32_t id = tableData.getIntField("id", -1);
entry.buyScalar = tableData.getFloatField("buyScalar", -1.0f);
entry.id = tableData.getIntField("id", -1);
entry.buyScalar = tableData.getFloatField("buyScalar", 0.0f);
entry.sellScalar = tableData.getFloatField("sellScalar", -1.0f);
entry.refreshTimeSeconds = tableData.getFloatField("refreshTimeSeconds", -1.0f);
entry.LootMatrixIndex = tableData.getIntField("LootMatrixIndex", -1);
this->entries.insert_or_assign(id, entry);
this->entries.push_back(entry);
tableData.nextRow();
}
tableData.finalize();
}
const std::optional<CDVendorComponent> CDVendorComponentTable::Query(uint32_t id) {
const auto& iter = entries.find(id);
return iter != entries.end() ? std::make_optional(iter->second) : std::nullopt;
//! Queries the table with a custom "where" clause
std::vector<CDVendorComponent> CDVendorComponentTable::Query(std::function<bool(CDVendorComponent)> predicate) {
std::vector<CDVendorComponent> data = cpplinq::from(this->entries)
>> cpplinq::where(predicate)
>> cpplinq::to_vector();
return data;
}
//! Gets all the entries in the table
const std::vector<CDVendorComponent>& CDVendorComponentTable::GetEntries() const {
return this->entries;
}

View File

@@ -4,6 +4,7 @@
#include "CDTable.h"
struct CDVendorComponent {
unsigned int id; //!< The Component ID
float buyScalar; //!< Buy Scalar (what does that mean?)
float sellScalar; //!< Sell Scalar (what does that mean?)
float refreshTimeSeconds; //!< The refresh time
@@ -12,11 +13,13 @@ struct CDVendorComponent {
class CDVendorComponentTable : public CDTable<CDVendorComponentTable> {
private:
std::unordered_map<uint32_t, CDVendorComponent> entries;
std::vector<CDVendorComponent> entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a custom "where" clause
const std::optional<CDVendorComponent> Query(uint32_t id);
std::vector<CDVendorComponent> Query(std::function<bool(CDVendorComponent)> predicate);
const std::vector<CDVendorComponent>& GetEntries(void) const;
};

View File

@@ -1,6 +1,18 @@
#include "CDZoneTableTable.h"
void CDZoneTableTable::LoadValuesFromDatabase() {
// First, get the size of the table
unsigned int size = 0;
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ZoneTable");
while (!tableSize.eof()) {
size = tableSize.getIntField(0, 0);
tableSize.nextRow();
}
tableSize.finalize();
// Now get the data
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ZoneTable");
while (!tableData.eof()) {
@@ -36,11 +48,18 @@ void CDZoneTableTable::LoadValuesFromDatabase() {
this->m_Entries.insert(std::make_pair(entry.zoneID, entry));
tableData.nextRow();
}
tableData.finalize();
}
//! Queries the table with a zoneID to find.
const std::optional<CDZoneTable> CDZoneTableTable::Query(unsigned int zoneID) {
const CDZoneTable* CDZoneTableTable::Query(unsigned int zoneID) {
const auto& iter = m_Entries.find(zoneID);
return iter != m_Entries.end() ? std::make_optional(iter->second) : std::nullopt;
if (iter != m_Entries.end()) {
return &iter->second;
}
return nullptr;
}

View File

@@ -1,5 +1,6 @@
#pragma once
// Custom Classes
#include "CDTable.h"
struct CDZoneTable {
@@ -34,11 +35,11 @@ struct CDZoneTable {
class CDZoneTableTable : public CDTable<CDZoneTableTable> {
private:
std::unordered_map<uint32_t, CDZoneTable> m_Entries;
std::map<unsigned int, CDZoneTable> m_Entries;
public:
void LoadValuesFromDatabase();
// Queries the table with a zoneID to find.
const std::optional<CDZoneTable> Query(unsigned int zoneID);
const CDZoneTable* Query(unsigned int zoneID);
};

View File

@@ -30,7 +30,7 @@
#include "Component.h"
#include "ControllablePhysicsComponent.h"
#include "RenderComponent.h"
#include "RocketLaunchLupComponent.h"
#include "MultiZoneEntranceComponent.h"
#include "CharacterComponent.h"
#include "DestroyableComponent.h"
#include "BuffComponent.h"
@@ -51,6 +51,7 @@
#include "BuildBorderComponent.h"
#include "MovementAIComponent.h"
#include "VendorComponent.h"
#include "DonationVendorComponent.h"
#include "RocketLaunchpadControlComponent.h"
#include "PropertyComponent.h"
#include "BaseCombatAIComponent.h"
@@ -70,6 +71,7 @@
#include "ShootingGalleryComponent.h"
#include "RailActivatorComponent.h"
#include "LUPExhibitComponent.h"
#include "RacingSoundTriggerComponent.h"
#include "TriggerComponent.h"
#include "eGameMasterLevel.h"
#include "eReplicaComponentType.h"
@@ -317,6 +319,9 @@ void Entity::Initialize() {
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::SOUND_TRIGGER, -1) != -1) {
auto* comp = new SoundTriggerComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::SOUND_TRIGGER, comp));
} else if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::RACING_SOUND_TRIGGER, -1) != -1) {
auto* comp = new RacingSoundTriggerComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::RACING_SOUND_TRIGGER, comp));
}
//Also check for the collectible id:
@@ -340,7 +345,7 @@ void Entity::Initialize() {
int buffComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::BUFF);
int rebuildComponentID = compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::QUICK_BUILD);
int componentID = 0;
int componentID = -1;
if (collectibleComponentID > 0) componentID = collectibleComponentID;
if (rebuildComponentID > 0) componentID = rebuildComponentID;
if (buffComponentID > 0) componentID = buffComponentID;
@@ -348,7 +353,8 @@ void Entity::Initialize() {
CDDestructibleComponentTable* destCompTable = CDClientManager::Instance().GetTable<CDDestructibleComponentTable>();
std::vector<CDDestructibleComponent> destCompData = destCompTable->Query([=](CDDestructibleComponent entry) { return (entry.id == componentID); });
if (buffComponentID > 0 || collectibleComponentID > 0) {
bool isSmashable = GetVarAs<int32_t>(u"is_smashable") != 0;
if (buffComponentID > 0 || collectibleComponentID > 0 || isSmashable) {
DestroyableComponent* comp = new DestroyableComponent(this);
if (m_Character) {
comp->LoadFromXml(m_Character->GetXMLDoc());
@@ -368,10 +374,12 @@ void Entity::Initialize() {
comp->SetMaxHealth(destCompData[0].life);
comp->SetMaxImagination(destCompData[0].imagination);
comp->SetMaxArmor(destCompData[0].armor);
comp->SetDeathBehavior(destCompData[0].death_behavior);
comp->SetIsSmashable(destCompData[0].isSmashable);
comp->SetLootMatrixID(destCompData[0].LootMatrixIndex);
Loot::CacheMatrix(destCompData[0].LootMatrixIndex);
// Now get currency information
uint32_t npcMinLevel = destCompData[0].level;
@@ -387,7 +395,7 @@ void Entity::Initialize() {
}
// extraInfo overrides. Client ORs the database smashable and the luz smashable.
comp->SetIsSmashable(comp->GetIsSmashable() | (GetVarAs<int32_t>(u"is_smashable") != 0));
comp->SetIsSmashable(comp->GetIsSmashable() | isSmashable);
}
} else {
comp->SetHealth(1);
@@ -420,6 +428,19 @@ void Entity::Initialize() {
}
}
// override the factions if needed.
auto setFaction = GetVarAsString(u"set_faction");
if (!setFaction.empty()) {
// TODO also split on space here however we do not have a general util for splitting on multiple characters yet.
std::vector<std::string> factionsToAdd = GeneralUtils::SplitString(setFaction, ';');
int32_t factionToAdd;
for (const auto faction : factionsToAdd) {
if (GeneralUtils::TryParse(faction, factionToAdd)) {
comp->AddFaction(factionToAdd, true);
}
}
}
m_Components.insert(std::make_pair(eReplicaComponentType::DESTROYABLE, comp));
}
@@ -446,9 +467,9 @@ void Entity::Initialize() {
m_Components.insert(std::make_pair(eReplicaComponentType::INVENTORY, comp));
}
// if this component exists, then we initialize it. it's value is always 0
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::ROCKET_LAUNCH_LUP, -1) != -1) {
auto comp = new RocketLaunchLupComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::ROCKET_LAUNCH_LUP, comp));
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MULTI_ZONE_ENTRANCE, -1) != -1) {
auto comp = new MultiZoneEntranceComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::MULTI_ZONE_ENTRANCE, comp));
}
/**
@@ -463,11 +484,9 @@ void Entity::Initialize() {
if (scriptComponentID > 0 || m_Character) {
std::string clientScriptName;
if (!m_Character) {
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (scriptCompData) {
scriptName = scriptCompData->script_name;
clientScriptName = scriptCompData->client_script_name;
}
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
scriptName = scriptCompData.script_name;
clientScriptName = scriptCompData.client_script_name;
} else {
scriptName = "";
}
@@ -506,17 +525,16 @@ void Entity::Initialize() {
// ZoneControl script
if (m_TemplateID == 2365) {
auto* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>();
CDZoneTableTable* zoneTable = CDClientManager::Instance().GetTable<CDZoneTableTable>();
const auto zoneID = Game::zoneManager->GetZoneID();
auto zoneData = zoneTable->Query(zoneID.GetMapID());
const CDZoneTable* zoneData = zoneTable->Query(zoneID.GetMapID());
if (zoneData) {
if (zoneData != nullptr) {
int zoneScriptID = zoneData->scriptID;
auto zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
if (zoneScriptData) {
ScriptComponent* comp = new ScriptComponent(this, zoneScriptData->script_name, true);
m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp));
}
CDScriptComponent zoneScriptData = scriptCompTable->GetByID(zoneScriptID);
ScriptComponent* comp = new ScriptComponent(this, zoneScriptData.script_name, true);
m_Components.insert(std::make_pair(eReplicaComponentType::SCRIPT, comp));
}
}
@@ -536,17 +554,17 @@ void Entity::Initialize() {
m_Components.insert(std::make_pair(eReplicaComponentType::QUICK_BUILD, comp));
CDRebuildComponentTable* rebCompTable = CDClientManager::Instance().GetTable<CDRebuildComponentTable>();
auto rebCompData = rebCompTable->Get(rebuildComponentID);
std::vector<CDRebuildComponent> rebCompData = rebCompTable->Query([=](CDRebuildComponent entry) { return (entry.id == rebuildComponentID); });
if (rebCompData) {
comp->SetResetTime(rebCompData->reset_time);
comp->SetCompleteTime(rebCompData->complete_time);
comp->SetTakeImagination(rebCompData->take_imagination);
comp->SetInterruptible(rebCompData->interruptible);
comp->SetSelfActivator(rebCompData->self_activator);
comp->SetActivityId(rebCompData->activityID);
comp->SetPostImaginationCost(rebCompData->post_imagination_cost);
comp->SetTimeBeforeSmash(rebCompData->time_before_smash);
if (rebCompData.size() > 0) {
comp->SetResetTime(rebCompData[0].reset_time);
comp->SetCompleteTime(rebCompData[0].complete_time);
comp->SetTakeImagination(rebCompData[0].take_imagination);
comp->SetInterruptible(rebCompData[0].interruptible);
comp->SetSelfActivator(rebCompData[0].self_activator);
comp->SetActivityId(rebCompData[0].activityID);
comp->SetPostImaginationCost(rebCompData[0].post_imagination_cost);
comp->SetTimeBeforeSmash(rebCompData[0].time_before_smash);
const auto rebuildResetTime = GetVar<float>(u"rebuild_reset_time");
@@ -563,6 +581,7 @@ void Entity::Initialize() {
if (activityID > 0) {
comp->SetActivityId(activityID);
Loot::CacheMatrix(activityID);
}
const auto compTime = GetVar<float>(u"compTime");
@@ -581,6 +600,9 @@ void Entity::Initialize() {
if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::VENDOR) > 0)) {
VendorComponent* comp = new VendorComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::VENDOR, comp));
} else if ((compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::DONATION_VENDOR, -1) != -1)) {
DonationVendorComponent* comp = new DonationVendorComponent(this);
m_Components.insert(std::make_pair(eReplicaComponentType::DONATION_VENDOR, comp));
}
if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::PROPERTY_VENDOR, -1) != -1) {
@@ -697,12 +719,12 @@ void Entity::Initialize() {
const Path* path = Game::zoneManager->GetZone()->GetPath(pathName);
//Check to see if we have an attached path and add the appropiate component to handle it:
if (path){
if (path) {
// if we have a moving platform path, then we need a moving platform component
if (path->pathType == PathType::MovingPlatform) {
MovingPlatformComponent* plat = new MovingPlatformComponent(this, pathName);
m_Components.insert(std::make_pair(eReplicaComponentType::MOVING_PLATFORM, plat));
// else if we are a movement path
// else if we are a movement path
} /*else if (path->pathType == PathType::Movement) {
auto movementAIcomp = GetComponent<MovementAIComponent>();
if (movementAIcomp){
@@ -917,10 +939,20 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
outBitStream->Write1(); //ldf data
RakNet::BitStream settingStream;
settingStream.Write<uint32_t>(m_Settings.size());
int32_t numberOfValidKeys = m_Settings.size();
// Writing keys value pairs the client does not expect to receive or interpret will result in undefined behavior,
// so we need to filter out any keys that are not valid and fix the number of valid keys to be correct.
// TODO should make this more efficient so that we dont waste loops evaluating the same condition twice
for (LDFBaseData* data : m_Settings) {
if (!data || data->GetValueType() == eLDFType::LDF_TYPE_UNKNOWN) {
numberOfValidKeys--;
}
}
settingStream.Write<uint32_t>(numberOfValidKeys);
for (LDFBaseData* data : m_Settings) {
if (data) {
if (data && data->GetValueType() != eLDFType::LDF_TYPE_UNKNOWN) {
data->WriteToPacket(&settingStream);
}
}
@@ -939,7 +971,6 @@ void Entity::WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacke
RakNet::BitStream settingStream;
settingStream.Write<uint32_t>(ldfData.size());
for (LDFBaseData* data : ldfData) {
if (data) {
data->WriteToPacket(&settingStream);
@@ -1015,57 +1046,60 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
*/
bool destroyableSerialized = false;
bool bIsInitialUpdate = false;
if (packetType == eReplicaPacketType::CONSTRUCTION) bIsInitialUpdate = true;
unsigned int flags = 0;
bool bIsInitialUpdate = packetType == eReplicaPacketType::CONSTRUCTION;
PossessableComponent* possessableComponent;
if (TryGetComponent(eReplicaComponentType::POSSESSABLE, possessableComponent)) {
possessableComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
possessableComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ModuleAssemblyComponent* moduleAssemblyComponent;
if (TryGetComponent(eReplicaComponentType::MODULE_ASSEMBLY, moduleAssemblyComponent)) {
moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
moduleAssemblyComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ControllablePhysicsComponent* controllablePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS, controllablePhysicsComponent)) {
controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
controllablePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
}
SimplePhysicsComponent* simplePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::SIMPLE_PHYSICS, simplePhysicsComponent)) {
simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
simplePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RigidbodyPhantomPhysicsComponent* rigidbodyPhantomPhysics;
if (TryGetComponent(eReplicaComponentType::RIGID_BODY_PHANTOM_PHYSICS, rigidbodyPhantomPhysics)) {
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate, flags);
rigidbodyPhantomPhysics->Serialize(outBitStream, bIsInitialUpdate);
}
VehiclePhysicsComponent* vehiclePhysicsComponent;
if (TryGetComponent(eReplicaComponentType::VEHICLE_PHYSICS, vehiclePhysicsComponent)) {
vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
vehiclePhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
}
PhantomPhysicsComponent* phantomPhysicsComponent;
if (TryGetComponent(eReplicaComponentType::PHANTOM_PHYSICS, phantomPhysicsComponent)) {
phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
phantomPhysicsComponent->Serialize(outBitStream, bIsInitialUpdate);
}
SoundTriggerComponent* soundTriggerComponent;
if (TryGetComponent(eReplicaComponentType::SOUND_TRIGGER, soundTriggerComponent)) {
soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
soundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RacingSoundTriggerComponent* racingSoundTriggerComponent;
if (TryGetComponent(eReplicaComponentType::RACING_SOUND_TRIGGER, racingSoundTriggerComponent)) {
racingSoundTriggerComponent->Serialize(outBitStream, bIsInitialUpdate);
}
BuffComponent* buffComponent;
if (TryGetComponent(eReplicaComponentType::BUFF, buffComponent)) {
buffComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
buffComponent->Serialize(outBitStream, bIsInitialUpdate);
DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
}
destroyableSerialized = true;
}
@@ -1073,7 +1107,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
if (HasComponent(eReplicaComponentType::COLLECTIBLE)) {
DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
}
destroyableSerialized = true;
outBitStream->Write(m_CollectibleID); // Collectable component
@@ -1081,7 +1115,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PetComponent* petComponent;
if (TryGetComponent(eReplicaComponentType::PET, petComponent)) {
petComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
petComponent->Serialize(outBitStream, bIsInitialUpdate);
}
CharacterComponent* characterComponent;
@@ -1089,7 +1123,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PossessorComponent* possessorComponent;
if (TryGetComponent(eReplicaComponentType::POSSESSOR, possessorComponent)) {
possessorComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
possessorComponent->Serialize(outBitStream, bIsInitialUpdate);
} else {
// Should never happen, but just to be safe
outBitStream->Write0();
@@ -1097,7 +1131,7 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
LevelProgressionComponent* levelProgressionComponent;
if (TryGetComponent(eReplicaComponentType::LEVEL_PROGRESSION, levelProgressionComponent)) {
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
levelProgressionComponent->Serialize(outBitStream, bIsInitialUpdate);
} else {
// Should never happen, but just to be safe
outBitStream->Write0();
@@ -1105,13 +1139,13 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
PlayerForcedMovementComponent* playerForcedMovementComponent;
if (TryGetComponent(eReplicaComponentType::PLAYER_FORCED_MOVEMENT, playerForcedMovementComponent)) {
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
playerForcedMovementComponent->Serialize(outBitStream, bIsInitialUpdate);
} else {
// Should never happen, but just to be safe
outBitStream->Write0();
}
characterComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
characterComponent->Serialize(outBitStream, bIsInitialUpdate);
}
if (HasComponent(eReplicaComponentType::ITEM)) {
@@ -1120,88 +1154,93 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
InventoryComponent* inventoryComponent;
if (TryGetComponent(eReplicaComponentType::INVENTORY, inventoryComponent)) {
inventoryComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
inventoryComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ScriptComponent* scriptComponent;
if (TryGetComponent(eReplicaComponentType::SCRIPT, scriptComponent)) {
scriptComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
scriptComponent->Serialize(outBitStream, bIsInitialUpdate);
}
SkillComponent* skillComponent;
if (TryGetComponent(eReplicaComponentType::SKILL, skillComponent)) {
skillComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
skillComponent->Serialize(outBitStream, bIsInitialUpdate);
}
BaseCombatAIComponent* baseCombatAiComponent;
if (TryGetComponent(eReplicaComponentType::BASE_COMBAT_AI, baseCombatAiComponent)) {
baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
baseCombatAiComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RebuildComponent* rebuildComponent;
if (TryGetComponent(eReplicaComponentType::QUICK_BUILD, rebuildComponent)) {
DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
}
destroyableSerialized = true;
rebuildComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
rebuildComponent->Serialize(outBitStream, bIsInitialUpdate);
}
MovingPlatformComponent* movingPlatformComponent;
if (TryGetComponent(eReplicaComponentType::MOVING_PLATFORM, movingPlatformComponent)) {
movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
movingPlatformComponent->Serialize(outBitStream, bIsInitialUpdate);
}
SwitchComponent* switchComponent;
if (TryGetComponent(eReplicaComponentType::SWITCH, switchComponent)) {
switchComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
switchComponent->Serialize(outBitStream, bIsInitialUpdate);
}
VendorComponent* vendorComponent;
if (TryGetComponent(eReplicaComponentType::VENDOR, vendorComponent)) {
vendorComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
vendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}
DonationVendorComponent* donationVendorComponent;
if (TryGetComponent(eReplicaComponentType::DONATION_VENDOR, donationVendorComponent)) {
donationVendorComponent->Serialize(outBitStream, bIsInitialUpdate);
}
BouncerComponent* bouncerComponent;
if (TryGetComponent(eReplicaComponentType::BOUNCER, bouncerComponent)) {
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
bouncerComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ScriptedActivityComponent* scriptedActivityComponent;
if (TryGetComponent(eReplicaComponentType::SCRIPTED_ACTIVITY, scriptedActivityComponent)) {
scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
scriptedActivityComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ShootingGalleryComponent* shootingGalleryComponent;
if (TryGetComponent(eReplicaComponentType::SHOOTING_GALLERY, shootingGalleryComponent)) {
shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
shootingGalleryComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RacingControlComponent* racingControlComponent;
if (TryGetComponent(eReplicaComponentType::RACING_CONTROL, racingControlComponent)) {
racingControlComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
racingControlComponent->Serialize(outBitStream, bIsInitialUpdate);
}
LUPExhibitComponent* lupExhibitComponent;
if (TryGetComponent(eReplicaComponentType::LUP_EXHIBIT, lupExhibitComponent)) {
lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
lupExhibitComponent->Serialize(outBitStream, bIsInitialUpdate);
}
ModelComponent* modelComponent;
if (TryGetComponent(eReplicaComponentType::MODEL, modelComponent)) {
modelComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
modelComponent->Serialize(outBitStream, bIsInitialUpdate);
}
RenderComponent* renderComponent;
if (TryGetComponent(eReplicaComponentType::RENDER, renderComponent)) {
renderComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
renderComponent->Serialize(outBitStream, bIsInitialUpdate);
}
if (modelComponent) {
if (modelComponent || !destroyableSerialized) {
DestroyableComponent* destroyableComponent;
if (TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent) && !destroyableSerialized) {
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate, flags);
destroyableComponent->Serialize(outBitStream, bIsInitialUpdate);
destroyableSerialized = true;
}
}
@@ -1216,10 +1255,6 @@ void Entity::WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType
outBitStream->Write0();
}
void Entity::ResetFlags() {
// Unused
}
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.
//Naturally, we don't include any non-player components in this update function.
@@ -1541,7 +1576,17 @@ void Entity::Kill(Entity* murderer) {
}
if (!IsPlayer()) {
Game::entityManager->DestroyEntity(this);
auto* destroyableComponent = GetComponent<DestroyableComponent>();
bool waitForDeathAnimation = false;
if (destroyableComponent) {
waitForDeathAnimation = destroyableComponent->GetDeathBehavior() == 0;
}
// Live waited a hard coded 12 seconds for death animations of type 0 before networking destruction!
constexpr float DelayDeathTime = 12.0f;
if (waitForDeathAnimation) AddCallbackTimer(DelayDeathTime, [this]() { Game::entityManager->DestroyEntity(this); });
else Game::entityManager->DestroyEntity(this);
}
const auto& grpNameQBShowBricks = GetVar<std::string>(u"grpNameQBShowBricks");
@@ -1631,9 +1676,9 @@ void Entity::PickupItem(const LWOOBJID& objectID) {
std::vector<CDObjectSkills> skills = skillsTable->Query([=](CDObjectSkills entry) {return (entry.objectTemplate == p.second.lot); });
for (CDObjectSkills skill : skills) {
CDSkillBehaviorTable* skillBehTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto behaviorData = skillBehTable->GetSkillByID(skill.skillID);
if (!behaviorData) continue;
SkillComponent::HandleUnmanaged(behaviorData->behaviorID, GetObjectID());
CDSkillBehavior behaviorData = skillBehTable->GetSkillByID(skill.skillID);
SkillComponent::HandleUnmanaged(behaviorData.behaviorID, GetObjectID());
auto* missionComponent = GetComponent<MissionComponent>();

View File

@@ -85,6 +85,7 @@ public:
bool GetPlayerReadyForUpdates() const { return m_PlayerIsReadyForUpdates; }
bool GetIsGhostingCandidate() const;
void SetIsGhostingCandidate(bool value) { m_IsGhostingCandidate = value; };
int8_t GetObservers() const;
@@ -173,7 +174,6 @@ public:
void WriteBaseReplicaData(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void WriteComponents(RakNet::BitStream* outBitStream, eReplicaPacketType packetType);
void ResetFlags();
void UpdateXMLDoc(tinyxml2::XMLDocument* doc);
void Update(float deltaTime);

View File

@@ -298,6 +298,16 @@ std::vector<Entity*> EntityManager::GetEntitiesByLOT(const LOT& lot) const {
return entities;
}
std::vector<Entity*> EntityManager::GetEntitiesByProximity(NiPoint3 reference, float radius) const{
std::vector<Entity*> entities = {};
if (radius > 1000.0f) return entities;
for (const auto& entity : m_Entities) {
if (NiPoint3::Distance(reference, entity.second->GetPosition()) <= radius) entities.push_back(entity.second);
}
return entities;
}
Entity* EntityManager::GetZoneControlEntity() const {
return m_ZoneControlEntity;
}
@@ -583,12 +593,6 @@ bool EntityManager::GetGhostingEnabled() const {
return m_GhostingEnabled;
}
void EntityManager::ResetFlags() {
for (const auto& e : m_Entities) {
e.second->ResetFlags();
}
}
void EntityManager::ScheduleForKill(Entity* entity) {
// Deactivate switches if they die
if (!entity)

View File

@@ -28,6 +28,7 @@ public:
std::vector<Entity*> GetEntitiesInGroup(const std::string& group);
std::vector<Entity*> GetEntitiesByComponent(eReplicaComponentType componentType) const;
std::vector<Entity*> GetEntitiesByLOT(const LOT& lot) const;
std::vector<Entity*> GetEntitiesByProximity(NiPoint3 reference, float radius) const;
Entity* GetZoneControlEntity() const;
// Get spawn point entity by spawn name
@@ -59,8 +60,6 @@ public:
Entity* GetGhostCandidate(int32_t id);
bool GetGhostingEnabled() const;
void ResetFlags();
void ScheduleForKill(Entity* entity);
void ScheduleForDeletion(LWOOBJID entity);

View File

@@ -131,8 +131,8 @@ void Leaderboard::QueryToLdf(std::unique_ptr<sql::ResultSet>& rows) {
// Time:1
break;
case Type::Donations:
entry.push_back(new LDFData<int32_t>(u"Points", rows->getInt("primaryScore")));
// Score:1
entry.push_back(new LDFData<int32_t>(u"Score", rows->getInt("primaryScore")));
// Score:1
break;
case Type::None:
// This type is included here simply to resolve a compiler warning on mac about unused enum types
@@ -170,32 +170,32 @@ void Leaderboard::SetupLeaderboard(bool weekly, uint32_t resultStart, uint32_t r
resultEnd++;
// We need everything except 1 column so i'm selecting * from leaderboard
const std::string queryBase =
R"QUERY(
WITH leaderboardsRanked AS (
SELECT leaderboard.*, charinfo.name,
RANK() OVER
(
R"QUERY(
WITH leaderboardsRanked AS (
SELECT leaderboard.*, charinfo.name,
RANK() OVER
(
ORDER BY %s, UNIX_TIMESTAMP(last_played) ASC, id DESC
) AS ranking
FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id
WHERE game_id = ? %s
),
myStanding AS (
SELECT
ranking as myRank
FROM leaderboardsRanked
WHERE id = ?
),
lowestRanking AS (
SELECT MAX(ranking) AS lowestRank
FROM leaderboardsRanked
)
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking
BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
) AS ranking
FROM leaderboard JOIN charinfo on charinfo.id = leaderboard.character_id
WHERE game_id = ? %s
),
myStanding AS (
SELECT
ranking as myRank
FROM leaderboardsRanked
WHERE id = ?
),
lowestRanking AS (
SELECT MAX(ranking) AS lowestRank
FROM leaderboardsRanked
)
SELECT leaderboardsRanked.*, character_id, UNIX_TIMESTAMP(last_played) as lastPlayed, leaderboardsRanked.name, leaderboardsRanked.ranking FROM leaderboardsRanked, myStanding, lowestRanking
WHERE leaderboardsRanked.ranking
BETWEEN
LEAST(GREATEST(CAST(myRank AS SIGNED) - 5, %i), lowestRanking.lowestRank - 9)
AND
LEAST(GREATEST(myRank + 5, %i), lowestRanking.lowestRank)
ORDER BY ranking ASC;
)QUERY";
@@ -277,15 +277,15 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons
if (useUpdate) {
insertStatement =
R"QUERY(
UPDATE leaderboard
SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
UPDATE leaderboard
SET primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
timesPlayed = timesPlayed + 1 WHERE character_id = ? AND game_id = ?;
)QUERY";
} else {
insertStatement =
R"QUERY(
INSERT leaderboard SET
primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
INSERT leaderboard SET
primaryScore = %f, secondaryScore = %f, tertiaryScore = %f,
character_id = ?, game_id = ?;
)QUERY";
}
@@ -300,9 +300,8 @@ std::string FormatInsert(const Leaderboard::Type& type, const Score& score, cons
void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activityId, const float primaryScore, const float secondaryScore, const float tertiaryScore) {
const Leaderboard::Type leaderboardType = GetLeaderboardType(activityId);
auto* lookup = "SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;";
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt(lookup));
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT * FROM leaderboard WHERE character_id = ? AND game_id = ?;"));
query->setInt(1, playerID);
query->setInt(2, activityId);
std::unique_ptr<sql::ResultSet> myScoreResult(query->executeQuery());
@@ -337,6 +336,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
case Leaderboard::Type::UnusedLeaderboard4:
case Leaderboard::Type::Donations: {
oldScore.SetPrimaryScore(myScoreResult->getInt("primaryScore"));
newScore.SetPrimaryScore(oldScore.GetPrimaryScore() + newScore.GetPrimaryScore());
break;
}
case Leaderboard::Type::Racing: {
@@ -382,7 +382,7 @@ void LeaderboardManager::SaveScore(const LWOOBJID& playerID, const GameID activi
saveStatement->setInt(1, playerID);
saveStatement->setInt(2, activityId);
saveStatement->execute();
// track wins separately
if (leaderboardType == Leaderboard::Type::Racing && tertiaryScore != 0.0f) {
std::unique_ptr<sql::PreparedStatement> winUpdate(Database::CreatePreppedStmt("UPDATE leaderboard SET numWins = numWins + 1 WHERE character_id = ? AND game_id = ?;"));

View File

@@ -1,8 +1,14 @@
#include "TeamManager.h"
#include "EntityManager.h"
#include "Game.h"
#include "dConfig.h"
TeamManager* TeamManager::m_Address = nullptr; //For singleton method
Team::Team() {
lootOption = Game::config->GetValue("default_team_loot") == "0" ? 0 : 1;
}
TeamManager::TeamManager() {
}

View File

@@ -2,16 +2,15 @@
#include "Entity.h"
struct Team
{
struct Team {
Team();
LWOOBJID teamID = LWOOBJID_EMPTY;
char lootOption = 0;
std::vector<LWOOBJID> members{};
char lootRound = 0;
};
class TeamManager
{
class TeamManager {
public:
static TeamManager* Instance() {
if (!m_Address) {

View File

@@ -28,6 +28,8 @@
#include "eRenameResponse.h"
#include "eConnectionType.h"
#include "eChatInternalMessageType.h"
#include "BitStreamUtils.h"
#include "CheatDetection.h"
UserManager* UserManager::m_Address = nullptr;
@@ -203,40 +205,38 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) {
stmt->setUInt(1, u->GetAccountID());
sql::ResultSet* res = stmt->executeQuery();
if (res->rowsCount() > 0) {
std::vector<Character*>& chars = u->GetCharacters();
for (size_t i = 0; i < chars.size(); ++i) {
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
{
delete chars[i];
continue;
}
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
if (skillComponent != nullptr) {
skillComponent->Reset();
}
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
chars[i]->SaveXMLToDatabase();
chars[i]->GetEntity()->SetCharacter(nullptr);
std::vector<Character*>& chars = u->GetCharacters();
for (size_t i = 0; i < chars.size(); ++i) {
if (chars[i]->GetEntity() == nullptr) // We don't have entity data to save
{
delete chars[i];
continue;
}
chars.clear();
auto* skillComponent = chars[i]->GetEntity()->GetComponent<SkillComponent>();
while (res->next()) {
LWOOBJID objID = res->getUInt64(1);
Character* character = new Character(uint32_t(objID), u);
character->SetIsNewLogin();
chars.push_back(character);
if (skillComponent != nullptr) {
skillComponent->Reset();
}
Game::entityManager->DestroyEntity(chars[i]->GetEntity());
chars[i]->SaveXMLToDatabase();
chars[i]->GetEntity()->SetCharacter(nullptr);
delete chars[i];
}
chars.clear();
while (res->next()) {
LWOOBJID objID = res->getUInt64(1);
Character* character = new Character(uint32_t(objID), u);
character->SetIsNewLogin();
chars.push_back(character);
}
delete res;
@@ -251,21 +251,21 @@ void UserManager::CreateCharacter(const SystemAddress& sysAddr, Packet* packet)
std::string name = PacketUtils::ReadString(8, packet, true);
uint32_t firstNameIndex = PacketUtils::ReadPacketU32(74, packet);
uint32_t middleNameIndex = PacketUtils::ReadPacketU32(78, packet);
uint32_t lastNameIndex = PacketUtils::ReadPacketU32(82, packet);
uint32_t firstNameIndex = PacketUtils::ReadU32(74, packet);
uint32_t middleNameIndex = PacketUtils::ReadU32(78, packet);
uint32_t lastNameIndex = PacketUtils::ReadU32(82, packet);
std::string predefinedName = GetPredefinedName(firstNameIndex, middleNameIndex, lastNameIndex);
uint32_t shirtColor = PacketUtils::ReadPacketU32(95, packet);
uint32_t shirtStyle = PacketUtils::ReadPacketU32(99, packet);
uint32_t pantsColor = PacketUtils::ReadPacketU32(103, packet);
uint32_t hairStyle = PacketUtils::ReadPacketU32(107, packet);
uint32_t hairColor = PacketUtils::ReadPacketU32(111, packet);
uint32_t lh = PacketUtils::ReadPacketU32(115, packet);
uint32_t rh = PacketUtils::ReadPacketU32(119, packet);
uint32_t eyebrows = PacketUtils::ReadPacketU32(123, packet);
uint32_t eyes = PacketUtils::ReadPacketU32(127, packet);
uint32_t mouth = PacketUtils::ReadPacketU32(131, packet);
uint32_t shirtColor = PacketUtils::ReadU32(95, packet);
uint32_t shirtStyle = PacketUtils::ReadU32(99, packet);
uint32_t pantsColor = PacketUtils::ReadU32(103, packet);
uint32_t hairStyle = PacketUtils::ReadU32(107, packet);
uint32_t hairColor = PacketUtils::ReadU32(111, packet);
uint32_t lh = PacketUtils::ReadU32(115, packet);
uint32_t rh = PacketUtils::ReadU32(119, packet);
uint32_t eyebrows = PacketUtils::ReadU32(123, packet);
uint32_t eyes = PacketUtils::ReadU32(127, packet);
uint32_t mouth = PacketUtils::ReadU32(131, packet);
LOT shirtLOT = FindCharShirtID(shirtColor, shirtStyle);
LOT pantsLOT = FindCharPantsID(pantsColor);
@@ -387,20 +387,19 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
return;
}
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
LWOOBJID objectID = PacketUtils::ReadS64(8, packet);
uint32_t charID = static_cast<uint32_t>(objectID);
Game::logger->Log("UserManager", "Received char delete req for ID: %llu (%u)", objectID, charID);
//Check if this user has this character:
bool hasCharacter = false;
std::vector<Character*>& characters = u->GetCharacters();
for (size_t i = 0; i < characters.size(); ++i) {
if (characters[i]->GetID() == charID) { hasCharacter = true; }
}
bool hasCharacter = CheatDetection::VerifyLwoobjidIsSender(
objectID,
sysAddr,
CheckType::User,
"User %i tried to delete a character that it does not own!",
u->GetAccountID());
if (!hasCharacter) {
Game::logger->Log("UserManager", "User %i tried to delete a character that it does not own!", u->GetAccountID());
WorldPackets::SendCharacterDeleteResponse(sysAddr, false);
} else {
Game::logger->Log("UserManager", "Deleting character %i", charID);
@@ -423,7 +422,7 @@ void UserManager::DeleteCharacter(const SystemAddress& sysAddr, Packet* packet)
stmt->execute();
delete stmt;
CBITSTREAM;
PacketUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT_INTERNAL, eChatInternalMessageType::PLAYER_REMOVED_NOTIFICATION);
bitStream.Write(objectID);
Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE, 0, Game::chatSysAddr, false);
}
@@ -483,7 +482,7 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
return;
}
LWOOBJID objectID = PacketUtils::ReadPacketS64(8, packet);
LWOOBJID objectID = PacketUtils::ReadS64(8, packet);
GeneralUtils::ClearBit(objectID, eObjectBits::CHARACTER);
GeneralUtils::ClearBit(objectID, eObjectBits::PERSISTENT);
@@ -495,16 +494,24 @@ void UserManager::RenameCharacter(const SystemAddress& sysAddr, Packet* packet)
Character* character = nullptr;
//Check if this user has this character:
bool hasCharacter = false;
std::vector<Character*>& characters = u->GetCharacters();
for (size_t i = 0; i < characters.size(); ++i) {
if (characters[i]->GetID() == charID) { hasCharacter = true; character = characters[i]; }
}
bool ownsCharacter = CheatDetection::VerifyLwoobjidIsSender(
objectID,
sysAddr,
CheckType::User,
"User %i tried to rename a character that it does not own!",
u->GetAccountID());
if (!hasCharacter || !character) {
Game::logger->Log("UserManager", "User %i tried to rename a character that it does not own!", u->GetAccountID());
std::find_if(u->GetCharacters().begin(), u->GetCharacters().end(), [&](Character* c) {
if (c->GetID() == charID) {
character = c;
return true;
}
return false;
});
if (!ownsCharacter || !character) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::UNKNOWN_ERROR);
} else if (hasCharacter && character) {
} else if (ownsCharacter && character) {
if (newName == character->GetName()) {
WorldPackets::SendCharacterRenameResponse(sysAddr, eRenameResponse::NAME_UNAVAILABLE);
return;

View File

@@ -20,134 +20,114 @@ void AreaOfEffectBehavior::Handle(BehaviorContext* context, RakNet::BitStream* b
return;
}
if (targetCount > this->m_maxTargets) {
if (this->m_useTargetPosition && branch.target == LWOOBJID_EMPTY) return;
if (targetCount == 0){
PlayFx(u"miss", context->originator);
return;
}
std::vector<LWOOBJID> targets;
if (targetCount > this->m_maxTargets) {
Game::logger->Log("AreaOfEffectBehavior", "Serialized size is greater than max targets! Size: %i, Max: %i", targetCount, this->m_maxTargets);
return;
}
auto caster = context->caster;
if (this->m_useTargetAsCaster) context->caster = branch.target;
std::vector<LWOOBJID> targets;
targets.reserve(targetCount);
for (auto i = 0u; i < targetCount; ++i) {
LWOOBJID target{};
if (!bitStream->Read(target)) {
Game::logger->Log("AreaOfEffectBehavior", "failed to read in target %i from bitStream, aborting target Handle!", i);
return;
};
targets.push_back(target);
}
for (auto target : targets) {
branch.target = target;
this->m_action->Handle(context, bitStream, branch);
}
context->caster = caster;
PlayFx(u"cast", context->originator);
}
void AreaOfEffectBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
auto* self = Game::entityManager->GetEntity(context->caster);
if (self == nullptr) {
Game::logger->Log("AreaOfEffectBehavior", "Invalid self for (%llu)!", context->originator);
auto* caster = Game::entityManager->GetEntity(context->caster);
if (!caster) return;
return;
// determine the position we are casting the AOE from
auto reference = branch.isProjectile ? branch.referencePosition : caster->GetPosition();
if (this->m_useTargetPosition) {
if (branch.target == LWOOBJID_EMPTY) return;
auto branchTarget = Game::entityManager->GetEntity(branch.target);
if (branchTarget) reference = branchTarget->GetPosition();
}
auto reference = branch.isProjectile ? branch.referencePosition : self->GetPosition();
reference += this->m_offset;
std::vector<Entity*> targets;
auto* presetTarget = Game::entityManager->GetEntity(branch.target);
if (presetTarget != nullptr) {
if (this->m_radius * this->m_radius >= Vector3::DistanceSquared(reference, presetTarget->GetPosition())) {
targets.push_back(presetTarget);
}
}
int32_t includeFaction = m_includeFaction;
if (self->GetLOT() == 14466) // TODO: Fix edge case
{
includeFaction = 1;
}
// Gets all of the valid targets, passing in if should target enemies and friends
for (auto validTarget : context->GetValidTargets(m_ignoreFaction, includeFaction, m_TargetSelf == 1, m_targetEnemy == 1, m_targetFriend == 1)) {
auto* entity = Game::entityManager->GetEntity(validTarget);
if (entity == nullptr) {
Game::logger->Log("AreaOfEffectBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
continue;
}
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
continue;
}
auto* destroyableComponent = entity->GetComponent<DestroyableComponent>();
if (destroyableComponent == nullptr) {
continue;
}
if (destroyableComponent->HasFaction(m_ignoreFaction)) {
continue;
}
const auto distance = Vector3::DistanceSquared(reference, entity->GetPosition());
if (this->m_radius * this->m_radius >= distance && (this->m_maxTargets == 0 || targets.size() < this->m_maxTargets)) {
targets.push_back(entity);
}
}
std::vector<Entity*> targets {};
targets = Game::entityManager->GetEntitiesByProximity(reference, this->m_radius);
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
// sort by distance
std::sort(targets.begin(), targets.end(), [reference](Entity* a, Entity* b) {
const auto aDistance = Vector3::DistanceSquared(a->GetPosition(), reference);
const auto bDistance = Vector3::DistanceSquared(b->GetPosition(), reference);
const auto aDistance = NiPoint3::Distance(a->GetPosition(), reference);
const auto bDistance = NiPoint3::Distance(b->GetPosition(), reference);
return aDistance < bDistance;
}
);
return aDistance > bDistance;
});
// resize if we have more than max targets allows
if (targets.size() > this->m_maxTargets) targets.resize(this->m_maxTargets);
const uint32_t size = targets.size();
bitStream->Write<uint32_t>(targets.size());
bitStream->Write(size);
if (size == 0) {
if (targets.size() == 0) {
PlayFx(u"miss", context->originator);
return;
}
} else {
context->foundTarget = true;
// write all the targets to the bitstream
for (auto* target : targets) {
bitStream->Write(target->GetObjectID());
}
context->foundTarget = true;
for (auto* target : targets) {
bitStream->Write(target->GetObjectID());
PlayFx(u"cast", context->originator, target->GetObjectID());
}
for (auto* target : targets) {
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
// then cast all the actions
for (auto* target : targets) {
branch.target = target->GetObjectID();
this->m_action->Calculate(context, bitStream, branch);
}
PlayFx(u"cast", context->originator);
}
}
void AreaOfEffectBehavior::Load() {
this->m_action = GetAction("action");
this->m_action = GetAction("action"); // required
this->m_radius = GetFloat("radius", 0.0f); // required
this->m_maxTargets = GetInt("max targets", 100);
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
this->m_useTargetPosition = GetBoolean("use_target_position", false);
this->m_useTargetAsCaster = GetBoolean("use_target_as_caster", false);
this->m_offset = NiPoint3(
GetFloat("offset_x", 0.0f),
GetFloat("offset_y", 0.0f),
GetFloat("offset_z", 0.0f)
);
this->m_radius = GetFloat("radius");
this->m_maxTargets = GetInt("max targets");
this->m_ignoreFaction = GetInt("ignore_faction");
this->m_includeFaction = GetInt("include_faction");
this->m_TargetSelf = GetInt("target_self");
this->m_targetEnemy = GetInt("target_enemy");
this->m_targetFriend = GetInt("target_friend");
// params after this are needed for filter targets
const auto parameters = GetParameterNames();
for (const auto& parameter : parameters) {
if (parameter.first.rfind("include_faction", 0) == 0) {
this->m_includeFactionList.push_front(parameter.second);
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
this->m_ignoreFactionList.push_front(parameter.second);
}
}
this->m_targetSelf = GetBoolean("target_self", false);
this->m_targetEnemy = GetBoolean("target_enemy", false);
this->m_targetFriend = GetBoolean("target_friend", false);
this->m_targetTeam = GetBoolean("target_team", false);
}

View File

@@ -1,34 +1,26 @@
#pragma once
#include "Behavior.h"
#include <forward_list>
class AreaOfEffectBehavior final : public Behavior
{
public:
Behavior* m_action;
uint32_t m_maxTargets;
float m_radius;
int32_t m_ignoreFaction;
int32_t m_includeFaction;
int32_t m_TargetSelf;
int32_t m_targetEnemy;
int32_t m_targetFriend;
/*
* Inherited
*/
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {
}
explicit AreaOfEffectBehavior(const uint32_t behaviorId) : Behavior(behaviorId) {}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
private:
Behavior* m_action;
uint32_t m_maxTargets;
float m_radius;
bool m_useTargetPosition;
bool m_useTargetAsCaster;
NiPoint3 m_offset;
std::forward_list<int32_t> m_ignoreFactionList {};
std::forward_list<int32_t> m_includeFactionList {};
bool m_targetSelf;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
};

View File

@@ -175,7 +175,7 @@ Behavior* Behavior::CreateBehavior(const uint32_t behaviorId) {
case BehaviorTemplates::BEHAVIOR_SPEED:
behavior = new SpeedBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
case BehaviorTemplates::BEHAVIOR_DARK_INSPIRATION:
behavior = new DarkInspirationBehavior(behaviorId);
break;
case BehaviorTemplates::BEHAVIOR_LOOT_BUFF:

View File

@@ -6,7 +6,7 @@
#include "Game.h"
#include "dLogger.h"
#include "dServer.h"
#include "PacketUtils.h"
#include "BitStreamUtils.h"
#include <sstream>
@@ -15,6 +15,7 @@
#include "PhantomPhysicsComponent.h"
#include "RebuildComponent.h"
#include "eReplicaComponentType.h"
#include "TeamManager.h"
#include "eConnectionType.h"
BehaviorSyncEntry::BehaviorSyncEntry() {
@@ -253,7 +254,7 @@ bool BehaviorContext::CalculateUpdate(const float deltaTime) {
// Write message
RakNet::BitStream message;
PacketUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
BitStreamUtils::WriteHeader(message, eConnectionType::CLIENT, eClientMessageType::GAME_MSG);
message.Write(this->originator);
echo.Serialize(&message);
@@ -307,46 +308,123 @@ void BehaviorContext::Reset() {
this->scheduledUpdates.clear();
}
std::vector<LWOOBJID> BehaviorContext::GetValidTargets(int32_t ignoreFaction, int32_t includeFaction, bool targetSelf, bool targetEnemy, bool targetFriend) const {
auto* entity = Game::entityManager->GetEntity(this->caster);
void BehaviorContext::FilterTargets(std::vector<Entity*>& targets, std::forward_list<int32_t>& ignoreFactionList, std::forward_list<int32_t>& includeFactionList, bool targetSelf, bool targetEnemy, bool targetFriend, bool targetTeam) const {
std::vector<LWOOBJID> targets;
if (entity == nullptr) {
Game::logger->Log("BehaviorContext", "Invalid entity for (%llu)!", this->originator);
return targets;
// if we aren't targeting anything, then clear the targets vector
if (!targetSelf && !targetEnemy && !targetFriend && !targetTeam && ignoreFactionList.empty() && includeFactionList.empty()) {
targets.clear();
return;
}
if (!ignoreFaction && !includeFaction) {
for (auto entry : entity->GetTargetsInPhantom()) {
auto* instance = Game::entityManager->GetEntity(entry);
if (instance == nullptr) {
continue;
}
targets.push_back(entry);
}
// if the caster is not there, return empty targets list
auto* caster = Game::entityManager->GetEntity(this->caster);
if (!caster) {
Game::logger->LogDebug("BehaviorContext", "Invalid caster for (%llu)!", this->originator);
targets.clear();
return;
}
if (ignoreFaction || includeFaction || (!entity->HasComponent(eReplicaComponentType::PHANTOM_PHYSICS) && targets.empty())) {
DestroyableComponent* destroyableComponent;
if (!entity->TryGetComponent(eReplicaComponentType::DESTROYABLE, destroyableComponent)) {
return targets;
auto index = targets.begin();
while (index != targets.end()) {
auto candidate = *index;
// make sure we don't have a nullptr
if (!candidate) {
index = targets.erase(index);
continue;
}
auto entities = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::CONTROLLABLE_PHYSICS);
for (auto* candidate : entities) {
const auto id = candidate->GetObjectID();
// handle targeting the caster
if (candidate == caster){
// if we aren't targeting self, erase, otherise increment and continue
if (!targetSelf) index = targets.erase(index);
else index++;
continue;
}
if ((id != entity->GetObjectID() || targetSelf) && destroyableComponent->CheckValidity(id, ignoreFaction || includeFaction, targetEnemy, targetFriend)) {
targets.push_back(id);
// make sure that the entity is targetable
if (!CheckTargetingRequirements(candidate)) {
index = targets.erase(index);
continue;
}
// get factions to check against
// CheckTargetingRequirements checks for a destroyable component
// but we check again because bounds check are necessary
auto candidateDestroyableComponent = candidate->GetComponent<DestroyableComponent>();
if (!candidateDestroyableComponent) {
index = targets.erase(index);
continue;
}
// if they are dead, then earse and continue
if (candidateDestroyableComponent->GetIsDead()){
index = targets.erase(index);
continue;
}
// if their faction is explicitly included, increment and continue
auto candidateFactions = candidateDestroyableComponent->GetFactionIDs();
if (CheckFactionList(includeFactionList, candidateFactions)){
index++;
continue;
}
// check if they are a team member
if (targetTeam){
auto* team = TeamManager::Instance()->GetTeam(this->caster);
if (team){
// 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()){
index++;
continue;
}
}
}
}
return targets;
// if the caster doesn't have a destroyable component, return an empty targets list
auto* casterDestroyableComponent = caster->GetComponent<DestroyableComponent>();
if (!casterDestroyableComponent) {
targets.clear();
return;
}
// if we arent targeting a friend, and they are a friend OR
// if we are not targeting enemies and they are an enemy OR.
// if we are ignoring their faction is explicitly ignored
// erase and continue
auto isEnemy = casterDestroyableComponent->IsEnemy(candidate);
if ((!targetFriend && !isEnemy) ||
(!targetEnemy && isEnemy) ||
CheckFactionList(ignoreFactionList, candidateFactions)) {
index = targets.erase(index);
continue;
}
index++;
}
return;
}
// some basic checks as well as the check that matters for this: if the quickbuild is complete
bool BehaviorContext::CheckTargetingRequirements(const Entity* target) const {
// if the target is a nullptr, then it's not valid
if (!target) return false;
// ignore quickbuilds that aren't completed
auto* targetQuickbuildComponent = target->GetComponent<RebuildComponent>();
if (targetQuickbuildComponent && targetQuickbuildComponent->GetState() != eRebuildState::COMPLETED) return false;
return true;
}
// 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 {
if (factionList.empty() || objectsFactions.empty()) return false;
for (auto faction : factionList){
if(std::find(objectsFactions.begin(), objectsFactions.end(), faction) != objectsFactions.end()) return true;
}
return false;
}

View File

@@ -6,6 +6,7 @@
#include "GameMessages.h"
#include <vector>
#include <forward_list>
class Behavior;
@@ -106,7 +107,11 @@ struct BehaviorContext
void Reset();
std::vector<LWOOBJID> GetValidTargets(int32_t ignoreFaction = 0, int32_t includeFaction = 0, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false) const;
void FilterTargets(std::vector<Entity*>& targetsReference, std::forward_list<int32_t>& ignoreFaction, std::forward_list<int32_t>& includeFaction, const bool targetSelf = false, const bool targetEnemy = true, const bool targetFriend = false, const bool targetTeam = false) const;
bool CheckTargetingRequirements(const Entity* target) const;
bool CheckFactionList(std::forward_list<int32_t>& factionList, std::vector<int32_t>& objectsFactions) const;
explicit BehaviorContext(LWOOBJID originator, bool calculation = false);

View File

@@ -2,9 +2,9 @@
#ifndef BEHAVIORSLOT_H
#define BEHAVIORSLOT_H
#include <cstdint>
enum class BehaviorSlot
{
enum class BehaviorSlot : int32_t {
Invalid = -1,
Primary,
Offhand,

View File

@@ -42,8 +42,7 @@ void OverTimeBehavior::Load() {
// Since m_Action is a skillID and not a behavior, get is correlated behaviorID.
CDSkillBehaviorTable* skillTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto skillData = skillTable->GetSkillByID(m_Action);
if (skillData) m_ActionBehaviorId = skillData->behaviorID;
m_ActionBehaviorId = skillTable->GetSkillByID(m_Action).behaviorID;
m_Delay = GetFloat("delay");
m_NumIntervals = GetInt("num_intervals");

View File

@@ -12,16 +12,24 @@
#include <vector>
void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
if (this->m_targetEnemy && this->m_usePickedTarget && branch.target > 0) {
this->m_action->Handle(context, bitStream, branch);
std::vector<Entity*> targets = {};
return;
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
auto target = Game::entityManager->GetEntity(branch.target);
if (!target) Game::logger->Log("TacArcBehavior", "target %llu is null", branch.target);
else {
targets.push_back(target);
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
if (!targets.empty()) {
this->m_action->Handle(context, bitStream, branch);
return;
}
}
}
bool hit = false;
if (!bitStream->Read(hit)) {
Game::logger->Log("TacArcBehavior", "Unable to read hit from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
bool hasTargets = false;
if (!bitStream->Read(hasTargets)) {
Game::logger->Log("TacArcBehavior", "Unable to read hasTargets from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
return;
};
@@ -35,26 +43,23 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
if (blocked) {
this->m_blockedAction->Handle(context, bitStream, branch);
return;
}
}
if (hit) {
if (hasTargets) {
uint32_t count = 0;
if (!bitStream->Read(count)) {
Game::logger->Log("TacArcBehavior", "Unable to read count from bitStream, aborting Handle! %i", bitStream->GetNumberOfUnreadBits());
return;
};
if (count > m_maxTargets && m_maxTargets > 0) {
count = m_maxTargets;
if (count > m_maxTargets) {
Game::logger->Log("TacArcBehavior", "Bitstream has too many targets Max:%i Recv:%i", this->m_maxTargets, count);
return;
}
std::vector<LWOOBJID> targets;
for (auto i = 0u; i < count; ++i) {
for (auto i = 0u; i < count; i++) {
LWOOBJID id{};
if (!bitStream->Read(id)) {
@@ -62,17 +67,19 @@ void TacArcBehavior::Handle(BehaviorContext* context, RakNet::BitStream* bitStre
return;
};
targets.push_back(id);
if (id != LWOOBJID_EMPTY) {
auto* canidate = Game::entityManager->GetEntity(id);
if (canidate) targets.push_back(canidate);
} else {
Game::logger->Log("TacArcBehavior", "Bitstream has LWOOBJID_EMPTY as a target!");
}
}
for (auto target : targets) {
branch.target = target;
branch.target = target->GetObjectID();
this->m_action->Handle(context, bitStream, branch);
}
} else {
this->m_missAction->Handle(context, bitStream, branch);
}
} else this->m_missAction->Handle(context, bitStream, branch);
}
void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) {
@@ -82,23 +89,15 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
return;
}
const auto* destroyableComponent = self->GetComponent<DestroyableComponent>();
if ((this->m_usePickedTarget || context->clientInitalized) && branch.target > 0) {
const auto* target = Game::entityManager->GetEntity(branch.target);
if (target == nullptr) {
std::vector<Entity*> targets = {};
if (this->m_usePickedTarget && branch.target != LWOOBJID_EMPTY) {
auto target = Game::entityManager->GetEntity(branch.target);
targets.push_back(target);
context->FilterTargets(targets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
if (!targets.empty()) {
this->m_action->Handle(context, bitStream, branch);
return;
}
// If the game is specific about who to target, check that
if (destroyableComponent == nullptr || ((!m_targetFriend && !m_targetEnemy
|| m_targetFriend && destroyableComponent->IsFriend(target)
|| m_targetEnemy && destroyableComponent->IsEnemy(target)))) {
this->m_action->Calculate(context, bitStream, branch);
}
return;
}
auto* combatAi = self->GetComponent<BaseCombatAIComponent>();
@@ -107,50 +106,25 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
auto reference = self->GetPosition(); //+ m_offset;
std::vector<Entity*> targets;
targets.clear();
std::vector<LWOOBJID> validTargets;
std::vector<Entity*> validTargets = Game::entityManager->GetEntitiesByProximity(reference, this->m_maxRange);
if (combatAi != nullptr) {
if (combatAi->GetTarget() != LWOOBJID_EMPTY) {
validTargets.push_back(combatAi->GetTarget());
}
}
// Find all valid targets, based on whether we target enemies or friends
for (const auto& contextTarget : context->GetValidTargets()) {
if (destroyableComponent != nullptr) {
const auto* targetEntity = Game::entityManager->GetEntity(contextTarget);
if (m_targetEnemy && destroyableComponent->IsEnemy(targetEntity)
|| m_targetFriend && destroyableComponent->IsFriend(targetEntity)) {
validTargets.push_back(contextTarget);
}
} else {
validTargets.push_back(contextTarget);
}
}
// filter all valid targets, based on whether we target enemies or friends
context->FilterTargets(validTargets, this->m_ignoreFactionList, this->m_includeFactionList, this->m_targetSelf, this->m_targetEnemy, this->m_targetFriend, this->m_targetTeam);
for (auto validTarget : validTargets) {
if (targets.size() >= this->m_maxTargets) {
break;
}
auto* entity = Game::entityManager->GetEntity(validTarget);
if (entity == nullptr) {
Game::logger->Log("TacArcBehavior", "Invalid target (%llu) for (%llu)!", validTarget, context->originator);
if (std::find(targets.begin(), targets.end(), validTarget) != targets.end()) {
continue;
}
if (std::find(targets.begin(), targets.end(), entity) != targets.end()) {
continue;
}
if (validTarget->GetIsDead()) continue;
if (entity->GetIsDead()) continue;
const auto otherPosition = entity->GetPosition();
const auto otherPosition = validTarget->GetPosition();
const auto heightDifference = std::abs(otherPosition.y - casterPosition.y);
@@ -180,8 +154,8 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
const float degreeAngle = std::abs(Vector3::Angle(forward, normalized) * (180 / 3.14) - 180);
if (distance >= this->m_minDistance && this->m_maxDistance >= distance && degreeAngle <= 2 * this->m_angle) {
targets.push_back(entity);
if (distance >= this->m_minRange && this->m_maxRange >= distance && degreeAngle <= 2 * this->m_angle) {
targets.push_back(validTarget);
}
}
@@ -228,43 +202,48 @@ void TacArcBehavior::Calculate(BehaviorContext* context, RakNet::BitStream* bitS
}
void TacArcBehavior::Load() {
this->m_usePickedTarget = GetBoolean("use_picked_target");
this->m_maxRange = GetFloat("max range");
this->m_height = GetFloat("height", 2.2f);
this->m_distanceWeight = GetFloat("distance_weight", 0.0f);
this->m_angleWeight = GetFloat("angle_weight", 0.0f);
this->m_angle = GetFloat("angle", 45.0f);
this->m_minRange = GetFloat("min range", 0.0f);
this->m_offset = NiPoint3(
GetFloat("offset_x", 0.0f),
GetFloat("offset_y", 0.0f),
GetFloat("offset_z", 0.0f)
);
this->m_method = GetInt("method", 1);
this->m_upperBound = GetFloat("upper_bound", 4.4f);
this->m_lowerBound = GetFloat("lower_bound", 0.4f);
this->m_usePickedTarget = GetBoolean("use_picked_target", false);
this->m_useTargetPostion = GetBoolean("use_target_position", false);
this->m_checkEnv = GetBoolean("check_env", false);
this->m_useAttackPriority = GetBoolean("use_attack_priority", false);
this->m_action = GetAction("action");
this->m_missAction = GetAction("miss action");
this->m_checkEnv = GetBoolean("check_env");
this->m_blockedAction = GetAction("blocked action");
this->m_minDistance = GetFloat("min range");
this->m_maxTargets = GetInt("max targets", 100);
if (this->m_maxTargets == 0) this->m_maxTargets = 100;
this->m_maxDistance = GetFloat("max range");
this->m_farHeight = GetFloat("far_height", 5.0f);
this->m_farWidth = GetFloat("far_width", 5.0f);
this->m_nearHeight = GetFloat("near_height", 5.0f);
this->m_nearWidth = GetFloat("near_width", 5.0f);
this->m_maxTargets = GetInt("max targets");
this->m_targetEnemy = GetBoolean("target_enemy");
this->m_targetFriend = GetBoolean("target_friend");
this->m_targetTeam = GetBoolean("target_team");
this->m_angle = GetFloat("angle");
this->m_upperBound = GetFloat("upper_bound");
this->m_lowerBound = GetFloat("lower_bound");
this->m_farHeight = GetFloat("far_height");
this->m_farWidth = GetFloat("far_width");
this->m_method = GetInt("method");
this->m_offset = {
GetFloat("offset_x"),
GetFloat("offset_y"),
GetFloat("offset_z")
};
// params after this are needed for filter targets
const auto parameters = GetParameterNames();
for (const auto& parameter : parameters) {
if (parameter.first.rfind("include_faction", 0) == 0) {
this->m_includeFactionList.push_front(parameter.second);
} else if (parameter.first.rfind("ignore_faction", 0) == 0) {
this->m_ignoreFactionList.push_front(parameter.second);
}
}
this->m_targetSelf = GetBoolean("target_caster", false);
this->m_targetEnemy = GetBoolean("target_enemy", false);
this->m_targetFriend = GetBoolean("target_friend", false);
this->m_targetTeam = GetBoolean("target_team", false);
}

View File

@@ -2,56 +2,42 @@
#include "Behavior.h"
#include "dCommonVars.h"
#include "NiPoint3.h"
#include <forward_list>
class TacArcBehavior final : public Behavior
{
class TacArcBehavior final : public Behavior {
public:
bool m_usePickedTarget;
Behavior* m_action;
bool m_checkEnv;
Behavior* m_missAction;
Behavior* m_blockedAction;
float m_minDistance;
float m_maxDistance;
uint32_t m_maxTargets;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
float m_angle;
float m_upperBound;
float m_lowerBound;
float m_farHeight;
float m_farWidth;
uint32_t m_method;
NiPoint3 m_offset;
/*
* Inherited
*/
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {
}
explicit TacArcBehavior(const uint32_t behavior_id) : Behavior(behavior_id) {}
void Handle(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Calculate(BehaviorContext* context, RakNet::BitStream* bitStream, BehaviorBranchContext branch) override;
void Load() override;
private:
float m_maxRange;
float m_height;
float m_distanceWeight;
float m_angleWeight;
float m_angle;
float m_minRange;
NiPoint3 m_offset;
uint32_t m_method;
float m_upperBound;
float m_lowerBound;
bool m_usePickedTarget;
bool m_useTargetPostion;
bool m_checkEnv;
bool m_useAttackPriority;
Behavior* m_action;
Behavior* m_missAction;
Behavior* m_blockedAction;
uint32_t m_maxTargets;
float m_farHeight;
float m_farWidth;
float m_nearHeight;
float m_nearWidth;
std::forward_list<int32_t> m_ignoreFactionList {};
std::forward_list<int32_t> m_includeFactionList {};
bool m_targetSelf;
bool m_targetEnemy;
bool m_targetFriend;
bool m_targetTeam;
};

View File

@@ -318,7 +318,7 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
// Speed towards start position
if (m_MovementAI != nullptr) {
m_MovementAI->SetHaltDistance(0);
m_MovementAI->SetSpeed(m_PursuitSpeed);
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition);
}
@@ -382,8 +382,6 @@ void BaseCombatAIComponent::CalculateCombat(const float deltaTime) {
}
LWOOBJID BaseCombatAIComponent::FindTarget() {
//const auto reference = m_MovementAI == nullptr ? m_StartPosition : m_MovementAI->ApproximateLocation();
NiPoint3 reference = m_StartPosition;
if (m_MovementAI) reference = m_MovementAI->ApproximateLocation();
@@ -522,7 +520,7 @@ bool BaseCombatAIComponent::IsMech() {
}
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void BaseCombatAIComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(m_DirtyStateOrTarget || bIsInitialUpdate);
if (m_DirtyStateOrTarget || bIsInitialUpdate) {
outBitStream->Write(uint32_t(m_State));
@@ -660,17 +658,17 @@ void BaseCombatAIComponent::Wander() {
destination.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(destination);
}
if (Vector3::DistanceSquared(destination, m_MovementAI->GetCurrentPosition()) < 2 * 2) {
if (Vector3::DistanceSquared(destination, m_MovementAI->GetParent()->GetPosition()) < 2 * 2) {
m_MovementAI->Stop();
return;
}
m_MovementAI->SetSpeed(m_TetherSpeed);
m_MovementAI->SetMaxSpeed(m_TetherSpeed);
m_MovementAI->SetDestination(destination);
m_Timer += (m_MovementAI->GetCurrentPosition().x - destination.x) / m_TetherSpeed;
m_Timer += (m_MovementAI->GetParent()->GetPosition().x - destination.x) / m_TetherSpeed;
}
void BaseCombatAIComponent::OnAggro() {
@@ -685,21 +683,21 @@ void BaseCombatAIComponent::OnAggro() {
m_MovementAI->SetHaltDistance(m_AttackRadius);
NiPoint3 targetPos = target->GetPosition();
NiPoint3 currentPos = m_MovementAI->GetCurrentPosition();
NiPoint3 currentPos = m_MovementAI->GetParent()->GetPosition();
// If the player's position is within range, attack
if (Vector3::DistanceSquared(currentPos, targetPos) <= m_AttackRadius * m_AttackRadius) {
m_MovementAI->Stop();
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
{
m_MovementAI->SetSpeed(m_PursuitSpeed);
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition);
} else //Chase the player's new position
{
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
m_MovementAI->SetSpeed(m_PursuitSpeed);
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(targetPos);
@@ -725,7 +723,7 @@ void BaseCombatAIComponent::OnTether() {
m_MovementAI->Stop();
} else if (Vector3::DistanceSquared(m_StartPosition, targetPos) > m_HardTetherRadius * m_HardTetherRadius) //Return to spawn if we're too far
{
m_MovementAI->SetSpeed(m_PursuitSpeed);
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(m_StartPosition);
@@ -733,7 +731,7 @@ void BaseCombatAIComponent::OnTether() {
} else {
if (IsMech() && Vector3::DistanceSquared(targetPos, currentPos) > m_AttackRadius * m_AttackRadius * 3 * 3) return;
m_MovementAI->SetSpeed(m_PursuitSpeed);
m_MovementAI->SetMaxSpeed(m_PursuitSpeed);
m_MovementAI->SetDestination(targetPos);
}

View File

@@ -53,7 +53,7 @@ public:
~BaseCombatAIComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* Get the current behavioral state of the enemy

View File

@@ -22,7 +22,7 @@ BouncerComponent::BouncerComponent(Entity* parent) : Component(parent) {
BouncerComponent::~BouncerComponent() {
}
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void BouncerComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(m_PetEnabled);
if (m_PetEnabled) {
outBitStream->Write(m_PetBouncerEnabled);

View File

@@ -17,7 +17,7 @@ public:
BouncerComponent(Entity* parentEntity);
~BouncerComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
Entity* GetParentEntity() const;

View File

@@ -20,7 +20,7 @@ BuffComponent::BuffComponent(Entity* parent) : Component(parent) {
BuffComponent::~BuffComponent() {
}
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void BuffComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (!bIsInitialUpdate) return;
if (m_Buffs.empty()) {
outBitStream->Write0();
@@ -104,12 +104,7 @@ void BuffComponent::ApplyBuff(const int32_t id, const float duration, const LWOO
if (parameter.name == "overtime") {
auto* behaviorTemplateTable = CDClientManager::Instance().GetTable<CDSkillBehaviorTable>();
auto skillInfo = behaviorTemplateTable->GetSkillByID(parameter.values[0]);
if (skillInfo) {
behaviorID = skillInfo->behaviorID;
} else {
Game::logger->Log("BuffComponent", "Failed to find skill info for skill ID %d!", parameter.values[0]);
}
behaviorID = behaviorTemplateTable->GetSkillByID(parameter.values[0]).behaviorID;
stacks = static_cast<int32_t>(parameter.values[1]);
tick = parameter.values[2];
const auto unknown2 = parameter.values[3]; // Always 0

View File

@@ -54,7 +54,7 @@ public:
void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void Update(float deltaTime) override;

View File

@@ -6,6 +6,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"Component.cpp"
"ControllablePhysicsComponent.cpp"
"DestroyableComponent.cpp"
"DonationVendorComponent.cpp"
"InventoryComponent.cpp"
"LevelProgressionComponent.cpp"
"LUPExhibitComponent.cpp"
@@ -17,6 +18,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"MovingPlatformComponent.cpp"
"PetComponent.cpp"
"PhantomPhysicsComponent.cpp"
"PhysicsComponent.cpp"
"PlayerForcedMovementComponent.cpp"
"PossessableComponent.cpp"
"PossessorComponent.cpp"
@@ -30,7 +32,7 @@ set(DGAME_DCOMPONENTS_SOURCES "BaseCombatAIComponent.cpp"
"RebuildComponent.cpp"
"RenderComponent.cpp"
"RigidbodyPhantomPhysicsComponent.cpp"
"RocketLaunchLupComponent.cpp"
"MultiZoneEntranceComponent.cpp"
"RocketLaunchpadControlComponent.cpp"
"ScriptedActivityComponent.cpp"
"ShootingGalleryComponent.cpp"

View File

@@ -70,7 +70,7 @@ bool CharacterComponent::LandingAnimDisabled(int zoneID) {
CharacterComponent::~CharacterComponent() {
}
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) {
outBitStream->Write0();

View File

@@ -70,7 +70,7 @@ public:
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* Updates the rocket configuration using a LOT string separated by commas
@@ -276,6 +276,10 @@ public:
*/
void UpdateClientMinimap(bool showFaction, std::string ventureVisionType) const;
void SetCurrentInteracting(LWOOBJID objectID) {m_CurrentInteracting = objectID;};
LWOOBJID GetCurrentInteracting() {return m_CurrentInteracting;};
/**
* Character info regarding this character, including clothing styles, etc.
*/
@@ -560,6 +564,8 @@ private:
* ID of the last rocket used
*/
LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY;
LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY;
};
#endif // CHARACTERCOMPONENT_H

View File

@@ -28,3 +28,7 @@ void Component::UpdateXml(tinyxml2::XMLDocument* doc) {
void Component::LoadFromXml(tinyxml2::XMLDocument* doc) {
}
void Component::Serialize(RakNet::BitStream* outBitStream, bool isConstruction) {
}

View File

@@ -43,6 +43,8 @@ public:
*/
virtual void LoadFromXml(tinyxml2::XMLDocument* doc);
virtual void Serialize(RakNet::BitStream* outBitStream, bool isConstruction);
protected:
/**

View File

@@ -15,15 +15,12 @@
#include "LevelProgressionComponent.h"
#include "eStateChangeType.h"
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : Component(entity) {
m_Position = {};
m_Rotation = NiQuaternion::IDENTITY;
ControllablePhysicsComponent::ControllablePhysicsComponent(Entity* entity) : PhysicsComponent(entity) {
m_Velocity = {};
m_AngularVelocity = {};
m_InJetpackMode = false;
m_IsOnGround = true;
m_IsOnRail = false;
m_DirtyPosition = true;
m_DirtyVelocity = true;
m_DirtyAngularVelocity = true;
m_dpEntity = nullptr;
@@ -74,7 +71,7 @@ void ControllablePhysicsComponent::Update(float deltaTime) {
}
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void ControllablePhysicsComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
//If this is a creation, then we assume the position is dirty, even when it isn't.
//This is because new clients will still need to receive the position.
//if (bIsInitialUpdate) m_DirtyPosition = true;
@@ -181,12 +178,6 @@ void ControllablePhysicsComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_DirtyPosition = true;
}
void ControllablePhysicsComponent::ResetFlags() {
m_DirtyAngularVelocity = false;
m_DirtyPosition = false;
m_DirtyVelocity = false;
}
void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
tinyxml2::XMLElement* character = doc->FirstChildElement("obj")->FirstChildElement("char");
if (!character) {
@@ -208,26 +199,14 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument* doc) {
}
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {
if (m_Static) {
return;
}
m_Position.x = pos.x;
m_Position.y = pos.y;
m_Position.z = pos.z;
m_DirtyPosition = true;
if (m_Static) return;
PhysicsComponent::SetPosition(pos);
if (m_dpEntity) m_dpEntity->SetPosition(pos);
}
void ControllablePhysicsComponent::SetRotation(const NiQuaternion& rot) {
if (m_Static) {
return;
}
m_Rotation = rot;
m_DirtyPosition = true;
if (m_Static) return;
PhysicsComponent::SetRotation(rot);
if (m_dpEntity) m_dpEntity->SetRotation(rot);
}

View File

@@ -6,7 +6,7 @@
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "tinyxml2.h"
#include "Component.h"
#include "PhysicsComponent.h"
#include "dpCollisionChecks.h"
#include "PhantomPhysicsComponent.h"
#include "eBubbleType.h"
@@ -19,7 +19,7 @@ enum class eStateChangeType : uint32_t;
/**
* Handles the movement of controllable Entities, e.g. enemies and players
*/
class ControllablePhysicsComponent : public Component {
class ControllablePhysicsComponent : public PhysicsComponent {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::CONTROLLABLE_PHYSICS;
@@ -27,9 +27,8 @@ public:
~ControllablePhysicsComponent() override;
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void ResetFlags();
void UpdateXml(tinyxml2::XMLDocument* doc) override;
/**
@@ -37,26 +36,14 @@ public:
* If the entity is static, this is a no-op.
* @param pos The position to set
*/
void SetPosition(const NiPoint3& pos);
/**
* Returns the current position of the entity
* @return The current position of the entity
*/
const NiPoint3& GetPosition() const { return m_Position; }
void SetPosition(const NiPoint3& pos) override;
/**
* Sets the rotation of this entity, ensures this change is serialized next tick. If the entity is static, this is
* a no-op.
* @param rot the rotation to set
*/
void SetRotation(const NiQuaternion& rot);
/**
* Returns the current rotation of this entity
* @return the current rotation of this entity
*/
const NiQuaternion& GetRotation() const { return m_Rotation; }
void SetRotation(const NiQuaternion& rot) override;
/**
* Sets the current velocity of this entity, ensures that this change is serialized next tick. If the entity is
@@ -323,21 +310,6 @@ private:
*/
dpEntity* m_dpEntity;
/**
* Whether or not the position is dirty, forcing a serialization update of the position
*/
bool m_DirtyPosition;
/**
* The current position of the entity
*/
NiPoint3 m_Position;
/**
* The current rotation of the entity
*/
NiQuaternion m_Rotation;
/**
* Whether or not the velocity is dirty, forcing a serialization of the velocity
*/

View File

@@ -72,6 +72,7 @@ DestroyableComponent::DestroyableComponent(Entity* parent) : Component(parent) {
m_ImmuneToImaginationLossCount = 0;
m_ImmuneToQuickbuildInterruptCount = 0;
m_ImmuneToPullToPointCount = 0;
m_DeathBehavior = -1;
}
DestroyableComponent::~DestroyableComponent() {
@@ -119,7 +120,7 @@ void DestroyableComponent::Reinitialize(LOT templateID) {
}
}
void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) {
void DestroyableComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) {
outBitStream->Write1(); // always write these on construction
outBitStream->Write(m_ImmuneToBasicAttackCount);
@@ -363,9 +364,10 @@ void DestroyableComponent::SetIsShielded(bool value) {
void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignoreChecks) {
// Ignore factionID -1
if (factionID == -1 && !ignoreChecks) {
return;
}
if (factionID == -1 && !ignoreChecks) return;
// if we already have that faction, don't add it again
if (std::find(m_FactionIDs.begin(), m_FactionIDs.end(), factionID) != m_FactionIDs.end()) return;
m_FactionIDs.push_back(factionID);
m_DirtyHealth = true;
@@ -407,6 +409,14 @@ void DestroyableComponent::AddFaction(const int32_t factionID, const bool ignore
}
bool DestroyableComponent::IsEnemy(const Entity* other) const {
if (m_Parent->IsPlayer() && other->IsPlayer()){
auto* thisCharacterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!thisCharacterComponent) return false;
auto* otherCharacterComponent = other->GetComponent<CharacterComponent>();
if (!otherCharacterComponent) return false;
if (thisCharacterComponent->GetPvpEnabled() && otherCharacterComponent->GetPvpEnabled()) return true;
return false;
}
const auto* otherDestroyableComponent = other->GetComponent<DestroyableComponent>();
if (otherDestroyableComponent != nullptr) {
for (const auto enemyFaction : m_EnemyFactionIDs) {
@@ -485,43 +495,6 @@ Entity* DestroyableComponent::GetKiller() const {
return Game::entityManager->GetEntity(m_KillerID);
}
bool DestroyableComponent::CheckValidity(const LWOOBJID target, const bool ignoreFactions, const bool targetEnemy, const bool targetFriend) const {
auto* targetEntity = Game::entityManager->GetEntity(target);
if (targetEntity == nullptr) {
Game::logger->Log("DestroyableComponent", "Invalid entity for checking validity (%llu)!", target);
return false;
}
auto* targetDestroyable = targetEntity->GetComponent<DestroyableComponent>();
if (targetDestroyable == nullptr) {
return false;
}
auto* targetQuickbuild = targetEntity->GetComponent<RebuildComponent>();
if (targetQuickbuild != nullptr) {
const auto state = targetQuickbuild->GetState();
if (state != eRebuildState::COMPLETED) {
return false;
}
}
if (ignoreFactions) {
return true;
}
// Get if the target entity is an enemy and friend
bool isEnemy = IsEnemy(targetEntity);
bool isFriend = IsFriend(targetEntity);
// Return true if the target type matches what we are targeting
return (isEnemy && targetEnemy) || (isFriend && targetFriend);
}
void DestroyableComponent::Heal(const uint32_t health) {
auto current = static_cast<uint32_t>(GetHealth());
const auto max = static_cast<uint32_t>(GetMaxHealth());
@@ -763,18 +736,18 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
auto* member = Game::entityManager->GetEntity(specificOwner);
if (member) LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
if (member) Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
} else {
for (const auto memberId : team->members) { // Free for all
auto* member = Game::entityManager->GetEntity(memberId);
if (member == nullptr) continue;
LootGenerator::Instance().DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
Loot::DropLoot(member, m_Parent, lootMatrixId, GetMinCoins(), GetMaxCoins());
}
}
} else { // drop loot for non team user
LootGenerator::Instance().DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
Loot::DropLoot(owner, m_Parent, GetLootMatrixID(), GetMinCoins(), GetMaxCoins());
}
}
} else {
@@ -792,7 +765,7 @@ void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType
coinsTotal -= coinsToLose;
LootGenerator::Instance().DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
Loot::DropLoot(m_Parent, m_Parent, -1, coinsToLose, coinsToLose);
character->SetCoins(coinsTotal, eLootSourceType::PICKUP);
}
}

View File

@@ -24,7 +24,7 @@ public:
DestroyableComponent(Entity* parentEntity);
~DestroyableComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadFromXml(tinyxml2::XMLDocument* doc) override;
void UpdateXml(tinyxml2::XMLDocument* doc) override;
@@ -371,14 +371,6 @@ public:
*/
Entity* GetKiller() const;
/**
* Checks if the target ID is a valid enemy of this entity
* @param target the target ID to check for
* @param ignoreFactions whether or not check for the factions, e.g. just return true if the entity cannot be smashed
* @return if the target ID is a valid enemy
*/
bool CheckValidity(LWOOBJID target, bool ignoreFactions = false, bool targetEnemy = true, bool targetFriend = false) const;
/**
* Attempt to damage this entity, handles everything from health and armor to absorption, immunity and callbacks.
* @param damage the damage to attempt to apply
@@ -424,6 +416,9 @@ public:
const bool GetImmuneToQuickbuildInterrupt() {return m_ImmuneToQuickbuildInterruptCount > 0;};
const bool GetImmuneToPullToPoint() {return m_ImmuneToPullToPointCount > 0;};
int32_t GetDeathBehavior() const { return m_DeathBehavior; }
void SetDeathBehavior(int32_t value) { m_DeathBehavior = value; }
/**
* Utility to reset all stats to the default stats based on items and completed missions
*/
@@ -605,6 +600,11 @@ private:
uint32_t m_ImmuneToImaginationLossCount;
uint32_t m_ImmuneToQuickbuildInterruptCount;
uint32_t m_ImmuneToPullToPointCount;
/**
* Death behavior type. If 0, the client plays a death animation as opposed to a smash animation.
*/
int32_t m_DeathBehavior;
};
#endif // DESTROYABLECOMPONENT_H

View File

@@ -0,0 +1,50 @@
#include "DonationVendorComponent.h"
#include "Database.h"
DonationVendorComponent::DonationVendorComponent(Entity* parent) : VendorComponent(parent) {
//LoadConfigData
m_PercentComplete = 0.0;
m_TotalDonated = 0;
m_TotalRemaining = 0;
// custom attribute to calculate other values
m_Goal = m_Parent->GetVar<int32_t>(u"donationGoal");
if (m_Goal == 0) m_Goal = INT32_MAX;
// Default to the nexus tower jawbox activity and setup settings
m_ActivityId = m_Parent->GetVar<uint32_t>(u"activityID");
if ((m_ActivityId == 0) || (m_ActivityId == 117)) {
m_ActivityId = 117;
m_PercentComplete = 1.0;
m_TotalDonated = INT32_MAX;
m_TotalRemaining = 0;
m_Goal = INT32_MAX;
return;
}
std::unique_ptr<sql::PreparedStatement> query(Database::CreatePreppedStmt("SELECT SUM(primaryScore) as donation_total FROM leaderboard WHERE game_id = ?;"));
query->setInt(1, m_ActivityId);
std::unique_ptr<sql::ResultSet> donation_total(query->executeQuery());
if (donation_total->next()) m_TotalDonated = donation_total->getInt("donation_total");
m_TotalRemaining = m_Goal - m_TotalDonated;
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
}
void DonationVendorComponent::SubmitDonation(uint32_t count) {
if (count <= 0 && ((m_TotalDonated + count) > 0)) return;
m_TotalDonated += count;
m_TotalRemaining = m_Goal - m_TotalDonated;
m_PercentComplete = m_TotalDonated/static_cast<float>(m_Goal);
m_DirtyDonationVendor = true;
}
void DonationVendorComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
VendorComponent::Serialize(outBitStream, bIsInitialUpdate);
outBitStream->Write(bIsInitialUpdate || m_DirtyDonationVendor);
if (bIsInitialUpdate || m_DirtyDonationVendor) {
outBitStream->Write(m_PercentComplete);
outBitStream->Write(m_TotalDonated);
outBitStream->Write(m_TotalRemaining);
if (!bIsInitialUpdate) m_DirtyDonationVendor = false;
}
}

View File

@@ -0,0 +1,27 @@
#ifndef __DONATIONVENDORCOMPONENT__H__
#define __DONATIONVENDORCOMPONENT__H__
#include "VendorComponent.h"
#include "eReplicaComponentType.h"
class Entity;
class DonationVendorComponent final : public VendorComponent {
public:
inline static const eReplicaComponentType ComponentType = eReplicaComponentType::DONATION_VENDOR;
DonationVendorComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
uint32_t GetActivityID() {return m_ActivityId;};
void SubmitDonation(uint32_t count);
private:
bool m_DirtyDonationVendor = false;
float m_PercentComplete = 0.0;
int32_t m_TotalDonated = 0;
int32_t m_TotalRemaining = 0;
uint32_t m_ActivityId = 0;
int32_t m_Goal = 0;
};
#endif //!__DONATIONVENDORCOMPONENT__H__

View File

@@ -116,6 +116,9 @@ Inventory* InventoryComponent::GetInventory(const eInventoryType type) {
case eInventoryType::VENDOR_BUYBACK:
size = 27u;
break;
case eInventoryType::DONATION:
size = 24u;
break;
default:
break;
}
@@ -709,7 +712,7 @@ void InventoryComponent::UpdateXml(tinyxml2::XMLDocument* document) {
}
}
void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate, unsigned& flags) {
void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool bIsInitialUpdate) {
if (bIsInitialUpdate || m_Dirty) {
outBitStream->Write(true);
@@ -767,10 +770,6 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
outBitStream->Write(false);
}
void InventoryComponent::ResetFlags() {
m_Dirty = false;
}
void InventoryComponent::Update(float deltaTime) {
for (auto* set : m_Itemsets) {
set->Update(deltaTime);
@@ -933,9 +932,8 @@ void InventoryComponent::EquipScripts(Item* equippedItem) {
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (!scriptCompData) return;
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData->script_name);
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?");
}
@@ -949,9 +947,8 @@ void InventoryComponent::UnequipScripts(Item* unequippedItem) {
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), eReplicaComponentType::SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance().GetTable<CDScriptComponentTable>();
auto scriptCompData = scriptCompTable->GetByID(scriptComponentID);
if (!scriptCompData) return;
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData->script_name);
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?");
}
@@ -1161,19 +1158,7 @@ void InventoryComponent::AddItemSkills(const LOT lot) {
const auto skill = FindSkill(lot);
if (skill == 0) {
return;
}
if (index != m_Skills.end()) {
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
GameMessages::SendAddSkill(m_Parent, skill, static_cast<int>(slot));
m_Skills.insert_or_assign(slot, skill);
SetSkill(slot, skill);
}
void InventoryComponent::RemoveItemSkills(const LOT lot) {
@@ -1200,7 +1185,7 @@ void InventoryComponent::RemoveItemSkills(const LOT lot) {
if (slot == BehaviorSlot::Primary) {
m_Skills.insert_or_assign(BehaviorSlot::Primary, 1);
GameMessages::SendAddSkill(m_Parent, 1, static_cast<int>(BehaviorSlot::Primary));
GameMessages::SendAddSkill(m_Parent, 1, BehaviorSlot::Primary);
}
}
@@ -1343,12 +1328,8 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
for (const auto& result : results) {
if (result.castOnType == 1) {
const auto entry = behaviors->GetSkillByID(result.skillID);
if (!entry) {
Game::logger->Log("InventoryComponent", "Buff %i not in database!", result.skillID);
continue;
}
if (entry->skillID == 0) {
if (entry.skillID == 0) {
Game::logger->Log("InventoryComponent", "Failed to find buff behavior for skill (%i)!", result.skillID);
continue;
@@ -1359,7 +1340,7 @@ std::vector<uint32_t> InventoryComponent::FindBuffs(Item* item, bool castOnEquip
}
// If item is not a proxy, add its buff to the added buffs.
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry->behaviorID));
if (item->GetParent() == LWOOBJID_EMPTY) buffs.push_back(static_cast<uint32_t>(entry.behaviorID));
}
}
@@ -1634,3 +1615,29 @@ void InventoryComponent::UpdatePetXml(tinyxml2::XMLDocument* document) {
petInventoryElement->LinkEndChild(petElement);
}
}
bool InventoryComponent::SetSkill(int32_t slot, uint32_t skillId){
BehaviorSlot behaviorSlot = BehaviorSlot::Invalid;
if (slot == 1 ) behaviorSlot = BehaviorSlot::Primary;
else if (slot == 2 ) behaviorSlot = BehaviorSlot::Offhand;
else if (slot == 3 ) behaviorSlot = BehaviorSlot::Neck;
else if (slot == 4 ) behaviorSlot = BehaviorSlot::Head;
else if (slot == 5 ) behaviorSlot = BehaviorSlot::Consumable;
else return false;
return SetSkill(behaviorSlot, skillId);
}
bool InventoryComponent::SetSkill(BehaviorSlot slot, uint32_t skillId){
if (skillId == 0) return false;
const auto index = m_Skills.find(slot);
if (index != m_Skills.end()) {
const auto old = index->second;
GameMessages::SendRemoveSkill(m_Parent, old);
}
GameMessages::SendAddSkill(m_Parent, skillId, slot);
m_Skills.insert_or_assign(slot, skillId);
return true;
}

View File

@@ -42,10 +42,9 @@ public:
explicit InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document = nullptr);
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void LoadXml(tinyxml2::XMLDocument* document);
void UpdateXml(tinyxml2::XMLDocument* document) override;
void ResetFlags();
/**
* Returns an inventory of the specified type, if it exists
@@ -368,6 +367,11 @@ public:
*/
void UnequipScripts(Item* unequippedItem);
std::map<BehaviorSlot, uint32_t> GetSkills(){ return m_Skills; };
bool SetSkill(int slot, uint32_t skillId);
bool SetSkill(BehaviorSlot slot, uint32_t skillId);
~InventoryComponent() override;
private:

View File

@@ -38,7 +38,7 @@ void LUPExhibitComponent::NextExhibit() {
Game::entityManager->SerializeEntity(m_Parent);
}
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags) {
void LUPExhibitComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write1(); // Dirty flag?
outBitStream->Write(m_Exhibit);
}

View File

@@ -16,7 +16,7 @@ public:
LUPExhibitComponent(Entity* parent);
~LUPExhibitComponent();
void Update(float deltaTime) override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, uint32_t& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* After the timer runs out, this changes the currently exhibited LOT to the next one

View File

@@ -37,7 +37,7 @@ void LevelProgressionComponent::LoadFromXml(tinyxml2::XMLDocument* doc) {
m_CharacterVersion = static_cast<eCharacterVersion>(characterVersion);
}
void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void LevelProgressionComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write(bIsInitialUpdate || m_DirtyLevelInfo);
if (bIsInitialUpdate || m_DirtyLevelInfo) outBitStream->Write(m_Level);
m_DirtyLevelInfo = false;

View File

@@ -21,7 +21,7 @@ public:
*/
LevelProgressionComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* Save data from this componennt to character XML

View File

@@ -8,7 +8,7 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}
void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void ModelComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// ItemComponent Serialization. Pets do not get this serialization.
if (!m_Parent->HasComponent(eReplicaComponentType::PET)) {
outBitStream->Write1();

View File

@@ -17,7 +17,7 @@ public:
ModelComponent(Entity* parent);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* Returns the original position of the model

View File

@@ -46,7 +46,7 @@ const std::u16string& ModuleAssemblyComponent::GetAssemblyPartsLOTs() const {
return m_AssemblyPartsLOTs;
}
void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void ModuleAssemblyComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
if (bIsInitialUpdate) {
outBitStream->Write1();

View File

@@ -17,7 +17,7 @@ public:
ModuleAssemblyComponent(Entity* parent);
~ModuleAssemblyComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
void Update(float deltaTime) override;
/**

View File

@@ -10,85 +10,77 @@
#include "EntityManager.h"
#include "SimplePhysicsComponent.h"
#include "CDClientManager.h"
#include "Game.h"
#include "dZoneManager.h"
#include "CDComponentsRegistryTable.h"
#include "CDPhysicsComponentTable.h"
std::map<LOT, float> MovementAIComponent::m_PhysicsSpeedCache = {};
namespace {
/**
* Cache of all lots and their respective speeds
*/
std::map<LOT, float> m_PhysicsSpeedCache;
}
MovementAIComponent::MovementAIComponent(Entity* parent, MovementAIInfo info) : Component(parent) {
m_Info = std::move(info);
m_Done = true;
m_Info = info;
m_AtFinalWaypoint = true;
m_BaseCombatAI = nullptr;
m_BaseCombatAI = reinterpret_cast<BaseCombatAIComponent*>(m_Parent->GetComponent(eReplicaComponentType::BASE_COMBAT_AI));
m_BaseCombatAI = m_Parent->GetComponent<BaseCombatAIComponent>();
//Try and fix the insane values:
if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius = m_Info.wanderRadius * 0.5f;
if (m_Info.wanderRadius > 5.0f) m_Info.wanderRadius *= 0.5f;
if (m_Info.wanderRadius > 8.0f) m_Info.wanderRadius = 8.0f;
if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed = m_Info.wanderSpeed * 0.5f;
if (m_Info.wanderSpeed > 0.5f) m_Info.wanderSpeed *= 0.5f;
m_BaseSpeed = GetBaseSpeed(m_Parent->GetLOT());
m_NextWaypoint = GetCurrentPosition();
m_NextWaypoint = m_Parent->GetPosition();
m_Acceleration = 0.4f;
m_Interrupted = false;
m_PullPoint = {};
m_PullingToPoint = false;
m_PullPoint = NiPoint3::ZERO;
m_HaltDistance = 0;
m_Timer = 0;
m_TimeToTravel = 0;
m_TimeTravelled = 0;
m_CurrentSpeed = 0;
m_Speed = 0;
m_TotalTime = 0;
m_MaxSpeed = 0;
m_LockRotation = false;
}
MovementAIComponent::~MovementAIComponent() = default;
void MovementAIComponent::Update(const float deltaTime) {
if (m_Interrupted) {
if (m_PullingToPoint) {
const auto source = GetCurrentWaypoint();
const auto speed = deltaTime * 2.5f;
NiPoint3 velocity;
velocity.x = (m_PullPoint.x - source.x) * speed;
velocity.y = (m_PullPoint.y - source.y) * speed;
velocity.z = (m_PullPoint.z - source.z) * speed;
NiPoint3 velocity = (m_PullPoint - source) * speed;
SetPosition(source + velocity);
if (Vector3::DistanceSquared(GetCurrentPosition(), m_PullPoint) < 2 * 2) {
m_Interrupted = false;
if (Vector3::DistanceSquared(m_Parent->GetPosition(), m_PullPoint) < std::pow(2, 2)) {
m_PullingToPoint = false;
}
return;
}
if (AtFinalWaypoint()) // Are we done?
{
return;
}
// Are we done?
if (AtFinalWaypoint()) return;
if (m_HaltDistance > 0) {
if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < m_HaltDistance * m_HaltDistance) // Prevent us from hugging the target
{
// Prevent us from hugging the target
if (Vector3::DistanceSquared(ApproximateLocation(), GetDestination()) < std::pow(m_HaltDistance, 2)) {
Stop();
return;
}
}
if (m_Timer > 0) {
m_Timer -= deltaTime;
if (m_Timer > 0) {
return;
}
m_Timer = 0;
}
m_TimeTravelled += deltaTime;
if (m_TimeTravelled < m_TimeToTravel) return;
m_TimeTravelled = 0.0f;
const auto source = GetCurrentWaypoint();
@@ -101,48 +93,44 @@ void MovementAIComponent::Update(const float deltaTime) {
m_NextWaypoint = GetCurrentWaypoint();
if (m_NextWaypoint == source) {
m_Timer = 0;
m_TimeToTravel = 0.0f;
goto nextAction;
}
if (m_CurrentSpeed < m_Speed) {
if (m_CurrentSpeed < m_MaxSpeed) {
m_CurrentSpeed += m_Acceleration;
}
if (m_CurrentSpeed > m_Speed) {
m_CurrentSpeed = m_Speed;
if (m_CurrentSpeed > m_MaxSpeed) {
m_CurrentSpeed = m_MaxSpeed;
}
const auto speed = m_CurrentSpeed * m_BaseSpeed;
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 = sqrtf(delta.x * delta.x + delta.y * delta.y + delta.z * delta.z);
const auto length = delta.Length();
if (length > 0) {
velocity.x = (delta.x / length) * speed;
velocity.y = (delta.y / length) * speed;
velocity.z = (delta.z / length) * speed;
velocity = (delta / length) * speed;
}
// Calclute the time it will take to reach the next waypoint with the current speed
m_TotalTime = m_Timer = length / speed;
m_TimeTravelled = 0.0f;
m_TimeToTravel = length / speed;
SetRotation(NiQuaternion::LookAt(source, m_NextWaypoint));
} else {
// Check if there are more waypoints in the queue, if so set our next destination to the next waypoint
if (!m_Queue.empty()) {
SetDestination(m_Queue.top());
m_Queue.pop();
} else {
// We have reached our final waypoint
if (m_CurrentPath.empty()) {
Stop();
return;
}
SetDestination(m_CurrentPath.top());
m_CurrentPath.pop();
}
nextAction:
@@ -157,7 +145,7 @@ const MovementAIInfo& MovementAIComponent::GetInfo() const {
}
bool MovementAIComponent::AdvanceWaypointIndex() {
if (m_PathIndex >= m_CurrentPath.size()) {
if (m_PathIndex >= m_InterpolatedWaypoints.size()) {
return false;
}
@@ -167,37 +155,19 @@ bool MovementAIComponent::AdvanceWaypointIndex() {
}
NiPoint3 MovementAIComponent::GetCurrentWaypoint() const {
if (m_PathIndex >= m_CurrentPath.size()) {
return GetCurrentPosition();
}
return m_CurrentPath[m_PathIndex];
}
NiPoint3 MovementAIComponent::GetNextWaypoint() const {
return m_NextWaypoint;
}
NiPoint3 MovementAIComponent::GetCurrentPosition() const {
return m_Parent->GetPosition();
return m_PathIndex >= m_InterpolatedWaypoints.size() ? m_Parent->GetPosition() : m_InterpolatedWaypoints[m_PathIndex];
}
NiPoint3 MovementAIComponent::ApproximateLocation() const {
auto source = GetCurrentPosition();
auto source = m_Parent->GetPosition();
if (m_Done) {
return source;
}
if (AtFinalWaypoint()) return source;
auto destination = m_NextWaypoint;
auto factor = m_TotalTime > 0 ? (m_TotalTime - m_Timer) / m_TotalTime : 0;
auto percentageToWaypoint = m_TimeToTravel > 0 ? m_TimeTravelled / m_TimeToTravel : 0;
auto x = source.x + factor * (destination.x - source.x);
auto y = source.y + factor * (destination.y - source.y);
auto z = source.z + factor * (destination.z - source.z);
NiPoint3 approximation = NiPoint3(x, y, z);
auto approximation = source + ((destination - source) * percentageToWaypoint);
if (dpWorld::Instance().IsLoaded()) {
approximation.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(approximation);
@@ -226,28 +196,20 @@ bool MovementAIComponent::Warp(const NiPoint3& point) {
return true;
}
float MovementAIComponent::GetTimer() const {
return m_Timer;
}
bool MovementAIComponent::AtFinalWaypoint() const {
return m_Done;
}
void MovementAIComponent::Stop() {
if (m_Done) {
return;
}
if (AtFinalWaypoint()) return;
SetPosition(ApproximateLocation());
SetVelocity(NiPoint3::ZERO);
m_TotalTime = m_Timer = 0;
m_TimeToTravel = 0;
m_TimeTravelled = 0;
m_Done = true;
m_AtFinalWaypoint = true;
m_CurrentPath = {};
m_InterpolatedWaypoints.clear();
while (!m_CurrentPath.empty()) m_CurrentPath.pop();
m_PathIndex = 0;
@@ -259,20 +221,17 @@ void MovementAIComponent::Stop() {
void MovementAIComponent::PullToPoint(const NiPoint3& point) {
Stop();
m_Interrupted = true;
m_PullingToPoint = true;
m_PullPoint = point;
}
void MovementAIComponent::SetPath(std::vector<NiPoint3> path) {
std::reverse(path.begin(), path.end());
if (path.empty()) return;
std::for_each(path.rbegin(), path.rend() - 1, [this](const NiPoint3& point) {
this->m_CurrentPath.push(point);
});
for (const auto& point : path) {
m_Queue.push(point);
}
SetDestination(m_Queue.top());
m_Queue.pop();
SetDestination(path.front());
}
float MovementAIComponent::GetBaseSpeed(LOT lot) {
@@ -291,26 +250,14 @@ float MovementAIComponent::GetBaseSpeed(LOT lot) {
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::CONTROLLABLE_PHYSICS, -1);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
if (componentID == -1) {
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
}
componentID = componentRegistryTable->GetByIDAndType(lot, eReplicaComponentType::SIMPLE_PHYSICS, -1);
if (componentID != -1) {
physicsComponent = physicsComponentTable->GetByID(componentID);
goto foundComponent;
}
foundComponent:
physicsComponent = physicsComponentTable->GetByID(componentID);
// Client defaults speed to 10 and if the speed is also null in the table, it defaults to 10.
float speed = 10.0f;
if (physicsComponent) speed = physicsComponent->speed;
float speed = physicsComponent != nullptr ? physicsComponent->speed : 10.0f;
float delta = fabs(speed) - 1.0f;
@@ -322,39 +269,11 @@ foundComponent:
}
void MovementAIComponent::SetPosition(const NiPoint3& value) {
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetPosition(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetPosition(value);
}
m_Parent->SetPosition(value);
}
void MovementAIComponent::SetRotation(const NiQuaternion& value) {
if (m_LockRotation) {
return;
}
auto* controllablePhysicsComponent = m_Parent->GetComponent<ControllablePhysicsComponent>();
if (controllablePhysicsComponent != nullptr) {
controllablePhysicsComponent->SetRotation(value);
return;
}
auto* simplePhysicsComponent = m_Parent->GetComponent<SimplePhysicsComponent>();
if (simplePhysicsComponent != nullptr) {
simplePhysicsComponent->SetRotation(value);
}
if (!m_LockRotation) m_Parent->SetRotation(value);
}
void MovementAIComponent::SetVelocity(const NiPoint3& value) {
@@ -373,15 +292,8 @@ void MovementAIComponent::SetVelocity(const NiPoint3& value) {
}
}
void MovementAIComponent::SetDestination(const NiPoint3& value) {
if (m_Interrupted) {
return;
}
/*if (Vector3::DistanceSquared(value, GetDestination()) < 2 * 2)
{
return;
}*/
void MovementAIComponent::SetDestination(const NiPoint3& destination) {
if (m_PullingToPoint) return;
const auto location = ApproximateLocation();
@@ -390,97 +302,53 @@ void MovementAIComponent::SetDestination(const NiPoint3& value) {
}
std::vector<NiPoint3> computedPath;
if (dpWorld::Instance().IsLoaded()) {
computedPath = dpWorld::Instance().GetNavMesh()->GetPath(GetCurrentPosition(), value, m_Info.wanderSpeed);
} else {
computedPath = dpWorld::Instance().GetNavMesh()->GetPath(m_Parent->GetPosition(), destination, m_Info.wanderSpeed);
}
// Somehow failed
if (computedPath.empty()) {
// Than take 10 points between the current position and the destination and make that the path
auto point = location;
auto start = location;
auto delta = value - point;
auto delta = destination - start;
auto step = delta / 10;
auto step = delta / 10.0f;
for (int i = 0; i < 10; i++) {
point = point + step;
// TODO: Replace this with += when the NiPoint3::operator+= is fixed
start = start + step;
computedPath.push_back(point);
computedPath.push_back(start);
}
}
if (computedPath.empty()) // Somehow failed
{
return;
}
m_CurrentPath.clear();
m_CurrentPath.push_back(location);
m_InterpolatedWaypoints.clear();
// Simply path
for (auto point : computedPath) {
for (auto& point : computedPath) {
if (dpWorld::Instance().IsLoaded()) {
point.y = dpWorld::Instance().GetNavMesh()->GetHeightAtPoint(point);
}
m_CurrentPath.push_back(point);
m_InterpolatedWaypoints.push_back(point);
}
m_CurrentPath.push_back(computedPath[computedPath.size() - 1]);
m_PathIndex = 0;
m_TotalTime = m_Timer = 0;
m_TimeTravelled = 0;
m_TimeToTravel = 0;
m_Done = false;
m_AtFinalWaypoint = false;
}
NiPoint3 MovementAIComponent::GetDestination() const {
if (m_CurrentPath.empty()) {
return GetCurrentPosition();
}
return m_CurrentPath[m_CurrentPath.size() - 1];
return m_InterpolatedWaypoints.empty() ? m_Parent->GetPosition() : m_InterpolatedWaypoints.back();
}
void MovementAIComponent::SetSpeed(const float value) {
m_Speed = value;
void MovementAIComponent::SetMaxSpeed(const float value) {
if (value == m_MaxSpeed) return;
m_MaxSpeed = value;
m_Acceleration = value / 5;
}
float MovementAIComponent::GetSpeed() const {
return m_Speed;
}
void MovementAIComponent::SetAcceleration(const float value) {
m_Acceleration = value;
}
float MovementAIComponent::GetAcceleration() const {
return m_Acceleration;
}
void MovementAIComponent::SetHaltDistance(const float value) {
m_HaltDistance = value;
}
float MovementAIComponent::GetHaltDistance() const {
return m_HaltDistance;
}
void MovementAIComponent::SetCurrentSpeed(float value) {
m_CurrentSpeed = value;
}
float MovementAIComponent::GetCurrentSpeed() const {
return m_CurrentSpeed;
}
void MovementAIComponent::SetLockRotation(bool value) {
m_LockRotation = value;
}
bool MovementAIComponent::GetLockRotation() const {
return m_LockRotation;
}

View File

@@ -1,6 +1,6 @@
/*
* Darkflame Universe
* Copyright 2018
* Copyright 2023
*/
#ifndef MOVEMENTAICOMPONENT_H
@@ -60,7 +60,6 @@ public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::MOVEMENT_AI;
MovementAIComponent(Entity* parentEntity, MovementAIInfo info);
~MovementAIComponent() override;
void Update(float deltaTime) override;
@@ -86,61 +85,55 @@ public:
* Sets the max speed at which this entity may run
* @param value the speed value to set
*/
void SetSpeed(float value);
/**
* Returns the max speed at which this entity may run
* @return the max speed at which this entity may run
*/
float GetSpeed() const;
void SetMaxSpeed(float value);
/**
* Sets how fast the entity will accelerate when not running at full speed
* @param value the acceleration to set
*/
void SetAcceleration(float value);
void SetAcceleration(float value) { m_Acceleration = value; };
/**
* Returns the current speed at which this entity accelerates when not running at full speed
* @return the current speed at which this entity accelerates when not running at full speed
*/
float GetAcceleration() const;
float GetAcceleration() const { return m_Acceleration; };
/**
* Sets the halting distance (the distance at which we consider the target to be reached)
* @param value the halting distance to set
*/
void SetHaltDistance(float value);
void SetHaltDistance(float value) { m_HaltDistance = value; }
/**
* Returns the current halting distance (the distance at which we consider the target to be reached)
* @return the current halting distance
*/
float GetHaltDistance() const;
float GetHaltDistance() const { return m_HaltDistance; }
/**
* Sets the speed the entity is currently running at
* @param value the speed value to set
*/
void SetCurrentSpeed(float value);
void SetCurrentSpeed(float value) { m_CurrentSpeed = value; }
/**
* Returns the speed the entity is currently running at
* @return the speed the entity is currently running at
*/
float GetCurrentSpeed() const;
float GetCurrentSpeed() const { return m_CurrentSpeed; }
/**
* Locks the rotation of this entity in place, depending on the argument
* @param value if true, the entity will be rotationally locked
*/
void SetLockRotation(bool value);
void SetLockRotation(bool value) { m_LockRotation = value; }
/**
* Returns whether this entity is currently rotationally locked
* @return true if the entity is rotationally locked, false otherwise
*/
bool GetLockRotation() const;
bool GetLockRotation() const { return m_LockRotation; };
/**
* Attempts to update the waypoint index, making the entity move to the next waypoint
@@ -158,13 +151,7 @@ public:
* Returns the waypoint this entity is supposed to move towards next
* @return the waypoint this entity is supposed to move towards next
*/
NiPoint3 GetNextWaypoint() const;
/**
* Returns the current position of this entity
* @return the current position of this entity
*/
NiPoint3 GetCurrentPosition() const;
NiPoint3 GetNextWaypoint() const { return m_NextWaypoint; }
/**
* Returns the approximate current location of the entity, including y coordinates
@@ -180,17 +167,11 @@ public:
*/
bool Warp(const NiPoint3& point);
/**
* Returns the time it will take to reach the final waypoint according to the current speed
* @return the time it will take to reach the final waypoint according to the current speed
*/
float GetTimer() const;
/**
* Returns if the entity is at its final waypoint
* @return if the entity is at its final waypoint
*/
bool AtFinalWaypoint() const;
bool AtFinalWaypoint() const { return m_AtFinalWaypoint; }
/**
* Renders the entity stationary
@@ -250,17 +231,12 @@ private:
/**
* The max speed this entity may move at
*/
float m_Speed;
float m_MaxSpeed;
/**
* The time it will take to reach the next waypoint using the current speed
*/
float m_Timer;
/**
* The total time it will take to reach the waypoint form its starting point
*/
float m_TotalTime;
float m_TimeTravelled;
/**
* The path this entity is currently traversing
@@ -270,7 +246,7 @@ private:
/**
* If the entity has reached it last waypoint
*/
bool m_Done;
bool m_AtFinalWaypoint;
/**
* The speed the entity is currently moving at
@@ -287,6 +263,11 @@ private:
*/
float m_HaltDistance;
/**
* The total time it will take to reach the waypoint form its starting point
*/
float m_TimeToTravel;
/**
* The base speed this entity has
*/
@@ -295,7 +276,7 @@ private:
/**
* If the AI is currently turned of (e.g. when teleporting to some location)
*/
bool m_Interrupted;
bool m_PullingToPoint;
/**
* A position that the entity is currently moving towards while being interrupted
@@ -315,17 +296,12 @@ private:
/**
* The path the entity is currently following
*/
std::vector<NiPoint3> m_CurrentPath;
std::vector<NiPoint3> m_InterpolatedWaypoints;
/**
* Queue of positions to traverse
* The path from the current position to the destination.
*/
std::stack<NiPoint3> m_Queue;
/**
* Cache of all lots and their respective speeds
*/
static std::map<LOT, float> m_PhysicsSpeedCache;
std::stack<NiPoint3> m_CurrentPath;
};
#endif // MOVEMENTAICOMPONENT_H

View File

@@ -32,7 +32,7 @@ MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) {
MoverSubComponent::~MoverSubComponent() = default;
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const {
void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
outBitStream->Write<bool>(true);
outBitStream->Write<uint32_t>(static_cast<uint32_t>(mState));
@@ -71,7 +71,7 @@ MovingPlatformComponent::~MovingPlatformComponent() {
delete static_cast<MoverSubComponent*>(m_MoverSubComponent);
}
void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) {
void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) {
// Here we don't serialize the moving platform to let the client simulate the movement
if (!m_Serialize) {
@@ -112,7 +112,7 @@ void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bI
if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) {
// TODO
} else {
mover->Serialize(outBitStream, bIsInitialUpdate, flags);
mover->Serialize(outBitStream, bIsInitialUpdate);
}
}
}

View File

@@ -38,7 +38,7 @@ public:
MoverSubComponent(const NiPoint3& startPos);
~MoverSubComponent();
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate);
/**
* The state the platform is currently in
@@ -111,7 +111,7 @@ public:
MovingPlatformComponent(Entity* parent, const std::string& pathName);
~MovingPlatformComponent() override;
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags);
void Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) override;
/**
* Stops all pathing, called when an entity starts a quick build associated with this platform

View File

@@ -1,9 +1,9 @@
#include "RocketLaunchLupComponent.h"
#include "MultiZoneEntranceComponent.h"
#include "RocketLaunchpadControlComponent.h"
#include "InventoryComponent.h"
#include "CharacterComponent.h"
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) {
MultiZoneEntranceComponent::MultiZoneEntranceComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
std::string zoneString = GeneralUtils::UTF16ToWTF8(m_Parent->GetVar<std::u16string>(u"MultiZoneIDs"));
std::stringstream ss(zoneString);
@@ -14,17 +14,17 @@ RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(p
}
}
RocketLaunchLupComponent::~RocketLaunchLupComponent() {}
MultiZoneEntranceComponent::~MultiZoneEntranceComponent() {}
void RocketLaunchLupComponent::OnUse(Entity* originator) {
void MultiZoneEntranceComponent::OnUse(Entity* originator) {
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) return;
// the LUP world menu is just the property menu, the client knows how to handle it
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress());
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), originator->GetSystemAddress());
}
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) {
void MultiZoneEntranceComponent::OnSelectWorld(Entity* originator, uint32_t index) {
auto* rocketLaunchpadControlComponent = m_Parent->GetComponent<RocketLaunchpadControlComponent>();
if (!rocketLaunchpadControlComponent) return;

View File

@@ -1,7 +1,6 @@
#pragma once
#include "Entity.h"
#include "GameMessages.h"
#include "Component.h"
#include "eReplicaComponentType.h"
@@ -9,16 +8,16 @@
* Component that handles the LUP/WBL rocket launchpad that can be interacted with to travel to WBL worlds.
*
*/
class RocketLaunchLupComponent : public Component {
class MultiZoneEntranceComponent : public Component {
public:
static const eReplicaComponentType ComponentType = eReplicaComponentType::ROCKET_LAUNCH_LUP;
static const eReplicaComponentType ComponentType = eReplicaComponentType::MULTI_ZONE_ENTRANCE;
/**
* Constructor for this component, builds the m_LUPWorlds vector
* @param parent parent that contains this component
*/
RocketLaunchLupComponent(Entity* parent);
~RocketLaunchLupComponent() override;
MultiZoneEntranceComponent(Entity* parent);
~MultiZoneEntranceComponent() override;
/**
* Handles an OnUse event from some entity, preparing it for launch to some other world

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