This resolves the race condition described as follows:
Session 0 logins in, enters world.
Session 0 closes client with X.
Person reconnects asap.
Session 1 pulls a new, full character list from the DB.
Session 0, Character does final logoff (Y logoff animation completes) and does final save
Character in database is updated
Session 1 Character record is now not up to date with what exists in the DB.
This PR re-uses Character references when they exist, and thus, solving the race condition, because the character that gets updated during the Y logoff animation is the same reference that the relogged client is also holding onto (at the character selection screen).
The Character cannot be relogged (already existing) until the prev instance is fully out of world, thus, there is no worry of concurrency with two sessions holding ref to the same character object.
* Step 1. Add new adapter classes and models
All classes and objects added are in new namespaces.
This add has 0 interraction on existing ACE code.
This sets the groundwork for Step 2. Moving World DB caching to the new models
* first part of the world entity usage
* almost there
* final part
* update WeenieExtensions
* fix
* merge fixes and improvements
* ACE.Database new code
* it compiles
* InitializePropertyDictionaries
* WOrldDatabaseWithEntityCache cleanup
* ShardDatabaseWithCaching
* ShardDatabase Cleanup
* todo
* cache counts reported in serverstatus
* House InitializePropertyDictionaries
* improve ServerStatus
* BiotaModel change HousePermissions to Dictionary<uint, bool>
* PropertiesAllegiance to dictionary
* BiotaUpdater improvements
* Shard Database Cache Reporting
* More code ported from Step 3
* Remove stub CharacterCache
* Update WorldDatabaseWithEntityCache.cs
* build fixes
* code audit
* Remove non-callback functions from SerializedShardDatabase
* BiotaUpdater progress.. only emote left
* Emotes done... now need to test
* fix
* Burden/Value fix for containers
* fat fingered EncumbranceVal
* add some safety
* Don't cache player biotas for initial PlayerManager load
* fix null spellbook
* Add another null check
This is redundant as the check is also upstream.
This is to prevent an exception in the future if this function is called from another source.
* Exclude summoned pets from saving to Shard DB
* Remove extra StartCooldown and set proper weenieType for faked summons
* Update CombatPet with some defaults that apply to the class
* More updates
* Do not consume PetDevice on last use
* Update changelog.md
* Update CombatPet to be destroyed on Player logout
* new ObjectGuid(0) -> ObjectGuid.Invalid
* Fix Projectiles to not Destroy() themselves multiple times
* Add BiotaOriginatedFromOrHasBeenSavedToDatabase() function
* Add recycling to GuidManager
* Recycle Guids on Destroy
* CLeanup pending additions/removals at the end of landblock Unload
This just helps release references
* Don't raise NotifyOfEvent when WO is destroyed from Landblock.Unload()
* Remove pending actions on landblock unload
* Couple more cases where we shouldn't continue to enqueue work for detached objects
* Debug message added for objects that still try to queue work detached from a landblock
* This should fix the landblock cache issues
* This fixes the landblock object cache, but may also result in higher load
In current master, not all objects were being ticked due to a very odd sort condition.
* Update changelog.md
* Decouple IsDynamic from ShouldPersistToShard
* We must save items when we move from player container to landblock container
* Fix CreateMoveToChain target for HandleActionPutItemInContainer
* Landblock EnqueueBroadcast cleanup
Avoid double cast by using OfType
Change excludeList from IEnumerable to ICollection because it was being enumerated more than once.
* SerializedShard handle OperationCancledException
* SerializedShard break on the exceptions
* Landblock save progress
* Landblock merge fix
* Decayable -> Dynamic
* Improved ShutdownServer to include landblock unload and db queue wait
* Sleep during shutdown instead of using up all the CPU
* Shard GetDynamic was comparing against the wrong id... woops
* Fix shard biota caching
* Improve shutdown logging and actions
* cosmetic code order
* Fix crash when trying to add a bad wo to a landblock
* This fixes reloading dynamic objects
* Don't save item on pickup
* Don't save item on pickup from chest
* Don't save items on drop
* Set Home Position only for creatures (Used for navigation)
* Improve the landblock SaveDB code
* Don't save player corpse on death. Landblock will do that for us in bulk.
* Add the housing objects to statics that persist to the shard
* WorldObjectFactory dont overwrite location if object came from database
* Improved IsStaticThatShouldPersistToShard
* This should fix player dropped missiles
* Move PositionType into ACE.Entity.Enum.Properties
* Ephemeral support for Positions
* Decay code/saving works much better
* Filter out contained and wielded items from shard GetDynamicsByLandblock
* TimeToRot provision for -1 (No rot)
* Corpse decay is now controlled by landblock and uses the same decay code
* ShardDatabase: Fix add/remove biota bug
* WorldObject.Destroy now destroys contained and equipped items as well
* TryRemoveFromInventoryWithNetworking no longer removes the object from the db
* Player corpse items now remain when the corpse decays
* Cleaned up SetPropertiesForWorld
* typo fix
* WorldObject Position/Cache cleanup
The positionCache (formerly named Positions) should only be referenced by the Get/Set/Remove functions.
Furthermore, on save, we make sure that the positions in the biota have the latest up to date values from the cache.
We must do this because we don't wrap Positions like we do attributes/skills/vitals.
* ACE.DatLoader BinaryReaderExtensiosn cosmetic
* WorldObject Obsolete Model Properties removed
* Remove Player.SetCharacterPosition()
* Important summary regarding setting positions
/// !!! VERY IMPORTANT NOTE REGARDING SetPosition !!!<para />
/// Position objects are reference types. Lets say you want to create a new object and give it the location of a player,
/// If you do LandscapeItem.SetPosition(PositionType.Location, Player.Location), you've now set the Location position
/// for both the player and the LandscapeItem to the same exact object. Modifying one will affect the other.<para />
/// The proper way to would be: LandscapeItem.SetPosition(PositionType.Location, (Position)Player.Location.Clone())<para />
/// Any time you want to set a position of a different PositionType, or, positions between WorldObjects, you should use the above Clone method.
* Removed unecessary virtual on some properties
* LandblockManager now controls access to activeLandblocks
* LandblockActionQueue Removed. Landblocks now Tick()
* Landblock adjacencies cleanup
* Landblocks are no longer IActor. WordObjects now Tick.
* WorldObjects now Tick properly
* More threading notes
* Migrate Session Player ticking to Player_Tick
This also fixes the Player age calculations.
The previous method could drift over time. The new method is far more accurate.
* cleanup SavePlayer
* Minor change
* Landblock cleanup
* Forgot to propegate Tick to contained and equipped items
* HeartbeatTimestamp should be Ephemeral
This allows items to tick without every tick setting the ChangesDetected flag to true.
Without this, every world object will end up having ChangesDetected set to true, and thus requiring a periodic save, even if no other values where changed.
* MonsterTick moved off a queued ActionChain to the new Tick mechanism
* HandleObjDesc for item i do not know about changed to Warn
* serverstatus command improved text for active landblocks
* Landblock intervals changed from Stopwatch floats to DateTime
DateTime should be used for intervals this large, not stopwatch.
DateTime should perform better for this case. and should be more accurate.
* Corpse, Rotting, Destroyed Items
Corpses now only finishes rotting once
WorldObject.Destroy now removes the object from the database (if it exists). Previously, this was only done by corpses.
* Collection Modified exception fix
GetPhysicsWorldObjects was returning a linq query. Think of this as a living entity.
As the results were being iterated over, and processed, the processing of a specific result would end up adding a new entity to worldObjects, thus, modifying the backing of the linq query.
By doing a .ToList() on the query, we force the entire query to execute and return all results.
* don't queue up unnecessary database work for Destroy()
* Revert requested object description message back to debug
* gmriggs HandleActionForceObjDescSend patch
* Destroy shouldn't use an action chain if no delay is needed.
* First batch of Character refactor
Characters are now cached with their dbContext, exactly like we do for Biotas. This greatly simplifies the way characters are pulled and saved, and is much more efficient.
Deleting and restoring characters have been migrated to the new SaveCharacter function. There is no need for separate functions to handle those specific tasks.
IsPlussed column added to Character. This removes the need to query the biota to see if we should display the + in front of the name at the character screen.
Character.LastLoginTimestamp, TotalLogins, CharacterOptions1, and CharacterOptions2 are now used. These property types were removed from the ACE.Entity.enum. (the 9000+ types)
Fixed a few places where ChangesDetected was being set insetad of CharacterChangesDetected. There is still more work to do to improve this pattern.
* Player_Client renamed to Player_Character
Plus some cleanup on the way CharacterOptions were get
* Remove duplicate appearance DID properties from WorldObject_Properties
* Removed the duplicate HairTexture DID properties
* Migrate HairTexture from biota property to Character property
* Forgot a CharacterChangesDetected
* Migrate Character specific functions to Player_Character
* Some more Biota wrappers added to BiotaExtensions
* CharacterPropertiesShortcutBar wrapped
* CharacterPropertiesSpellBar migrated to CharacterExtensions
* CharacterPropertiesTitleBook migrated to CharacterExtensions
* CharacterPropertiesFriendList migrated to CharacterExtensions
* drop AccountId from CharacterPropertiesFriendList
* EnchantmentRegistry improvements
Thread safety added to EnchantmentManager
Thread safety added to EnchantmentRegistry
The locks are required only when we iterate/insert/remove from the collections. EF accesses these collections on a separate thread and they cannot be modified while being iterated over.
Player save removed from EnchantmentManager. Saving a player should be a function of the player, not the enchantment manager. If we want to save a player on death, we can do that from the player.
Some minor cosmetic code improvements here and there.
* Moved the remaining thread locks to BiotaExtensions
* White spaces removed
* SaveBiota major changes
SaveBiota now only updates values in the database that have changed instead of updating every single value.
In addition. SaveBiota detects added and removed linked entities.
* Replace AddBiota(s) with SaveBiota(s)
* This improves the way a few places referenced the biota
* SaveBiota switched to a diff method. Still some work to do
* More notes + fixes
* SaveBiota move GetBiota to before EnterWriteLock
* Added a note about the lock performance
* More summary improvements for BiotaDatabaseLock
* Thread safety added to the SaveBiotas and AddBiotas functions
SavePlayer also switched to use the SaveBiotas bulk action instead of saving each biota individually.
* Biota.Clone() added
* BiotaExtensions cosmetic only
* DbUpdateException fixed
If there were multiple new records in a collection, the first one was attached but with the last new records values.
This would end up resulting in property records with duplicate types id's.
* ReoveBiota check if biota actually exists in db before removal.
* /databasequeueinfo added
* New From-Weenie Containers should not be searching the shard for inventory
* DeveloperDatabaseCommands removed RequiresWorld flag
* Improved DatabasePerfTest results.
Records (properties) are removed from the db as they're removed from a biota. This is because the context can't track removed entries after the context has been disposed.
Adding new records (properties) does not automatically save to the db. Instead, periodic saves, or saves at critical points, will cause the entire biota to be saved to the db which will update any existing records (properties) as well as add any new ones.