Thread: Player updating misconception

Page 1 of 2 12 LastLast
Results 1 to 10 of 20
  1. #1 Player updating misconception 
    Renown Programmer and Respected Member
    Maxi's Avatar
    Join Date
    Jun 2008
    Posts
    3,197
    Thanks given
    281
    Thanks received
    1,095
    Rep Power
    1366
    When I was writing the player updating for the proof of concept actor server I'm writing, I stumbled onto something that got me curious whilst referencing a part of Hyperion's protocol. This part of adding players to the update list is what I'm talking about:

    Code:
    /*
     * Write two flags here: the first indicates an update is required
     * (this is always true as we add the appearance after adding a player)
     * and the second to indicate we should discard client-side walk
     * queues.
     */
    packet.putBits(1, 1);
    packet.putBits(1, 1);
    This code is part of the method called addNewPlayer(...). The comment says the second bit sent is a flag to let the client discard the client sided walking queues. When I looked into the method that does something with this flag I noticed it's actually quite some different. It's actually has to do with cached players that were not in the current client's viewport but within 8 tiles from the current client's viewport. This led to an understanding of a misconception we have been carrying with us for quite some time. Allow me to explain how this current conception is wrong and what I think is the correct way.

    The client's viewport is 32 x 32 tiles big. This is exactly 4 x 4 segments big. Segments are blocks of 8 x 8 tiles. You need to build your viewport from a block of 5 x 5 segments covering 40 x 40 tiles and ignore tiles/players you don't want to consider because of the client's 32 x 32 viewport. The center segment of this 5 x 5 area would be the segment you're currently in. You would use the 5 x 5 segments area to take the players within to queue for adding to the local update list of the client and still have a distance check to make sure you only send players within the 32 x 32 viewport when trying to add players to the client. Assume you add a player to the local viewport and this player is on the edge of the 32 x 32 area. If you walk away one tile, he will be outside the 32 x 32 viewport. The code mentioned above allows us to not remove the player from the client yet as explicitly as long as the player stays within the 5 x 5 segment. Instead it caches the player and if the player walks one tile into the same direction as you did, or you walk back, we can notify the client through that code above and let it know it's not a 'new' player but instead a player that was still within the 5 x 5 segments area and let it update its walking queue.

    Consider this code:

    Code:
    protected Set<Integer> localList = new LinkedHashSet<Integer>();
    protected Set<Integer> additionList = new LinkedHashSet<Integer>();
    protected Set<Integer> removalList = new LinkedHashSet<Integer>();
    protected Set<Integer> cachedList = new LinkedHashSet<Integer>();
    This gives us an idea how to handle this concept. Players that are in the 5 x 5 segment area are added to the additionList list. From this list we add players to the localList, these are the players that are within the 32 x 32 client's viewport. All players that leave the 5 x 5 segment area or you move to a neighbouring segment and are left that way, are on the removalList list and those players are removed form the localList. Players that are not within the viewport anymore are still considered being on the localList but don't get updates, instead for them the client receives false flags for updating. They are stored on the cachedList and are checked upon in the procedure of adding players to the viewport, to see if they are back in the viewport. If they are, the first bit mentioned above is set to 0 or 1 according to whether an appearance update needs to be forced and or other masks have been flagged for updating. The second bit referenced above is set to 0 if a cached player is added back or to 1 if it's a new player.

    This is a concept implementation:

    Code:
    package maximemeire.phantom.model.entity.player.update;
    
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.Map;
    import java.util.Set;
    
    import maximemeire.phantom.model.entity.MovementNode;
    import maximemeire.phantom.model.entity.player.Player;
    import maximemeire.phantom.model.entity.player.update.PlayerUpdateFlags.PlayerUpdateFlag;
    import maximemeire.phantom.network.DynamicOutboundBuffer;
    
    /**
     * @author Maxime Meire
     *
     */
    public class PlayerUpdate {
    	
    	protected PlayerUpdateNode updateNode;
    	protected Set<Integer> localList = new LinkedHashSet<Integer>();
    	protected Set<Integer> additionList = new LinkedHashSet<Integer>();
    	protected Set<Integer> removalList = new LinkedHashSet<Integer>();
    	protected Set<Integer> cachedList = new LinkedHashSet<Integer>();
    	protected Map<Integer, PlayerUpdateNode> otherUpdates = new HashMap<Integer, PlayerUpdateNode>();
    	private int movementSize = 512;
    	private int generalSize = 256;
    
    	protected static DynamicOutboundBuffer encodePayload(Player player) {
    		try {
    			PlayerUpdate update = player.getUpdate();
    			int movementSize = update.movementSize + (1 / 4) * update.movementSize;
    			DynamicOutboundBuffer payload = new DynamicOutboundBuffer(movementSize);
    			int generalSize = update.generalSize + (1 / 2) * update.generalSize;
    			DynamicOutboundBuffer masks = new DynamicOutboundBuffer(generalSize);
    			payload.start_bit_access();
    			encodeThisMovement(player, payload);
    			encodeGeneralUpdate(update.updateNode, masks, false);
    			payload.put_bits(8, update.localList.size());
    			encodeOtherMovementUpdates(player, payload, masks);
    			encodeAddFOVPlayer(player, payload, masks);
    			if (masks.readable()) {
    				payload.put_bits(11, 2047);
    				payload.end_bit_access();
    				payload.writeBytes(masks);
    			} else {
    				payload.end_bit_access();
    			}
    			return payload;
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    	
    	protected static void encodeThisMovement(Player player, DynamicOutboundBuffer movement) {
    		PlayerUpdate update = player.getUpdate();
    		if (update.updateNode.teleported || update.updateNode.changedRegion) {
    			movement.put_bits(1, 1);
    			movement.put_bits(2, 3);
    			movement.put_bits(2, player.getLocation().getZ());
    			movement.put_bits(1, update.updateNode.teleported ? 1 : 0);
    			movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			movement.put_bits(7, player.getClientLoadedBase().deltaY(player.getLocation()));
    			movement.put_bits(7, player.getClientLoadedBase().deltaX(player.getLocation()));
    		} else {
    			if (update.updateNode.movement.notMoving()) {
    				if (update.updateNode.requireUpdate()) {
    					movement.put_bits(1, 1);
    					movement.put_bits(2, 0);
    				} else {
    					movement.put_bits(1, 0);
    				}
    			} else if (update.updateNode.movement.walking()) {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 1);
    				movement.put_bits(3, update.updateNode.movement.getFirst());
    				movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			} else {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 2);
    				movement.put_bits(3, update.updateNode.movement.getFirst());
    				movement.put_bits(3, update.updateNode.movement.getSecond());
    				movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			}
    		}
    	}
    	
    	protected static void encodeOtherMovementUpdates(Player player, DynamicOutboundBuffer movement, DynamicOutboundBuffer general) throws Exception {
    		PlayerUpdate update = player.getUpdate();
    		for (int id : update.localList) {
    			if (update.additionList.contains(id))
    				throw new Exception("Player is on the addition list but already on the local list");
    			if (update.removalList.contains(id)) {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 3);
    				update.localList.remove(id);
    				update.otherUpdates.remove(id);
    				update.cachedList.remove(id);
    				continue;
    			} else if (player.getLocation().absDeltaX(update.updateNode.location) > 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) > 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				movement.put_bits(1, 0);
    				update.cachedList.add(id);
    				continue;
    			} else {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				MovementNode movementNode = updateNode.movement;
    				if (movementNode.notMoving()) {
    					if (updateNode.requireUpdate()) {
    						movement.put_bits(1, 1); 
    						movement.put_bits(2, 0); 
    					} else 
    						movement.put_bits(1, 0);
    				} else if (movementNode.walking()) {
    					movement.put_bits(1, 1); 
    					movement.put_bits(2, 1); 
    					movement.put_bits(3, movementNode.getFirst());
    					movement.put_bits(1, updateNode.requireUpdate() ? 1 : 0);
    				} else {
    					movement.put_bits(1, 1);
    					movement.put_bits(2, 2);
    					movement.put_bits(3, movementNode.getFirst());
    					movement.put_bits(3, movementNode.getSecond());
    					movement.put_bits(1, updateNode.requireUpdate() ? 1 : 0);
    				}
    				if (updateNode.requireUpdate()) {
    					encodeGeneralUpdate(updateNode, general, false); 
    				}
    			}
    		}
    	}
    	
    	protected static void encodeGeneralUpdate(PlayerUpdateNode updateNode, DynamicOutboundBuffer general, boolean forceAppearance) {
    		if (!updateNode.requireUpdate() && !forceAppearance) {
    			return;
    		}
    		if (updateNode.hasCachedMaskBlock() && !forceAppearance) {
    			general.writeBytes(updateNode.cachedMaskBlock);
    			return;
    		}
    		DynamicOutboundBuffer cacheBlock = new DynamicOutboundBuffer(100);
    		int mask = updateNode.mask;
    		if (mask >= 0x100) {
    			mask |= 0x40;
    			cacheBlock.put8(mask & 0xff);
    			cacheBlock.put8(mask >> 8);
    		} else
    			cacheBlock.put8(mask);
    		if (updateNode.requireGraphicsUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.GRAPHICS_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireAnimationUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.ANIMATION_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireForcedChatUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FORCED_CHAT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireChatUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.CHAT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireFaceEntityUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FACE_ENTITY_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireAppearanceUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.APPEARANCE_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireFaceCoordinateUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FACE_COORDINATE_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireHitUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.HIT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireHit2Update()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.HIT2_UPDATE_MASK.ordinal()].payload);
    		}
    		if (!forceAppearance) {
    			updateNode.cachedMaskBlock = cacheBlock;
    		}
    		general.writeBytes(cacheBlock);
    	}
    	
    	protected static void encodeAddFOVPlayer(Player player, DynamicOutboundBuffer movement, DynamicOutboundBuffer general) throws Exception {
    		PlayerUpdate update = player.getUpdate();
    		for (int id : update.additionList) {
    			if (update.localList.contains(id))
    				throw new Exception("Player is already on the local list although scheduled to be added to it");
    			if (update.localList.size() >= 255) 
    				return;
    			if (update.cachedList.contains(id))
    				continue;
    			if (player.getLocation().absDeltaX(update.updateNode.location) <= 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) <= 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				movement.put_bits(11, updateNode.index);
    				movement.put_bits(1, 1);
    				movement.put_bits(1, 1);
    				int deltaX = updateNode.location.deltaX(player.getLocation());
    				int deltaY = updateNode.location.deltaY(player.getLocation());
    				movement.put_bits(5, deltaY);
    				movement.put_bits(5, deltaX);
    				encodeGeneralUpdate(updateNode, general, true);
    				update.localList.add(id);
    				update.additionList.remove(id);
    			} else {
    				continue;
    			}
    		}
    		for (int id : update.cachedList) {
    			if (update.localList.contains(id))
    				throw new Exception("Player is already on the local list although also cached.");
    			if (update.localList.size() >= 255)
    				return;
    			if (player.getLocation().absDeltaX(update.updateNode.location) <= 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) <= 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				movement.put_bits(11, updateNode.index);
    				movement.put_bits(1, 1); // force appearance until having the logic in place to check for it
    				movement.put_bits(1, 0);
    				int deltaX = updateNode.location.deltaX(player.getLocation());
    				int deltaY = updateNode.location.deltaY(player.getLocation());
    				movement.put_bits(5, deltaY);
    				movement.put_bits(5, deltaX);
    				encodeGeneralUpdate(updateNode, general, true);
    				update.localList.add(id);
    				update.cachedList.remove(id);
    			}
    		}
    	}
    	
    }
    The reason I believe Jagex made it this way is because I believe Jagex handled the viewport with the fixed 8 x 8 segments regions are build with. But 4 x 4 segments are only sufficient if you're right in the middle of them. Instead they used 5 x 5 segments, making it easy to grab the players without having to check a too big area and limit the use of bandwidth by ignoring anything thats further away than absolute 15 tiles.

    Question: Actually I remain unsure about the second bit. Because all it does is pretty much is shifting every element in the walking queue one index higher. What could this be useful for?
    Reply With Quote  
     


  2. #2  
    q.q


    Join Date
    Dec 2010
    Posts
    6,519
    Thanks given
    1,072
    Thanks received
    3,535
    Rep Power
    4752
    interesting find, nice one
    Reply With Quote  
     

  3. #3  
     

    Vastiko's Avatar
    Join Date
    Dec 2006
    Posts
    5,700
    Thanks given
    300
    Thanks received
    663
    Rep Power
    5000
    I bet on a horse says Harlan has no idea what you said
    Reply With Quote  
     


  4. #4  
    Renown Programmer and Respected Member
    Maxi's Avatar
    Join Date
    Jun 2008
    Posts
    3,197
    Thanks given
    281
    Thanks received
    1,095
    Rep Power
    1366
    Added a concept implementation:

    Code:
    package maximemeire.phantom.model.entity.player.update;
    
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.Map;
    import java.util.Set;
    
    import maximemeire.phantom.model.entity.MovementNode;
    import maximemeire.phantom.model.entity.player.Player;
    import maximemeire.phantom.model.entity.player.update.PlayerUpdateFlags.PlayerUpdateFlag;
    import maximemeire.phantom.network.DynamicOutboundBuffer;
    
    /**
     * @author Maxime Meire
     *
     */
    public class PlayerUpdate {
    	
    	protected PlayerUpdateNode updateNode;
    	protected Set<Integer> localList = new LinkedHashSet<Integer>();
    	protected Set<Integer> additionList = new LinkedHashSet<Integer>();
    	protected Set<Integer> removalList = new LinkedHashSet<Integer>();
    	protected Set<Integer> cachedList = new LinkedHashSet<Integer>();
    	protected Map<Integer, PlayerUpdateNode> otherUpdates = new HashMap<Integer, PlayerUpdateNode>();
    	private int movementSize = 512;
    	private int generalSize = 256;
    
    	protected static DynamicOutboundBuffer encodePayload(Player player) {
    		try {
    			PlayerUpdate update = player.getUpdate();
    			int movementSize = update.movementSize + (1 / 4) * update.movementSize;
    			DynamicOutboundBuffer payload = new DynamicOutboundBuffer(movementSize);
    			int generalSize = update.generalSize + (1 / 2) * update.generalSize;
    			DynamicOutboundBuffer masks = new DynamicOutboundBuffer(generalSize);
    			payload.start_bit_access();
    			encodeThisMovement(player, payload);
    			encodeGeneralUpdate(update.updateNode, masks, false);
    			payload.put_bits(8, update.localList.size());
    			encodeOtherMovementUpdates(player, payload, masks);
    			encodeAddFOVPlayer(player, payload, masks);
    			if (masks.readable()) {
    				payload.put_bits(11, 2047);
    				payload.end_bit_access();
    				payload.writeBytes(masks);
    			} else {
    				payload.end_bit_access();
    			}
    			return payload;
    		} catch (Exception e) {
    			e.printStackTrace();
    			return null;
    		}
    	}
    	
    	protected static void encodeThisMovement(Player player, DynamicOutboundBuffer movement) {
    		PlayerUpdate update = player.getUpdate();
    		if (update.updateNode.teleported || update.updateNode.changedRegion) {
    			movement.put_bits(1, 1);
    			movement.put_bits(2, 3);
    			movement.put_bits(2, player.getLocation().getZ());
    			movement.put_bits(1, update.updateNode.teleported ? 1 : 0);
    			movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			movement.put_bits(7, player.getClientLoadedBase().deltaY(player.getLocation()));
    			movement.put_bits(7, player.getClientLoadedBase().deltaX(player.getLocation()));
    		} else {
    			if (update.updateNode.movement.notMoving()) {
    				if (update.updateNode.requireUpdate()) {
    					movement.put_bits(1, 1);
    					movement.put_bits(2, 0);
    				} else {
    					movement.put_bits(1, 0);
    				}
    			} else if (update.updateNode.movement.walking()) {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 1);
    				movement.put_bits(3, update.updateNode.movement.getFirst());
    				movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			} else {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 2);
    				movement.put_bits(3, update.updateNode.movement.getFirst());
    				movement.put_bits(3, update.updateNode.movement.getSecond());
    				movement.put_bits(1, update.updateNode.requireUpdate() ? 1 : 0);
    			}
    		}
    	}
    	
    	protected static void encodeOtherMovementUpdates(Player player, DynamicOutboundBuffer movement, DynamicOutboundBuffer general) throws Exception {
    		PlayerUpdate update = player.getUpdate();
    		for (int id : update.localList) {
    			if (update.additionList.contains(id))
    				throw new Exception("Player is on the addition list but already on the local list");
    			if (update.removalList.contains(id)) {
    				movement.put_bits(1, 1);
    				movement.put_bits(2, 3);
    				update.localList.remove(id);
    				update.otherUpdates.remove(id);
    				update.cachedList.remove(id);
    				continue;
    			} else if (player.getLocation().absDeltaX(update.updateNode.location) > 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) > 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				movement.put_bits(1, 0);
    				update.cachedList.add(id);
    				continue;
    			} else {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				MovementNode movementNode = updateNode.movement;
    				if (movementNode.notMoving()) {
    					if (updateNode.requireUpdate()) {
    						movement.put_bits(1, 1); 
    						movement.put_bits(2, 0); 
    					} else 
    						movement.put_bits(1, 0);
    				} else if (movementNode.walking()) {
    					movement.put_bits(1, 1); 
    					movement.put_bits(2, 1); 
    					movement.put_bits(3, movementNode.getFirst());
    					movement.put_bits(1, updateNode.requireUpdate() ? 1 : 0);
    				} else {
    					movement.put_bits(1, 1);
    					movement.put_bits(2, 2);
    					movement.put_bits(3, movementNode.getFirst());
    					movement.put_bits(3, movementNode.getSecond());
    					movement.put_bits(1, updateNode.requireUpdate() ? 1 : 0);
    				}
    				if (updateNode.requireUpdate()) {
    					encodeGeneralUpdate(updateNode, general, false); 
    				}
    			}
    		}
    	}
    	
    	protected static void encodeGeneralUpdate(PlayerUpdateNode updateNode, DynamicOutboundBuffer general, boolean forceAppearance) {
    		if (!updateNode.requireUpdate() && !forceAppearance) {
    			return;
    		}
    		if (updateNode.hasCachedMaskBlock() && !forceAppearance) {
    			general.writeBytes(updateNode.cachedMaskBlock);
    			return;
    		}
    		DynamicOutboundBuffer cacheBlock = new DynamicOutboundBuffer(100);
    		int mask = updateNode.mask;
    		if (mask >= 0x100) {
    			mask |= 0x40;
    			cacheBlock.put8(mask & 0xff);
    			cacheBlock.put8(mask >> 8);
    		} else
    			cacheBlock.put8(mask);
    		if (updateNode.requireGraphicsUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.GRAPHICS_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireAnimationUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.ANIMATION_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireForcedChatUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FORCED_CHAT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireChatUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.CHAT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireFaceEntityUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FACE_ENTITY_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireAppearanceUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.APPEARANCE_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireFaceCoordinateUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.FACE_COORDINATE_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireHitUpdate()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.HIT_UPDATE_MASK.ordinal()].payload);
    		}
    		if (updateNode.requireHit2Update()) {
    			cacheBlock.writeBytes(updateNode.masks[PlayerUpdateFlag.HIT2_UPDATE_MASK.ordinal()].payload);
    		}
    		if (!forceAppearance) {
    			updateNode.cachedMaskBlock = cacheBlock;
    		}
    		general.writeBytes(cacheBlock);
    	}
    	
    	protected static void encodeAddFOVPlayer(Player player, DynamicOutboundBuffer movement, DynamicOutboundBuffer general) throws Exception {
    		PlayerUpdate update = player.getUpdate();
    		for (int id : update.additionList) {
    			if (update.localList.contains(id))
    				throw new Exception("Player is already on the local list although scheduled to be added to it");
    			if (update.localList.size() >= 255) 
    				return;
    			if (update.cachedList.contains(id))
    				continue;
    			if (player.getLocation().absDeltaX(update.updateNode.location) <= 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) <= 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				movement.put_bits(11, updateNode.index);
    				movement.put_bits(1, 1);
    				movement.put_bits(1, 1);
    				int deltaX = updateNode.location.deltaX(player.getLocation());
    				int deltaY = updateNode.location.deltaY(player.getLocation());
    				movement.put_bits(5, deltaY);
    				movement.put_bits(5, deltaX);
    				encodeGeneralUpdate(updateNode, general, true);
    				update.localList.add(id);
    				update.additionList.remove(id);
    			} else {
    				continue;
    			}
    		}
    		for (int id : update.cachedList) {
    			if (update.localList.contains(id))
    				throw new Exception("Player is already on the local list although also cached.");
    			if (update.localList.size() >= 255)
    				return;
    			if (player.getLocation().absDeltaX(update.updateNode.location) <= 15 
    					&& player.getLocation().absDeltaX(update.updateNode.location) <= 15
    					&& player.getLocation().getZ() == update.updateNode.location.getZ()) {
    				PlayerUpdateNode updateNode = update.otherUpdates.get(id);
    				movement.put_bits(11, updateNode.index);
    				movement.put_bits(1, 1); // force appearance until having the logic in place to check for it
    				movement.put_bits(1, 0);
    				int deltaX = updateNode.location.deltaX(player.getLocation());
    				int deltaY = updateNode.location.deltaY(player.getLocation());
    				movement.put_bits(5, deltaY);
    				movement.put_bits(5, deltaX);
    				encodeGeneralUpdate(updateNode, general, true);
    				update.localList.add(id);
    				update.cachedList.remove(id);
    			}
    		}
    	}
    	
    }
    Reply With Quote  
     

  5. #5  
    Renown Programmer
    Method's Avatar
    Join Date
    Feb 2009
    Posts
    1,455
    Thanks given
    0
    Thanks received
    845
    Rep Power
    3019
    To actually take advantage of this, wouldn't you need to keep informing the client about the players' movements (and probably the 'general update' stuff) when they're still nearby but not in the player's 32x32 FOV? If you just tell the client to remove them like normal, it will forget about their information.
    :-)
    Reply With Quote  
     

  6. Thankful users:


  7. #6  
    Renown Programmer and Respected Member
    Maxi's Avatar
    Join Date
    Jun 2008
    Posts
    3,197
    Thanks given
    281
    Thanks received
    1,095
    Rep Power
    1366
    Quote Originally Posted by Method View Post
    To actually take advantage of this, wouldn't you need to keep informing the client about the players' movements (and probably the 'general update' stuff) when they're still nearby but not in the player's 32x32 FOV? If you just tell the client to remove them like normal, it will forget about their information.
    I didn't look at the removal code, I should have done that. I looked at it and you're right, the client discards the player when sending the removal bits. I looked into it a bit more and came to the following conclusion:

    As long as the player is within the 5 x 5 segments and has been added to the viewport and has left the viewport, the player should not be removed and should be kept sending movement updates for. However, it should not be updating its position, so all that should be sending for updating the player movement is 1 bit being set to 0. If it enters the viewport again, the masks updating flag (first bit referenced above) is set to 0 or 1 according whether there has been an appearance change and or other masks are flagged. The second flag (second bit referenced above) should be set to 0 if it is a cached player I believe.

    I'll change my thread to display this information correctly.
    Reply With Quote  
     

  8. #7  
    q.q


    Join Date
    Dec 2010
    Posts
    6,519
    Thanks given
    1,072
    Thanks received
    3,535
    Rep Power
    4752
    Quote Originally Posted by Vastico View Post
    I bet on a horse says Harlan has no idea what you said
    looks like you lost yourself a horse don't see why you need to be a faggot..

    i obviously know what he's saying or i wouldn't of posted
    Reply With Quote  
     

  9. #8  
    Banned

    Join Date
    Apr 2012
    Age
    27
    Posts
    2,936
    Thanks given
    1,126
    Thanks received
    1,081
    Rep Power
    0
    Quote Originally Posted by Vastico View Post
    I bet on a horse says Harlan has no idea what you said
    Mad he knows more than you
    Reply With Quote  
     

  10. #9  
    Donator

    Tringan's Avatar
    Join Date
    Feb 2011
    Age
    27
    Posts
    2,101
    Thanks given
    381
    Thanks received
    334
    Rep Power
    297
    Quote Originally Posted by Vastico View Post
    I bet on a horse says Harlan has no idea what you said
    Really what is here not to understand? if you read it carefully you get there.
    Reply With Quote  
     

  11. #10  
    :doge:

    Join Date
    Jan 2009
    Posts
    3,758
    Thanks given
    221
    Thanks received
    817
    Rep Power
    2116
    I was actually recently discussing this with someone when I came across this thread.

    To clarify for people who may have a hard time understanding... If a player is within a 32 tile radius of your current position, you apply a full update as normal, but if the player is within a 40 yard radius, to save all the useless adding/removing of the players, you want to simply use this update type to update only the player's position (so that the client knows where they are and stays in sync)
    Reply With Quote  
     

Page 1 of 2 12 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. #490 Player Updating
    By Assassino in forum Show-off
    Replies: 28
    Last Post: 08-04-2011, 01:26 AM
  2. 481 Player & Npc Updating
    By Assassino in forum Show-off
    Replies: 13
    Last Post: 05-20-2011, 04:57 PM
  3. Player updating/Region updating
    By Ecstasy in forum Help
    Replies: 5
    Last Post: 04-25-2011, 06:01 PM
  4. Player Updating
    By tommo in forum Help
    Replies: 2
    Last Post: 03-22-2011, 12:01 AM
  5. player updating 613
    By Zᴀᴄʜ in forum Help
    Replies: 1
    Last Post: 10-12-2010, 01:05 PM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •