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.
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());
}