Well using the current code yes, a second collection to handle the objects data cached in client for the object went outofview is working and seems to be the only way because the obj in removed from GameObjectUpdateArray and you lose the data about this obj for the client.I wonder if I'll need a second collection for Object still existing for "Client" but that server removed from "Player" Vincinity or if I could use some specific values like setting Object "Update" Time to long.MaxValue...
All this discussion lead me to think that the GameObjectUpdateArray is not enough to keep track of what Server AND Client do about Object Update...
mmm again , are you sure 100% of that? tested ?Good one !
Yes I made the "out of view" update to make sure the player will get the last "packet" from this mob in a correct position.
This was made to prevent client keeping old mob speed info when getting out of view (example the mob sent its last update 8 seconds ago running towards you but in fact you outrunned him some time ago and he's returning to spawn)
Yes the "outofview" collection must be cleared like the GameCurrentArray and maybe when you move the player over the RefreshDistance in the player .MoveTo() -> .RefreshWorld() but you have to send a SendObjectRemove to every single obj in that list if exists "in a row".Your static method in Region.RemoveObject is also a solution I tried to implement locally, it sends remove packet to every player still having the object in cache (in Server Side Cache), but without using a "out of view collection"
the out of view collection would need to be updated when an object re-enter the player view range, and when we can guess that client removed this object from his cache, so we can't do "all the job" in the Region.RemoveObject Method, we have to change the Update Thread to keep this other collection clean (Clearing when changing Region ? Removing Object that didn't send update to player since "Cache Time" ? etc...)
An other change I tried locally is to change the Region nextObjectID update when removing object from Region, every object removed always register it's Own ID as the next Free ID this cause the most troubles you've seen even if it's nice to show WorldUpdateThread Wrong Implementations This nextObjectID update is only needed when the Region Object Array is Full to improve Next ID Lookup (this mean we still have to fix the Update Thread in case we're in a "Full" Region)
Create Request Packet are somewhat handled sequentially by client that's why there is always some "latency" when porting on long range to make new NPC appear, update packet are meant to be concurrent for the client (they should even be handled as UDP, but we force them through TCP because we can't handle UDP Encryption), so you should better send too much "Update" than too much "Create"
public sealed class ObjectUpdateData
{
long m_updateTime;
public long UpdateTime { get { return m_updateTime; } set { m_updateTime = value; OutOfView = false; }; }
public bool OutOfView { get; set; }
public DataObjectUpdate(long time) { UpdateTime = time; }
}
public ReaderWriterDictionary<Tuple<ushort, ushort>, ObjectUpdateData> GameObjectUpdateArray...
public bool UpdateIfExists(TKey key, Func<TValue, TValue> func)
{
m_rwLock.EnterUpgradeableReadLock();
bool replaced = false;
try
{
if (m_dictionary.ContainsKey(key))
{
m_rwLock.EnterWriteLock();
try
{
m_dictionary[key] = func(m_dictionary[key]);
replaced = true;
}
finally
{
m_rwLock.ExitWriteLock();
}
}
}
finally
{
m_rwLock.ExitUpgradeableReadLock();
}
return replaced;
}
dict.UpdateIfExists(key, obj => { obj.OutOfView = true; return obj; } );
yes ofcif you can test your own implementation already we'll know if there are any flows in the "specs"
private static void UpdatePlayerWorld(GamePlayer player, long nowTicks)
{
// Update Player Player's
if (ServerProperties.Properties.WORLD_PLAYERTOPLAYER_UPDATE_INTERVAL > 0)
UpdatePlayerOtherPlayers(player, nowTicks);
// Update Player Mob's
if (ServerProperties.Properties.WORLD_NPC_UPDATE_INTERVAL > 0)
UpdatePlayerNPCs(player, nowTicks);
// Update Player Static Item
if (ServerProperties.Properties.WORLD_OBJECT_UPDATE_INTERVAL > 0)
UpdatePlayerItems(player, nowTicks);
// Update Player Doors
if (ServerProperties.Properties.WORLD_OBJECT_UPDATE_INTERVAL > 0)
UpdatePlayerDoors(player, nowTicks);
//update OutofviewCache
UpdateClientOutOfViewCache(player, nowTicks);
// Update Player Housing
if (ServerProperties.Properties.WORLD_OBJECT_UPDATE_INTERVAL > 0)
UpdatePlayerHousing(player, nowTicks);
}
// We have a NPC in cache that is not in vincinity
if (obj is GameNPC && !npcs.Contains((GameNPC)obj) && (nowTicks - objEntry.Value) >= GetPlayerNPCUpdateInterval)
{
// Update him out of View
if (obj.IsVisibleTo(player))
player.Client.Out.SendObjectUpdate(obj);
long dummy;
// this will add the object to the cache again, remove it after sending...
if(player.Client.GameObjectUpdateArray.TryRemove(objKey, out dummy))
{
if (!player.Client.OutOfViewGameObjectUpdateArray.AddIfNotExists(objKey, GameTimer.GetTickCount()))
player.Client.OutOfViewGameObjectUpdateArray[new Tuple<ushort, ushort>(obj.CurrentRegionID, (ushort)obj.ObjectID)] = GameTimer.GetTickCount();
}
}
foreach (var objEntry in player.Client.GameObjectUpdateArray)
{
var objKey = objEntry.Key;
GameObject obj = WorldMgr.GetRegion(objKey.Item1).GetObject(objKey.Item2);
// Brain is updating to its master, no need to handle it.
if (obj is GameNPC && ((GameNPC)obj).Brain is IControlledBrain && ((IControlledBrain)((GameNPC)obj).Brain).GetPlayerOwner() == player)
continue;
//Stop player if he's running....
CurrentSpeed = 0;
foreach (GamePlayer player in GetPlayersInRadius(WorldMgr.VISIBILITY_DISTANCE))
{
if (player.ObjectState != eObjectState.Active || player == null || player == this)
continue;
//Maybe there is a better solution?
player.Out.SendObjectRemove(this);
player.Out.SendPlayerCreate(this);
}
In this case , the player is in-view and it leaves his very last update in the worldupdatethread (we can't check the object remove trigger to remove a "outofview" obj in cache) This is what is happen in-game.After a kick, I see a ghost-char , visible and targettable but not a real target.SendPlayerCreate is updating the GameObjectUpdateArray.
What is the trouble here ? The ObjectRemove should be sent after Linkdead timer runs out
Users browsing this forum: No registered users and 1 guest