   Grid System.
  Hey, I am currently working on a grid-update-system for my server (not sure you call it that, if anyone knows the right term, please tell :) ). The client expects a UPDATE_OBJECT packet. which contains updates for already existing objects (on the client) or info to create a new object. (i dont want to send the create new object part, if the object already exists on the server.)The system and one of my problems is probably best explained with a picture:I already create a first draft of the WorldCell class, to represent on cell in that grid.using System;using System.Collections.Generic;using System.Text;using WowwoW.Tools;using WowwoW.RealmServer.Objects;namespace WowwoW.RealmServer.GameWorld{/// &lt;summary&gt;/// This is not the fastest implementation but its simple. and why chase bottlenecks that/// arent even there yet./// /// Status: active - inactive/// What does this mean?/// if a cell is active it means that /// it will send updates about objects /// inside it to players and that creatures/// inside it will "live"./// if a cell is inactive the cell won't/// update players about status changes and/// the creatures will "sleep"./// The big question is when should a cell be/// activated. Only when a player enters, or when/// a player enters a cell next to it. I think/// a cell should be activated as soon as a player/// is in a cell next to it./// &lt;/summary&gt;class WorldCell : WoWTimer{Int32 myX;Int32 myY;List&lt;BaseObject&gt; myObjects = new List&lt;BaseObject&gt;(50);List&lt;Character&gt; myCharacters = new List&lt;Character&gt;(50);List&lt;WorldCell&gt; myCellsAround;Boolean myStatus;/// &lt;summary&gt;/// creates a new WorldCell./// &lt;/summary&gt;/// &lt;param name="xOffset"&gt;the Xoffset of this cell&lt;/param&gt;/// &lt;param name="yOffset"&gt;the Yoffset of this cell&lt;/param&gt;/// &lt;param name="nextCells"&gt;a List of cells around this cell.&lt;/param&gt;public WorldCell(Int32 xOffset,Int32 yOffset, List&lt;WorldCell&gt; nextCells) : base(2000,String.Format("WorldCell[{0},{1}]",xOffset,yOffset)){myX = xOffset;myY = yOffset;myCellsAround = nextCells;}/// &lt;summary&gt;/// Adds an BaseObject to this Cell./// &lt;/summary&gt;/// &lt;param name="anObject"&gt;&lt;/param&gt;public void Add(BaseObject anObject) {if (myObjects.Contains(anObject))throw new Exception("Object Added Twice");myObjects.Add(anObject);}/// &lt;summary&gt;/// Adds a Character to this Cell/// &lt;/summary&gt;/// &lt;param name="aCharacter"&gt;&lt;/param&gt;public void Add(Character aCharacter) {Add((BaseObject)aCharacter);myCharacters.Add(aCharacter);activateCell();}/// &lt;summary&gt;/// Removes a Creature from this cell/// &lt;/summary&gt;/// &lt;param name="aCreature"&gt;&lt;/param&gt;public void Remove(BaseObject anObject) {if (!myObjects.Contains(anObject))throw new Exception("Tryed to remove nonexistant Object");myObjects.Remove(anObject);}/// &lt;summary&gt;/// Removes a Character from this Cell/// &lt;/summary&gt;/// &lt;param name="aCharacter"&gt;&lt;/param&gt;public void Remove(Character aCharacter) {Remove((BaseObject)aCharacter);myCharacters.Remove(aCharacter);deactivateCell();}/// &lt;summary&gt;/// Activates a cell./// &lt;/summary&gt;private void activateCell(){/// if we are already active/// we don't need to do a thing.if (myStatus)return;myStatus = true;/// activate cell here. activate mobs/// etc.}/// &lt;summary&gt;/// Tries to deactivate the cell./// &lt;/summary&gt;private void deactivateCell(){/// If we have still players inside ourself no/// need to deactivateif (myCharacters.Count &gt; 0)return;/// if a cell next to us still has players/// lets stay active.foreach (WorldCell aCell in myCellsAround)if (aCell.myCharacters.Count &gt; 0)return;/// No player around. We should deactivate/// to safe resources.myStatus = false;//deactivate cell here. deactivate mobs etc.}/// &lt;summary&gt;/// Loads the objects for this cell./// &lt;/summary&gt;public void Load() {//Load stuff and add to List}/// &lt;summary&gt;/// Safes all objects in this cell./// &lt;/summary&gt;public void Safe() { }/// &lt;summary&gt;/// Unloads all objects in this cell./// &lt;/summary&gt;public void UnLoad() { }/// &lt;summary&gt;/// This updates all players around./// &lt;/summary&gt;public void Update(){/// create a update builder here./// this is really ugly.../// we have to find a better way \o/using (UpdateObjectBuilder myBuilder = new UpdateObjectBuilder()){foreach (WorldCell aCell in myCellsAround){if (aCell.myCharacters.Count == 0)continue;foreach (Character aChar in aCell.myCharacters){myBuilder.Target = aChar;/// we have to generate one packet for each player/// because dynamic flags could be different./// there must be a better way here \o/.foreach (BaseObject anObject in myObjects)myBuilder.AddFieldUpdate(anObject);myBuilder.Flush();myBuilder.Reset();}}foreach (BaseObject anObject in myObjects){ }//anObject.Updated(); or something...}}public List&lt;Character&gt; Characters { get { return myCharacters; } }public List&lt;BaseObject&gt; Objects { get { return myObjects;} }public Boolean Active{get { return myStatus; }}}}Although i dont know if its the best approach to update on a per cell basis. I though about maybe on a per player basis or something else. Anyway i wanted to get some feedback from you guys, maybe someone has a better idea on how to implement this.Thanks in advance,[Edited by - Gotisch on September 14, 2007 9:45:54 AM]
  I might be over simplifying an idea. But just give every object a number. It's what I've always done. What do you think the client would do if it got information for an object with a number ID 2045 but for some reason that number didn't exist to the client. The client would make room for it and throw away the info it received or store it. It would then request info on the object. The server would verify and send a full state update for the user rather than a state change. The player would go on their merry way until the object got way too far out of range.What happens then? You remove the object. Mind you the range at which the object is deleted is pretty much when the server stops sending you data for that object. Giving all the objects in range a time out feature is a good idea. If the player gets kicked from the server, the server will stop sending you data for the player. It gets removed automagically and the world is happy :)For figuring out range, I too use a grid on the server and client, but it's for spatial partitioning so it has more than one purpose. You'd iterate beyond the player's sight range and send them data they don't even care about. Mind you I don't mean update it, but you can tell someone about an object and set it's time out to 1 minute in case the player gets close really quick and the server doesn't freak out and the player sees nothing for a few seconds. Tons of ways to handle it.Good UDP approach anyway. If you are using TCP you might handle things differently. Especially if it's not really real-time.   ;Quote:not sure you call it thatArea of Interest management.   ;  First: use [ source ] tags for your code, to get that nice scrolling window with syntax hilite.Second: I wouldn't use an object per world cell. Perhaps I'd use a list head per world cell, and link objects to cells. You can also have a list of players per cell. When an object moves, it will queue a message to all players in cells that it leaves behind, and another message to players in cells that become new. Similarly, when a player moves, it will queue messages on itself for objects left and coming into view.You still have a problem where first an object moves, and goes out of view, and then the player moves, and the object goes into view again. You can solve that by keeping the queue of messages, and collapsing the first message out of the queue when the contradictory messages is sent. Then send messages from the queue every so often (don't know if you're turn based or real-time).;Quote:Second: I wouldn't use an object per world cell. Perhaps I'd use a list head per world cell, and link objects to cells. You can also have a list of players per cell. When an object moves, it will queue a message to all players in cells that it leaves behind, and another message to players in cells that become new. Similarly, when a player moves, it will queue messages on itself for objects left and coming into view.I was considering using such a system, and realized there's no need for grid at all.Assuming rectangular world (w*h), cell index can be obtained by simply partitioning the grid into chunks sized (sw,sh) and indexing it.Index of the cell can now be determined from player's coordinates (x,y):Player cell: (x/sw, y/sh) = (x',y'), cell index (Ci) = y' * (w/sw) + x'Each player maintains a list of cells it's subscribed to. This can be a simple, possibly fixed size array (depending on the grid size, 4x4, 5x5). My concept assumed that player always sees 3x3, but is subscribed to 4x4, where the extra row and column are determined based on their movement (the usual tile lookahead).Since we know the relation between cell indices, subscription now becomes merely manipulation of indices.At any given time, player is subscribed to Ci (and Ci-1, Ci+1, Ci-(w/sw)-1, Ci-(w/sw), Ci-(w/sw)+1, ....).Set operations determining which cells to subscribe to now become very cheap. Analyzing the movement it's rather easy to determined which indices to add, and which to remove, without taking into consideration actual coordinates, or other expensive proximity checks. It's also possible to organize the array storing subscriptions in a certain way to make subscription changes essentially free, or O(C), C={3,4,7,9,16} operation, all on integers.With this approach, cells are managed using a hash map. Worst-case size of the map will be n_players * 4x4 (if that's our AoI), so even for a large game, the total number of cell objects that need to be alive is in the tens of thousands. Typical case however will be much better (Zipf or Gaussian distribution of population), resulting in only hundreds or thousands of cells.Ultimately, I chose against such approach, since it imposes heavy restrictions, and doesn't allow variable AoI, dungeons, may cause problems with multiple layers or different altitudes (tunnels, bridges), or force undesirable partitioning (showing objects which are beyond unpassable wall) and overall lack of granularity, which may be adequate for completely open worlds, but unsuitable for interiors or extruded paths (corridors, tunnels).   ;  Even if you implement your grid as a 1D array (which is very common, for example when dealing with bitmap images), it's still a grid :-)If you want another rule for interest than simple grid distance, then a grid (however implemented) won't work right. For example, in a dungeon, you may have a portal system for rendering, and thus probably want to use something other than a grid for interest.;  Ok a little update on what i finally decided.public class World{static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);static readonly Single myCellSize = 133.33f;static Dictionary<Int32, WorldCell> myWorld = new Dictionary<Int32, WorldCell>(100);static Dictionary<UInt64, WorldObject> myObjects = new Dictionary<UInt64, WorldObject>(100);public static void Add(WorldObject anObject) {if (myObjects.ContainsKey(anObject.Guid.Full))throw new Exception("Object already in World");myObjects.Add(anObject.Guid.Full, anObject);Position aPosition = anObject.Position;Int32 aCell = (int)(aPosition.X / myCellSize)+ (int)(aPosition.Y / myCellSize) * 100+ anObject.Map * 10000;List<Character> CharactersToNotify = new List<Character>();for (int y = aCell - 100; y <= aCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){///If the cell doesnt exists create it!if (!myWorld.ContainsKey(x)){/// if the cell is not there, we add one to the world./// normally this should never happen./// since why would mobiles be added somewhere where there are no/// players? the cell isnt activated anyway./// problem here would be that creating a new worldcell, should/// load all the mobiles in it, no? /// if it loads all the mobiles, this will result in a cascading loop/// that will eventually spawn the whole map./// so worldobjects shouldnt be loaded by a cell unless the cell is activated/// due to the presence of a player./// actually when a cell loads the mobiles it shouldn't send anything.myWorld[x] = new WorldCell(aCell);}else{/// If the cell is there we take the chars we will need to update about this new object.CharactersToNotify.AddRange(myWorld[x].Characters);}}myWorld[aCell].Add(anObject);if (CharactersToNotify.Count == 0)return;using (UpdateObjectBuilder a = new UpdateObjectBuilder()){foreach (Character aCharacter in CharactersToNotify){a.Target = aCharacter;a.AddCreateUpdate(anObject);a.Flush();a.Reset();}}}public static void Remove(WorldObject anObject) {if (!myObjects.ContainsKey(anObject.Guid.Full))throw new Exception("Can't remove nonexistant Objects");myObjects.Remove(anObject.Guid.Full);Position aPosition = anObject.Position;Int32 aCell = (int)(aPosition.X / myCellSize)+ (int)(aPosition.Y / myCellSize) * 100+ anObject.Map * 10000;List<Character> CharactersToNotify = new List<Character>();for (int y = aCell - 100; y <= aCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){if (!myWorld.ContainsKey(x))continue;CharactersToNotify.AddRange(myWorld[x].Characters);}myWorld[aCell].Remove(anObject);if (CharactersToNotify.Count == 0)return;using (PacketOutBuilder a = new PacketOutBuilder((short)OpCodes.SMSG_DESTROY_OBJECT)){a.Add(anObject.Guid.Full);foreach (Character aChar in CharactersToNotify)a.Send(aChar.Handler);}}public static void Add(Character aCharacter) {if (myObjects.ContainsKey(aCharacter.Guid.Full))throw new Exception("Object already in World");myObjects.Add(aCharacter.Guid.Full, aCharacter);try{Position aPosition = aCharacter.Position;Int32 aCell = (int)(aPosition.X / myCellSize)+ (int)(aPosition.Y / myCellSize) * 100+ aCharacter.Map * 10000;List<Character> CharactersToNotify = new List<Character>();List<WorldObject> ObjectsToCreate = new List<WorldObject>();for (int y = aCell - 100; y <= aCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){///If the cell doesnt exists create it!if (!myWorld.ContainsKey(x)){/// if the cell is not there, we add one to the world.myWorld[x] = new WorldCell(x);}myWorld[x].Ping(); //we activate the world if its not already active./// in the ping function the cell doesnt need to send any data to anyone./// because a cell can only be activated when a player enters the range,/// at which moment the update gets collected anyway.CharactersToNotify.AddRange(myWorld[x].Characters);ObjectsToCreate.AddRange(myWorld[x].Objects);}/// we now have two lists./// One containing all characters (besides this one) that should get info about this character/// and one containing all objects this character should be rendered aware of./// what we do now is add our character to this second list.ObjectsToCreate.Add(aCharacter); //because we add this char to the world, which means he doesnt/// even know about himself.myWorld[aCell].Add(aCharacter);using (UpdateObjectBuilder a = new UpdateObjectBuilder()){if (CharactersToNotify.Count != 0) // If there are chars to notify about this one do it.foreach (Character someCharacter in CharactersToNotify){a.Target = someCharacter;a.AddCreateUpdate(aCharacter);a.Flush();a.Reset();}/// Finally we send the added character all the info about the world.a.Target = aCharacter;foreach (WorldObject anObject in ObjectsToCreate)a.AddCreateUpdate(anObject);a.Flush();}}catch (Exception e){log.ErrorFormat("Error Adding Character {0} to the World:{1}",aCharacter, e);myObjects.Remove(aCharacter.Guid.Full);/// we need to remove the character from the cell too!}}public static void Remove(Character aCharacter) {if (!myObjects.ContainsKey(aCharacter.Guid.Full))throw new Exception("Can't remove nonexistant Objects");myObjects.Remove(aCharacter.Guid.Full);Int32 aCell = aCharacter.Cell;List<Character> CharactersToNotify = new List<Character>();myWorld[aCell].Remove(aCharacter);for (int y = aCell - 100; y <= aCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){if (!myWorld.ContainsKey(x))continue; //should never happen...CharactersToNotify.AddRange(myWorld[x].Characters);myWorld[x].Pong();}if (CharactersToNotify.Count == 0)return;using (PacketOutBuilder a = new PacketOutBuilder((short)OpCodes.SMSG_DESTROY_OBJECT)){a.Add(aCharacter.Guid.Full);foreach (Character aChar in CharactersToNotify)a.Send(aChar.Handler);}}/// <summary>/// This should be called by Objects after their movement./// </summary>/// <param name="anObject"></param>public static void Update(WorldObject anObject) {if (anObject.Cell == (int)(anObject.Position.X / myCellSize)+ (int)(anObject.Position.Y / myCellSize) * 100+ anObject.Map * 10000)return;Int32 oldCell = anObject.Cell;Int32 newCell = (int)(anObject.Position.X / myCellSize)+ (int)(anObject.Position.Y / myCellSize) * 100+ anObject.Map * 10000;myWorld[oldCell].Remove(anObject);List<WorldCell> oldCells = new List<WorldCell>(9);List<WorldCell> newCells = new List<WorldCell>(9);for (int y = oldCell - 100; y <= oldCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){//myWorld[x].Pong(); objects dont ping/pongoldCells.Add(myWorld[x]);}for (int y = newCell - 100; y <= newCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){if (!myWorld.ContainsKey(x))myWorld[x] = new WorldCell(x);//myWorld[x].Ping(); objects dont ping/pongnewCells.Add(myWorld[x]);}/// we now need the differences that is objects that appear in oldcells but not/// newcells and those that appear in newcells but not oldcells.oldCells.RemoveAll(delegate(WorldCell aCell) { return newCells.Remove(aCell); //this returns true if the item was found./// this way we remove the cell from boths lists at once!});/// now oldcells only has the cells where the object isnt in range anymore/// and newcells only the cells where the object is newly in range.using (PacketOutBuilder a = new PacketOutBuilder((short)OpCodes.SMSG_DESTROY_OBJECT)){a.Add(anObject.Guid.Full);foreach (WorldCell aCell in oldCells)if (aCell.Characters.Count > 0)foreach (Character aChar in aCell.Characters)a.Send(aChar.Handler);}using (UpdateObjectBuilder a = new UpdateObjectBuilder())foreach (WorldCell aCell in newCells)if (aCell.Characters.Count > 0)foreach (Character aChar in aCell.Characters){a.Target = aChar;a.AddCreateUpdate(anObject);a.Flush();a.Reset();}myWorld[newCell].Add(anObject);}/// <summary>/// This should be called by players on a regular basis./// Timer maybe. Can also manually update it if you think/// a players position is not syncronised in the world./// </summary>/// <param name="aCharacter"></param>public static void Update(Character aCharacter) {if (aCharacter.Cell == (int)(aCharacter.Position.X / myCellSize)+ (int)(aCharacter.Position.Y / myCellSize) * 100+ aCharacter.Map * 10000)return;Int32 oldCell = aCharacter.Cell;Int32 newCell = (int)(aCharacter.Position.X / myCellSize)+ (int)(aCharacter.Position.Y / myCellSize) * 100+ aCharacter.Map * 10000;myWorld[oldCell].Remove(aCharacter);List<WorldCell> oldCells = new List<WorldCell>(9);List<WorldCell> newCells = new List<WorldCell>(9);for (int y = oldCell - 100; y <= oldCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){oldCells.Add(myWorld[x]);}for (int y = newCell - 100; y <= newCell + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){if (!myWorld.ContainsKey(x))myWorld[x] = new WorldCell(x);myWorld[x].Ping();newCells.Add(myWorld[x]);}/// we now need the differences that is objects that appear in oldcells but not/// newcells and those that appear in newcells but not oldcells.oldCells.RemoveAll(delegate(WorldCell aCell){return newCells.Remove(aCell); //this returns true if the item was found./// this way we remove the cell from boths lists at once!});/// now oldcells only has the cells where the object isnt in range anymore/// and newcells only the cells where the object is newly in range.using (PacketOutBuilder a = new PacketOutBuilder((short)OpCodes.SMSG_DESTROY_OBJECT)){a.Add(aCharacter.Guid.Full);foreach (WorldCell aCell in oldCells){aCell.Pong();if (aCell.Characters.Count > 0)foreach (Character aChar in aCell.Characters)a.Send(aChar.Handler);}}using (UpdateObjectBuilder b = new UpdateObjectBuilder(aCharacter)){using (UpdateObjectBuilder a = new UpdateObjectBuilder())foreach (WorldCell aCell in newCells) // For each "new" cell{if (aCell.Characters.Count > 0) // If there are Charactersforeach (Character aChar in aCell.Characters){a.Target = aChar;a.AddCreateUpdate(aCharacter); //Create this char for them.a.Flush();a.Reset();}/// And add all objects here to the objects that need to be created for/// the character.foreach (WorldObject anObject in aCell.Objects)b.AddCreateUpdate(anObject);}b.Flush(); //send the created objects to the character.}myWorld[newCell].Add(aCharacter);}/// <summary>/// This is not the fastest implementation but its simple. and why chase bottlenecks that/// arent even there yet./// /// Status: active - inactive/// What does this mean?/// if a cell is active it means that /// it will send updates about objects /// inside it to players and that creatures/// inside it will "live"./// if a cell is inactive the cell won't/// update players about status changes and/// the creatures will "sleep"./// The big question is when should a cell be/// activated. Only when a player enters, or when/// a player enters a cell next to it. I think/// a cell should be activated as soon as a player/// is in a cell next to it./// </summary>class WorldCell : WoWTimer {Int32 myHash;List<WorldObject> myObjects = new List<WorldObject>(50);List<Character> myCharacters = new List<Character>(50);Boolean myStatus;/// <summary>/// creates a new WorldCell./// </summary>/// <param name="xOffset">the Xoffset of this cell</param>/// <param name="yOffset">the Yoffset of this cell</param>/// <param name="nextCells">a List of cells around this cell.</param>public WorldCell(Int32 aCellHash): base(2000, String.Format("WorldCell[{0}]", aCellHash)){myHash = aCellHash;}/// <summary>/// Adds an BaseObject to this Cell./// </summary>/// <param name="anObject"></param>public void Add(WorldObject anObject){if (myObjects.Contains(anObject))throw new Exception("Object Added Twice");myObjects.Add(anObject);anObject.Cell = myHash;}/// <summary>/// Adds a Character to this Cell/// </summary>/// <param name="aCharacter"></param>public void Add(Character aCharacter){Add((WorldObject)aCharacter);myCharacters.Add(aCharacter);}/// <summary>/// Removes a Creature from this cell/// </summary>/// <param name="aCreature"></param>public void Remove(WorldObject anObject){if (!myObjects.Contains(anObject))throw new Exception("Tryed to remove nonexistant Object");myObjects.Remove(anObject);}/// <summary>/// Removes a Character from this Cell/// </summary>/// <param name="aCharacter"></param>public void Remove(Character aCharacter){Remove((WorldObject)aCharacter);myCharacters.Remove(aCharacter);}/// <summary>/// Activates a cell./// </summary>public void Ping(){/// if we are already active/// we don't need to do a thing.if (myStatus)return;myStatus = true;/// activate cell here. activate mobs/// etc.}/// <summary>/// Tries to deactivate the cell./// </summary>public void Pong(){/// If we have still players inside ourself no/// need to deactivateif (myCharacters.Count > 0)return;/// if a cell next to us still has players/// lets stay active.for (int y = myHash - 100; y <= myHash + 100; y += 100)for (int x = y - 1; x <= y + 1; x++)if (World.myWorld.ContainsKey(x) && World.myWorld[x].myCharacters.Count > 0)return;/// No player around. We should deactivate/// to safe resources.myStatus = false;//deactivate cell here. deactivate mobs etc.}/// <summary>/// Loads the objects for this cell./// </summary>public void Load(){//Load stuff and add to List}/// <summary>/// Safes all objects in this cell./// </summary>public void Safe() { }/// <summary>/// Unloads all objects in this cell./// </summary>public void UnLoad() { }/// <summary>/// This updates all players around./// </summary>public void Update(){/// create a update builder here./// this is really ugly.../// we have to find a better way \o/WorldCell aCell;using (UpdateObjectBuilder myBuilder = new UpdateObjectBuilder()){for (int y = myHash - 100; y <= myHash + 100; y += 100)for (int x = y - 1; x <= y + 1; x++){if (!World.myWorld.ContainsKey(x))continue;aCell = World.myWorld[x];if (aCell.myCharacters.Count == 0)continue;foreach (Character aChar in aCell.myCharacters){myBuilder.Target = aChar;/// we have to generate one packet for each player/// because dynamic flags could be different./// there must be a better way here \o/.foreach (BaseObject anObject in myObjects)myBuilder.AddFieldUpdate(anObject);myBuilder.Flush();myBuilder.Reset();}}foreach (BaseObject anObject in myObjects){ }//anObject.Updated(); or something...}}public List<Character> Characters{get{return myCharacters;}}public List<WorldObject> Objects{get{return myObjects;}}public Boolean Active{get { return myStatus; }}}}Although im not to happy with it, since i think it could break when there are exceptions in either updateing, adding or removing players. Also there are threading issues i ahve become aware of (i want to remove a character from the world when the associated connection is dropped, but connections drop in async threads and not the applications main loop. This could be a problem, but its more a general design flaw then something related to the world.There are also lots of loops in this code, i am not sure how much of a performance hook that is. Well currently im bugtesting it so there still might be errors in the design im not aware of.Another problem is, that Add / remove updates dont get cached. i would prefer if i could only update the client every x ms with all changes (unless its time critical data) but i would need to somehow buffer updatesegments and track them. 