PlayerManager saves offline players in bulk, once very hour. This is done in bulk to help avoid desync between offline player updates.
Player caching is typically set to 30 min.
What can happen now is that PlayerManager may save a bunch of offline players, that will then be loaded into the player biota cache (for 30 min)... then after 30 min, they all get unloaded because nothign touches those biotas, then 30 min later again the PlayerManager performs its hourly maint of bulk save, thus, re-caching all those biotas.
Caching is not necessary for these biotas.
Caching is designed for biotas that exist in an online state.
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.
Limit number of threads used by database parallel work. This helps avoid "too many connections" errors on larger threaded machines. It also helps reduce memory consumption.
Use parallel for PlayerManager.Initialize()
ForceGC at the end of Startup, before the world is open. ForceGC is a pretty controversial thing. Typically if you're forcing gc, you're doing something wrong. In this case, we're forcing gc before the world starts up so that the work is done in an off-line state. This work will be done eventually, natrually by the gc, so better to get it done before the world is active. We force gc 10 times to make sure we get all objects. On a large server with many players and world precaching, this can free up 4 GB - 6 GB.
This makes the legacy code for bypassing EF tracking for non player biotas back into master, and optional.
This will make it easier for ACE devs to update this legacy code for those servers that still require it, reducing the amount of work they'll have to do to merge to latest when certain schema changes happen.
* WIP: Multi-thread landblock ticking
This is the start of "landblock group" ticking.
The idea behind the landblock groups are that each group may contain multiple landblocks that must be ticked on the same thread, but, each group itself can be ticked on independant threads.
The current groups are as follows:
Every outdoor landblock is in a group
Every dungeon landblock is in it's own group (one per dungeon)
This is not ready for public servers yet.
* More changes
* First pass at actual groups
* ObjMaint.KnownPlayers needs to be concurrent
* Removing original PoC code from LandblockManager
* Landblock Tick Cleanup
* VisibleObjects also needs to be concurrent
* DestructionQueue also needs to be concurrent
* KnownObjects needs to be concurrent
* Use a bool to toggle multi-threading
* Add thread safety to SequenceManager
* Move landblock phsyics ticking to LandblockManager
Also
* Cleanup log.Info level messages
Log.info is the default console output and is intended for:
server startup
connection/disconnections
admin initiated command output
server shutdown
Log.Debug is the appropriate level for debug-type messages that are to be logged and audited at a later date, but not output to the console.
* Missed a couple
* Move HandleSalvaging from Warn to Debug
* Add thread safety to Landblock
* More landblock group calc stuff
* couple comments
* Add some thread capping
* Limit database from taking all the threads
This adds limits to the database thread consumption.
It also should allow the world to now consume threads easier.
The way it is done is as follows.
- We determine the number of available threads using Environment.ProcessCount
- We allocate (int)Math.Max(Environment.ProcessorCount * .34, 1) to the World, and the remainder tothe database.
It breaks down as follows
1 vCPU = 1 thread world, 1 thread database
2 vCPU = 1 thread world, 1 thread database
3 vCPU = 1 thread world, 2 thread database
4 vCPU = 1 thread world, 3 thread database
5 vCPU = 1 thread world, 4 thread database
6 vCPU = 2 thread world, 4 thread database
7 vCPU = 2 thread world, 5 thread database
8 vCPU = 2 thread world, 6 thread database
9 vCPU = 3 thread world, 6 thread database
10 vCPU = 3 thread world, 7 thread database
I'd like to get some feedback from this PR on various sized servers.
What you may notice is that loading a player may take slightly longer (very slightly).
What you will probably notice is no discenerable difference in-game.
What I want to make sure happens is that the world doesn't end up feeling more choppy due to the parallel processing of outbound network traffic. Hopefully the more fair thread distriubiton will help prevent thread starvation.
* quit if not enough vCPU
* separate landblock group recal between add/remove
* revert landblockMutex in Landblock.cs
* World Manager AboveNormal thread priority.
* Add some tags
* Couple more
* Create LandBlockGroup entity
* remove space between tags
* Update the log4net examples
* Efficiency improvements
* fix message
* more WIP
* Improve log4net
Add color to console output
Make the default logger use Log4Net.Async
* WIP
* More WIP
* more WIP
* More WIP
* more WIP
* More WIP
* more WIP
* wip
* more WIP
* remove old processor count check
* Create ServerObjectManager
This removes the ServerObjects collection out of ObjectMaint into it's own class.
This paves the way for thread safety that will need to be added to ObjectMaint for the multi-threaded landblock groups.
* Only split if multi-threading is enabled
* remove comment
* ObjMaint refactor
* all obj maint collections are now private
* few more optimizations
* alternate objectmaint thread safety model
* fix
* Remove a couple of comments
* improved ObjectMaint locking
* lock (ThreadConfiguration.WorldLockObject) OnDeath
* progress
* set MultiThreadedLandblockGroupPhysicsTicking to false
* Move a bool
* add lock
* physics ticking thread safety improvement
* use Config.js for thread configuration
* Add config comments
* measure physics ticking performance
* /serverstatus info added
* comments
* Limit database from taking all the threads
This adds limits to the database thread consumption.
It also should allow the world to now consume threads easier.
The way it is done is as follows.
- We determine the number of available threads using Environment.ProcessCount
- We allocate (int)Math.Max(Environment.ProcessorCount * .34, 1) to the World, and the remainder tothe database.
It breaks down as follows
1 vCPU = 1 thread world, 1 thread database
2 vCPU = 1 thread world, 1 thread database
3 vCPU = 1 thread world, 2 thread database
4 vCPU = 1 thread world, 3 thread database
5 vCPU = 1 thread world, 4 thread database
6 vCPU = 2 thread world, 4 thread database
7 vCPU = 2 thread world, 5 thread database
8 vCPU = 2 thread world, 6 thread database
9 vCPU = 3 thread world, 6 thread database
10 vCPU = 3 thread world, 7 thread database
I'd like to get some feedback from this PR on various sized servers.
What you may notice is that loading a player may take slightly longer (very slightly).
What you will probably notice is no discenerable difference in-game.
What I want to make sure happens is that the world doesn't end up feeling more choppy due to the parallel processing of outbound network traffic. Hopefully the more fair thread distriubiton will help prevent thread starvation.
* quit if not enough vCPU
* Give World Manager thread AboveNormal priority.
* support 1 CPU
* prototype: adding account-wide house recall
* updating restriction db / has permissions
* additional ObjMaint refactoring
* re-adding CheckPlayers()
* the call to GetVisibleObjects() in Monster.FindNextTarget() is no longer needed in new design
* removing more unused code
* very preliminary testing
* prevent 1 character from purchasing multiple houses
* adding account/character houses
* further work on AccountHouses / CharacterHouses
* latest updates, account/character multihouse polish, apartment deed location
* adding /house-select process
* HouseManager refactoring, removing async, fixing PlayerHouse.CompareTo equals, fixed InventoryLoaded for weenies, added Player_House.GetHouseInstance()
* first posted draft
* wiring up the remaining HouseInstance -> GetHouseInstance()
* preventing maintenance items from being added for houses owned by players in multihouse state
* cleaning up log messages, handling some edge cases
* Add GetCharacterByGuid
* Adjust finger command to only return "playable" characters, and correct accounts for offline players
* Add IsDeleted and adjust IsPendingDeletion
This is just a partial fix for dispoing contexts properly.
The contexts that are released from BiotaContexts as biotas go out of scope are never disposed. They are only finalized when the GC collects them. I plan to come up with a solution for that as well.
* Flags attribute
* empty statement
* Fix Formatting
* Fix OrderBy -> ThenBy
* use OfType
* Replace with asingle call to Count(...)
* Replace with a single call to FirstOrDefault
* Merge cast with type check
* use String.IsNullOrEmpty
* use collections count property
* use format specifier (results in shorter loc)
* use Any() instead of Count() > 0
* revert ThenBy with comment
* Rework Name and IsPlussed a bit
* Add basic support for player weenie changes when accesslevel changes
* Cleanup
* Update changelog
* cleanup
* clean up returns
* Return to Virtual + Override
* cleanup
* Restore @rename functionality
* Strip + from killer's name in Corpse.LongDesc for VTank compatiblity
* Add @pk command
* Add in support for PlayerKillerStatus.Free
* Add Chorzite to comps list
* If Level is greater than maxlevel, do not crash
* Fix @morph command
* Reuse available Dynamic GUIDs
On ACE startup, the shard database is queried for available id's in the dynamic guid range.
This helps further reduce (max + 1) id allocation.
It's a bit premature, but, based on current architecture, I don't know if we'll ever need to do a id defragment.
A requirement of this method is that ace_world may only contain guids in the static range.
* GetMaxGuidFoundInRange has no valid use in ace_world
All ids must be in the static range
* Add world update script
This changes the SaveBiota from using a Write lock to using a Read lock. The benefit here is that now the biota can still be read while it is in the process of being saved.
We don't need a write lock when we save the biota because the only properties that will be changed are record id's will be added for records that are new.
Because we maintain the context for the life of the biota, when we add/save, it's simply a one way change, meaning, only the database will change. The biota itself is just read and compared to the clone that the context holds. Thus, this is a safe way to do it.