Thread: NPC Updating Issue

Results 1 to 5 of 5
  1. #1 NPC Updating Issue 
    Ex Rune-Scaper

    Join Date
    Jun 2008
    Posts
    3,534
    Thanks given
    457
    Thanks received
    1,257
    Rep Power
    990
    Been working on npc updating for the past few days and I'm stuck on an issue I have with npc updating. So maybe one of you know more about this then I do.

    This occurs upon login, if I have any npcs spawned in the game world.

    Client is telling me this.
    Code:
    Error: test size mismatch in getnpcpos - pos:13 psize:11
    It's not npc bits, because npc bits throw a "too many npc" error and I made sure both matched.

    The client is also telling me this.

    Code:
    Error: T2 - 65,81,73 - 11,3093,3498 - 0,0,7,-38,-110,-68,0,47,102,26,-80,
    Opcode 65 is NpcUpdating which I figure to be the problem. 81 is playing updating, and 73 is regional updating.

    I'm assuming it has to do with having too much or too little bytes being sent.

    First number is the position in the buffer, and second is the packet size.
    Code:
    pos:13 psize:11
    Here is my npc updating packet

    Code:
    package com.astraeus.core.net.channel.packet.outgoing;
    
    import java.nio.ByteBuffer;
    import java.util.Iterator;
    
    import com.astraeus.core.Server;
    import com.astraeus.core.game.model.entity.UpdateFlags.UpdateFlag;
    import com.astraeus.core.game.model.entity.mobile.npc.Npc;
    import com.astraeus.core.game.model.entity.mobile.npc.update.UpdateBlock;
    import com.astraeus.core.game.model.entity.mobile.npc.update.impl.NpcMovementBlock;
    import com.astraeus.core.game.model.entity.mobile.npc.update.impl.StatefulUpdateBlock;
    import com.astraeus.core.game.model.entity.mobile.player.Player;
    import com.astraeus.core.net.channel.packet.OutgoingPacket;
    import com.astraeus.core.net.channel.packet.PacketBuilder;
    import com.astraeus.core.net.channel.packet.PacketHeader;
    import com.astraeus.core.net.channel.protocol.codec.game.ByteAccess;
    
    /**
     * The {@link OutgoingPacket} that updates a non-player character.
     * 
     * @author SeVen
     */
    public class NpcUpdatePacket extends OutgoingPacket {
    
    	/**
    	 * Creates a new {@link NpcUpdatePacket}.
    	 */
    	public NpcUpdatePacket() {
    		super(65, PacketHeader.VARIABLE_SHORT, 16384);
    	}
    	
    	/**
    	 * Appends an update to the main update block.
    	 * 
    	 * @param block
    	 * 		The block to append.
    	 * 
    	 * @param builder
    	 * 		The builder to create a buffer.
    	 * 
    	 * @param npc
    	 * 		The npc to update.
    	 */
    	public final void append(UpdateBlock block, PacketBuilder builder, Npc npc) {
    		block.update(npc, builder);
    	}
    
    	@Override
    	public PacketBuilder dispatch(Player player) {
    		PacketBuilder update = new PacketBuilder(ByteBuffer.allocate(8192));
    		
    		player.getContext().prepare(this, builder);	
    		builder.setAccessType(ByteAccess.BIT_ACCESS);
    
    		builder.putBits(8, player.getLocalNpcs().size());
    	
    		for(Iterator<Npc> iterator = player.getLocalNpcs().iterator(); iterator.hasNext();) {
    			Npc npc = iterator.next();
    			
    			if(npc.getPosition().isWithinDistance(player.getPosition(), 15) && Server.getUpdateProcessor().getNpcs().containsKey(npc.getIndex())) {
    				append(new NpcMovementBlock(), builder, npc);				
    				if (npc.getUpdateFlags().isUpdateRequired()) {
    					append(new StatefulUpdateBlock(), update, npc);
    				}
    			} else {
    				iterator.remove();
    				builder.putBits(1,  1);
    				builder.putBits(2, 3);
    			}
    		}
    		
    		for(Npc npc : Server.getUpdateProcessor().getNpcs().values()) {
    			if (player.getLocalNpcs().contains(npc)) {
    				continue;
    			}
    			if (npc.getPosition().isWithinDistance(player.getPosition(), 15)) {
    				player.getLocalNpcs().add(npc);
    				npc.getUpdateFlags().flag(UpdateFlag.REGISTERED_LOCALLY);
    				builder.putBits(14,  npc.getIndex()); // The slot in an array
    				builder.putBits(5,  npc.getPosition().getY() - player.getPosition().getY());
    				builder.putBits(5, npc.getPosition().getX() - player.getPosition().getX());
    				builder.putBits(1, 1); // walking queue
    				builder.putBits(12, npc.getIdentityIndex()); //The real id of the npc
    				builder.putBits(1, 1);
    				if(npc.getUpdateFlags().isUpdateRequired()) {
    					append(new StatefulUpdateBlock(), update, npc);
    				}
    			}
    		}
    		if(update.getPosition() > 0) {
    			builder.putBits(14, 16383);
    			builder.setAccessType(ByteAccess.BYTE_ACCESS);
    			System.out.println("Update Length: " + update.getLength() + " Pos: " +  update.getPosition());
    			builder.putBytes(update.getBuffer());
    		} else {
    			builder.setAccessType(ByteAccess.BYTE_ACCESS);
    		}
    		builder.endVariableShortPacketHeader();		
    		return builder;
    	}
    
    
    }
    The updating procedure I follow 317 Protocol
    Code:
    	@Override
    	public void execute() {
    		synchronized (this) {
    			//The update procedure.
    			
    			//Our player movement, and other player movement.
    			for (final Player player : getPlayers().values()) {
    				player.prepare();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.prepare();
    			}
    
    			// Player list & Player flag based update
    			for (final Player player : players.values()) {
    				player.getEventListener().update(player); // also sends the npc update packet
    			}
    
    			for (final Player player : players.values()) {
    				player.getUpdateFlags().clear();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.getUpdateFlags().clear();
    			}
    		}
    	}
    Here's the main block for flag based updates for an npc.

    Code:
    public class StatefulUpdateBlock extends UpdateBlock {
    
    	@Override
    	public void update(Npc npc, PacketBuilder builder) {
    		int mask = 0x4;
    		
    		if (npc.getUpdateFlags().get(UpdateFlag.FACE_COORDINATE)) {
    			mask |= 0x4;
    		}
    		
    		builder.putByte(mask);
    		
    		if (npc.getUpdateFlags().get(UpdateFlag.FACE_COORDINATE)) {
    			final Position position = (Position) npc.getAttributes().get(Attributes.FACE_COORDINATE);
    			builder.putShort(position.getX() * 2 + 1, ByteOrder.LITTLE);
    			builder.putShort(position.getY() * 2 + 1, ByteOrder.LITTLE);
    		}		
    	}
    
    }
    And then the npc movement block which gets appended to the main update block

    Code:
    public class NpcMovementBlock extends UpdateBlock {
    
    	@Override
    	public void update(Npc npc, PacketBuilder builder) {
    		if (npc.getWalkingDirection() == -1) {	
    			if(npc.getUpdateFlags().isUpdateRequired()) {
    				builder.putBits(1, 1);
    				builder.putBits(2, 0);				
    			} else {				
    				builder.putBits(1, 0);
    			}			
    		} else {
    			builder.putBits(1, 1);
    			builder.putBits(2, 1);
    			builder.putBits(3, npc.getWalkingDirection());
    			builder.putBits(1, 1);
    		}
    	}
    
    }
    UPDATE

    I think it has to do with this.

    When an npc is registered its initially flagged so the update block appends an update that adds 1 byte.

    Code:
    			if (npc.getPosition().isWithinDistance(player.getPosition(), 15)) {
    				player.getLocalNpcs().add(npc);
    				npc.getUpdateFlags().flag(UpdateFlag.REGISTERED_LOCALLY);
    				builder.putBits(14,  npc.getIndex()); // The slot in an array
    				builder.putBits(5,  npc.getPosition().getY() - player.getPosition().getY());
    				builder.putBits(5, npc.getPosition().getX() - player.getPosition().getX());
    				builder.putBits(1, 1); // walking queue
    				builder.putBits(12, npc.getIdentityIndex()); //The real id of the npc
    				builder.putBits(1, 1);
    				System.out.println(npc.getUpdateFlags().isUpdateRequired());
    				if(npc.getUpdateFlags().isUpdateRequired()) {
    					append(new StatefulUpdateBlock(), update, npc);
    				}
    			}
    The position of the buffer should be 1 at this point but it's 0 so this isn't being called.

    Code:
    		if(update.getPosition() > 0) {
    			builder.putBits(14, 16383);
    			builder.setAccessType(ByteAccess.BYTE_ACCESS);
    			builder.putBytes(update.getBuffer());
    		}
    Attached image
    Reply With Quote  
     

  2. #2  


    Major's Avatar
    Join Date
    Jan 2011
    Posts
    2,997
    Thanks given
    1,293
    Thanks received
    3,556
    Rep Power
    5000
    Quote Originally Posted by SeVen View Post

    Code:
    public class StatefulUpdateBlock extends UpdateBlock {
    
    	@Override
    	public void update(Npc npc, PacketBuilder builder) {
    		int mask = 0x4;
    		
    		if (npc.getUpdateFlags().get(UpdateFlag.FACE_COORDINATE)) {
    			mask |= 0x4;
    		}
    		
    		builder.putByte(mask);
    		
    		if (npc.getUpdateFlags().get(UpdateFlag.FACE_COORDINATE)) {
    			final Position position = (Position) npc.getAttributes().get(Attributes.FACE_COORDINATE);
    			builder.putShort(position.getX() * 2 + 1, ByteOrder.LITTLE);
    			builder.putShort(position.getY() * 2 + 1, ByteOrder.LITTLE);
    		}		
    	}
    
    }[/cpde]
    Mask should have an initial value of 0, not 4. Also I have no idea why this is an UpdateBlock as it's doing the initialisation as well.

    You also keep calling the same methods on classes, instead of storing the returned value. Use local variables, they exist for a reason:

    Code:
    for (final Player player : getPlayers().values()) {
    				player.prepare();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.prepare();
    			}
    
    			// Player list & Player flag based update
    			for (final Player player : players.values()) {
    				player.getEventListener().update(player); // also sends the npc update packet
    			}
    
    			for (final Player player : players.values()) {
    				player.getUpdateFlags().clear();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.getUpdateFlags().clear();
    			}
    I'm not really sure why you're storing them in a Map anyway
    Reply With Quote  
     

  3. Thankful user:


  4. #3  
    Respected Member


    George's Avatar
    Join Date
    Mar 2009
    Posts
    7,099
    Thanks given
    2,226
    Thanks received
    3,146
    Rep Power
    5000
    Code:
    builder.putBits(12, npc.getIdentityIndex()); //The real id of the npc
    Code:
    builder.putBits(14, npc.getIdentityIndex()); //The real id of the npc
    Attached image

    Spoiler for Spoilers!:
    Attached image
    Attached image
    Attached image
    Attached image
    Reply With Quote  
     

  5. #4  
    Ex Rune-Scaper

    Join Date
    Jun 2008
    Posts
    3,534
    Thanks given
    457
    Thanks received
    1,257
    Rep Power
    990
    Quote Originally Posted by Major View Post
    Mask should have an initial value of 0, not 4. Also I have no idea why this is an UpdateBlock as it's doing the initialisation as well.

    You also keep calling the same methods on classes, instead of storing the returned value. Use local variables, they exist for a reason:

    Code:
    for (final Player player : getPlayers().values()) {
    				player.prepare();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.prepare();
    			}
    
    			// Player list & Player flag based update
    			for (final Player player : players.values()) {
    				player.getEventListener().update(player); // also sends the npc update packet
    			}
    
    			for (final Player player : players.values()) {
    				player.getUpdateFlags().clear();
    			}
    			
    			for(final Npc npc : getNpcs().values()) {
    				npc.getUpdateFlags().clear();
    			}
    I'm not really sure why you're storing them in a Map anyway
    Yeah I didn't realize the npc mask wasn't set to 0. My bad.

    That does solve that position, but then gives

    Code:
    size mismatch in getnpcpos - pos:36 psize:6
    I'm using a concurrent hashmap for updating players and npcs. What would be a better alternative?

    Quote Originally Posted by Deadpool View Post
    Code:
    builder.putBits(12, npc.getIdentityIndex()); //The real id of the npc
    Code:
    builder.putBits(14, npc.getIdentityIndex()); //The real id of the npc
    That's the npc's bits, 14 is too high. It will throw the npc list off.

    Code:
    	private final void updateNpcList(Buffer buffer, int packetSize) {
    		while (buffer.getBitPosition() + 21 < packetSize * 8) {
    			int index = buffer.readBits(14);
    			if (index == 16383) {
    				break;
    			}
    
    			if (npcs[index] == null) {
    				npcs[index] = new Npc();
    			}
    
    			Npc npc = npcs[index];
    			npcList[npcCount++] = index;
    			npc.time = tick;
    
    			int y = buffer.readBits(5);
    			if (y > 15) {
    				y -= 32;
    			}
    
    			int x = buffer.readBits(5);
    			if (x > 15) {
    				x -= 32;
    			}
    
    			int teleport = buffer.readBits(1);
    			npc.setDefinition(NpcDefinition.lookup(buffer.readBits(12))); //npc bits right here set to 12
    			int updateRequired = buffer.readBits(1);
    			if (updateRequired == 1) {
    				mobsAwaitingUpdate[mobsAwaitingUpdateCount++] = index;
    			}
    
    			npc.size = npc.getDefinition().getSize();
    			npc.rotation = npc.getDefinition().getRotation();
    			npc.walkingAnimation = npc.getDefinition().getWalkingAnimation();
    			npc.halfTurnAnimation = npc.getDefinition().getHalfTurnAnimation();
    			npc.quarterClockwiseTurnAnimation = npc.getDefinition().getRotateClockwiseAnimation();
    			npc.quarterAnticlockwiseTurnAnimation = npc.getDefinition().getRotateAntiClockwiseAnimation();
    			npc.idleAnimation = npc.getDefinition().getIdleAnimation();
    			npc.move(localPlayer.pathX[0] + x, localPlayer.pathY[0] + y, teleport == 1);
    		}
    		buffer.disableBitAccess();
    	}
    In Major's 317 Refactored Client the bits is set to 12, so the server has to be 12 as well.
    Attached image
    Reply With Quote  
     

  6. #5  


    Major's Avatar
    Join Date
    Jan 2011
    Posts
    2,997
    Thanks given
    1,293
    Thanks received
    3,556
    Rep Power
    5000
    In the 317 protocol it should be 12 bits. Project Insanity servers use 14 because a bunch of custom/later revision NPCs were added and they went over the id limit.
    Reply With Quote  
     


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. Fixed NPC Updates.
    By Shiver in forum Tutorials
    Replies: 163
    Last Post: 11-19-2009, 04:24 AM
  2. Fixed NPC updating
    By Shiver in forum RS 503+ Client & Server
    Replies: 24
    Last Post: 09-13-2008, 10:10 PM
  3. NPC update masks fixed
    By Palidino in forum RS 503+ Client & Server
    Replies: 110
    Last Post: 09-07-2008, 07:47 PM
  4. ALL NPC Update Masks
    By Sean in forum RS 503+ Client & Server
    Replies: 16
    Last Post: 08-07-2008, 12:25 AM
  5. Npc Update Masks
    By Evolution X in forum RS2 Client
    Replies: 8
    Last Post: 04-09-2008, 12:27 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •