Code:
RS2 user client - release #317
Error: mopar size mismatch in getnpcpos - pos:1 psize:2
Error: T2 - 65,81,73 - 2,3222,3218 - 0,0,
I get this error in the client whenever I try and login. I tried adding npc updating to apollo and I am 99% sure all of the bits being wrote to the client are correct... Here's my classes.
Encoder:
Code:
package org.apollo.net.release.r317;
import org.apollo.game.event.impl.NPCSynchronizationEvent;
import org.apollo.game.model.Animation;
import org.apollo.game.model.Direction;
import org.apollo.game.model.Graphic;
import org.apollo.game.model.Position;
import org.apollo.game.sync.block.AnimationBlock;
import org.apollo.game.sync.block.GraphicBlock;
import org.apollo.game.sync.block.SynchronizationBlockSet;
import org.apollo.game.sync.block.TurnToPositionBlock;
import org.apollo.game.sync.seg.AddCharacterSegment;
import org.apollo.game.sync.seg.MovementSegment;
import org.apollo.game.sync.seg.SegmentType;
import org.apollo.game.sync.seg.SynchronizationSegment;
import org.apollo.net.codec.game.DataOrder;
import org.apollo.net.codec.game.DataType;
import org.apollo.net.codec.game.GamePacket;
import org.apollo.net.codec.game.GamePacketBuilder;
import org.apollo.net.meta.PacketType;
import org.apollo.net.release.EventEncoder;
/**
* NPCSynchronizationEventEncoder.java
* @author The Wanderer
*/
public class NPCSynchronizationEventEncoder extends EventEncoder<NPCSynchronizationEvent> {
@Override
public GamePacket encode(NPCSynchronizationEvent event) {
GamePacketBuilder builder = new GamePacketBuilder(65, PacketType.VARIABLE_SHORT);
builder.switchToBitAccess();
GamePacketBuilder blockBuilder = new GamePacketBuilder();
putMovementUpdate(event.getSegment(), event, builder);
putBlocks(event.getSegment(), blockBuilder);
/*
* Write the current size of the npc list.
*/
builder.putBits(8, event.getLocalNPCs());
//TODO: Handle removing npc SegmentType
for (SynchronizationSegment segment : event.getSegments()) {
SegmentType type = segment.getType();
if (type == SegmentType.ADD_CHARACTER) {
putAddCharacterUpdate((AddCharacterSegment) segment, event, builder);
putBlocks(segment, blockBuilder);
} else {
putMovementUpdate(segment, event, builder);
putBlocks(segment, blockBuilder);
}
}
/*
* Check if the update block isn't empty.
*/
if(blockBuilder.getLength() > 0) {
/*
* If so, put a flag indicating that an update block follows.
*/
builder.putBits(14, 16383);
builder.switchToByteAccess();
/*
* And append the update block.
*/
builder.putRawBuilder(blockBuilder);
} else {
/*
* Terminate the packet normally.
*/
builder.switchToByteAccess();
}
/*
* Write the packet.
*/
return builder.toGamePacket();
}
/**
* Adds a new NPC.
* @param packet The main packet.
* @param npc The npc to add.
*/
private void putAddCharacterUpdate(AddCharacterSegment seg, NPCSynchronizationEvent event, GamePacketBuilder builder) {
boolean updateRequired = seg.getBlockSet().size() > 0;
Position npc = event.getPosition();
Position other = seg.getPosition();
builder.putBits(14, seg.getIndex());
builder.putBits(5, npc.getY() - other.getY());
builder.putBits(5, npc.getX() - other.getX());
builder.putBits(1, 0); // discard walking queue?
builder.putBits(12, event.getNpcId());
builder.putBits(1, updateRequired ? 1 : 0);
}
/**
* Update an NPC's movement.
* @param packet The main packet.
* @param npc The npc.
*/
private void putMovementUpdate(SynchronizationSegment seg, NPCSynchronizationEvent event, GamePacketBuilder builder) {
boolean updateRequired = seg.getBlockSet().size() > 0;
if (seg.getType() == SegmentType.RUN) {
Direction[] directions = ((MovementSegment) seg).getDirections();
builder.putBits(1, 1);
builder.putBits(2, 2);
builder.putBits(3, directions[0].toInteger());
builder.putBits(3, directions[1].toInteger());
builder.putBits(1, updateRequired ? 1 : 0);
} else if (seg.getType() == SegmentType.WALK) {
Direction[] directions = ((MovementSegment) seg).getDirections();
builder.putBits(1, 1);
builder.putBits(2, 1);
builder.putBits(3, directions[0].toInteger());
builder.putBits(1, updateRequired ? 1 : 0);
} else {
if (updateRequired) {
builder.putBits(1, 1);
builder.putBits(2, 0);
} else {
builder.putBits(1, 0);
}
}
}
/**
* Update an NPC.
* @param packet The update block.
* @param npc The npc.
*/
private void putBlocks(SynchronizationSegment segment, GamePacketBuilder blockBuilder) {
SynchronizationBlockSet blockSet = segment.getBlockSet();
if (blockSet.size() > 0) {
int mask = 0;
//TODO: masks Hit, Hit_2, Transform, Face Entity, and Forced Chat
if (blockSet.contains(GraphicBlock.class)) {
mask |= 0x80;
}
if (blockSet.contains(AnimationBlock.class)){
mask |= 0x10;
}
if (blockSet.contains(TurnToPositionBlock.class)) {
mask |= 0x4;
}
blockBuilder.put(DataType.BYTE, mask);
if (blockSet.contains(GraphicBlock.class)) {
putGraphicBlock(blockSet.get(GraphicBlock.class), blockBuilder);
}
if (blockSet.contains(AnimationBlock.class)) {
putAnimationBlock(blockSet.get(AnimationBlock.class), blockBuilder);
}
if (blockSet.contains(TurnToPositionBlock.class)) {
putTurnToPositionBlock(blockSet.get(TurnToPositionBlock.class), blockBuilder);
}
}
}
/**
* Puts a turn to position block into the specified builder.
* @param block The block.
* @param blockBuilder The builder.
*/
private void putTurnToPositionBlock(TurnToPositionBlock block, GamePacketBuilder blockBuilder) {
Position pos = block.getPosition();
blockBuilder.put(DataType.SHORT, DataOrder.LITTLE, pos.getX() * 2 + 1);
blockBuilder.put(DataType.SHORT, DataOrder.LITTLE, pos.getY() * 2 + 1);
}
/**
* Puts a graphic block into the specified builder.
* @param block The block.
* @param blockBuilder The builder.
*/
private void putGraphicBlock(GraphicBlock block, GamePacketBuilder blockBuilder) {
Graphic graphic = block.getGraphic();
blockBuilder.put(DataType.SHORT, graphic.getId());
blockBuilder.put(DataType.INT, graphic.getDelay());
}
/**
* Puts an animation block into the specified builder.
* @param block The block.
* @param blockBuilder The builder.
*/
private void putAnimationBlock(AnimationBlock block, GamePacketBuilder blockBuilder) {
Animation animation = block.getAnimation();
blockBuilder.put(DataType.SHORT, DataOrder.LITTLE, animation.getId());
blockBuilder.put(DataType.BYTE, animation.getDelay());
}
}
Task:
Code:
package org.apollo.game.sync.task;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apollo.game.event.impl.NPCSynchronizationEvent;
import org.apollo.game.model.NPC;
import org.apollo.game.model.Player;
import org.apollo.game.model.World;
import org.apollo.game.sync.block.SynchronizationBlockSet;
import org.apollo.game.sync.seg.AddCharacterSegment;
import org.apollo.game.sync.seg.MovementSegment;
import org.apollo.game.sync.seg.RemoveCharacterSegment;
import org.apollo.game.sync.seg.SynchronizationSegment;
import org.apollo.util.CharacterRepository;
/**
* NPCSynchronizzationTask.java
* @author The Wanderer
*/
public class NPCSynchronizationTask extends SynchronizationTask {
/**
* The maximum number of players to load per cycle. This prevents the
* update packet from becoming too large (the client uses a 5000 byte
* buffer) and also stops old spec PCs from crashing when they login or
* teleport.
*/
private static final int NEW_PLAYERS_PER_CYCLE = 20;
private final NPC npc;
/**
* The player.
*/
private final Player player;
/**
* Creates the {@link NPCSynchronizationTask} for the specified player.
* @param player The player.
*/
public NPCSynchronizationTask(Player player, NPC npc) {
this.player = player;
this.npc = npc;
}
@Override
public void run() {
SynchronizationSegment segment;
SynchronizationBlockSet blockSet = npc.getBlockSet();
segment = new MovementSegment(blockSet, npc.getDirections());
List<NPC> localNPCs = player.getLocalNPCList();
int oldLocalPlayers = localNPCs.size();
List<SynchronizationSegment> segments = new ArrayList<SynchronizationSegment>();
for (Iterator<NPC> it = localNPCs.iterator(); it.hasNext(); ) {
NPC n = it.next();
if (!n.isActive() || n.isTeleporting() || n.getPosition().getLongestDelta(player.getPosition()) > player.getViewingDistance()) {
it.remove();
segments.add(new RemoveCharacterSegment());
} else {
segments.add(new MovementSegment(n.getBlockSet(), n.getDirections()));
}
}
int added = 0;
CharacterRepository<NPC> repository = World.getWorld().getNPCRepository();
for (Iterator<NPC> it = repository.iterator(); it.hasNext(); ) {
NPC n = it.next();
if (localNPCs.size() >= 255) {
player.flagExcessivePlayers();
break;
} else if (added >= NEW_PLAYERS_PER_CYCLE) {
break;
}
// we do not check n.isActive() here, since if they are active they must be in the repository
if (n != npc && n.getPosition().isWithinDistance(player.getPosition(), player.getViewingDistance()) && !localNPCs.contains(n)) {
localNPCs.add(n);
added++;
blockSet = n.getBlockSet();
segments.add(new AddCharacterSegment(blockSet, n.getIndex(), n.getPosition()));
}
}
NPCSynchronizationEvent event = new NPCSynchronizationEvent(npc.getPosition(), segment, oldLocalPlayers, segments, npc.getDefinition().getId());
player.send(event);
}
}
Event:
Code:
package org.apollo.game.event.impl;
import java.util.List;
import org.apollo.game.event.Event;
import org.apollo.game.model.Position;
import org.apollo.game.sync.seg.SynchronizationSegment;
/**
* NPCSynchronizationEvent.java
* @author The Wanderer
*/
public class NPCSynchronizationEvent extends Event {
/**
* The npc's position.
*/
private final Position position;
/**
* The current player's synchronization segment.
*/
private final SynchronizationSegment segment;
/**
* The number of local players.
*/
private final int localNPCs;
/**
* The NPC id.
*/
private final int npcId;
/**
* A list of segments.
*/
private final List<SynchronizationSegment> segments;
/**
* Creates the player synchronization event.
* @param lastKnownRegion The last known region.
* @param position The player's current position.
* @param regionChanged A flag indicating if the region has changed.
* @param segment The current player's synchronization segment.
* @param localPlayers The number of local players.
* @param segments A list of segments.
* @param npcId The NPC id.
*/
public NPCSynchronizationEvent(Position position, SynchronizationSegment segment, int localNPCs, List<SynchronizationSegment> segments, int npcId) {
this.position = position;
this.segment = segment;
this.localNPCs = localNPCs;
this.segments = segments;
this.npcId = npcId;
}
/**
* Gets the player's position.
* @return The player's position.
*/
public Position getPosition() {
return position;
}
/**
* Gets the current player's segment.
* @return The current player's segment.
*/
public SynchronizationSegment getSegment() {
return segment;
}
/**
* Gets the number of local players.
* @return The number of local players.
*/
public int getLocalNPCs() {
return localNPCs;
}
/**
* Gets the synchronization segments.
* @return The segments.
*/
public List<SynchronizationSegment> getSegments() {
return segments;
}
/**
* Gets the NPC's id.
* @return The NPC's id.
*/
public int getNpcId() {
return npcId;
}
}
Usage:
Code:
package org.apollo.game.sync;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Phaser;
import java.util.concurrent.ThreadFactory;
import org.apollo.game.GameService;
import org.apollo.game.model.NPC;
import org.apollo.game.model.Player;
import org.apollo.game.model.World;
import org.apollo.game.sync.task.NPCSynchronizationTask;
import org.apollo.game.sync.task.PhasedSynchronizationTask;
import org.apollo.game.sync.task.PlayerSynchronizationTask;
import org.apollo.game.sync.task.PostPlayerSynchronizationTask;
import org.apollo.game.sync.task.PrePlayerSynchronizationTask;
import org.apollo.game.sync.task.SynchronizationTask;
import org.apollo.util.CharacterRepository;
import org.apollo.util.NamedThreadFactory;
/**
* An implementation of {@link ClientSynchronizer} which runs in a thread pool.
* A {@link Phaser} is used to ensure that the synchronization is complete,
* allowing control to return to the {@link GameService} that started the
* synchronization. This class will scale well with machines that have multiple
* cores/processors. The {@link SequentialClientSynchronizer} will work better
* on machines with a single core/processor, however, both classes will work.
* @author Graham
*/
public final class ParallelClientSynchronizer extends ClientSynchronizer {
/**
* The executor service.
*/
private final ExecutorService executor;
/**
* The phaser.
*/
private final Phaser phaser = new Phaser(1);
/**
* Creates the parallel client synchronizer backed by a thread pool with a
* number of threads equal to the number of processing cores available
* (this is found by the {@link Runtime#availableProcessors()} method.
*/
public ParallelClientSynchronizer() {
int processors = Runtime.getRuntime().availableProcessors();
ThreadFactory factory = new NamedThreadFactory("ClientSynchronizer");
executor = Executors.newFixedThreadPool(processors, factory);
}
@Override
public void synchronize() {
CharacterRepository<Player> players = World.getWorld().getPlayerRepository();
int playerCount = players.size();
CharacterRepository<NPC> npcs = World.getWorld().getNPCRepository();
int npcCount = npcs.size();
phaser.bulkRegister(playerCount);
for (Player player : players) {
SynchronizationTask task = new PrePlayerSynchronizationTask(player);
executor.submit(new PhasedSynchronizationTask(phaser, task));
}
phaser.arriveAndAwaitAdvance();
phaser.bulkRegister(playerCount);
for (Player player : players) {
SynchronizationTask task = new PlayerSynchronizationTask(player);
executor.submit(new PhasedSynchronizationTask(phaser, task));
}
phaser.arriveAndAwaitAdvance();
phaser.bulkRegister(playerCount);
for (Player player : players) {
for(NPC npc : npcs) {
SynchronizationTask task = new NPCSynchronizationTask(player, npc);
executor.submit(new PhasedSynchronizationTask(phaser, task));
}
}
phaser.arriveAndAwaitAdvance();
phaser.bulkRegister(playerCount);
for (Player player : players) {
SynchronizationTask task = new PostPlayerSynchronizationTask(player);
executor.submit(new PhasedSynchronizationTask(phaser, task));
}
phaser.arriveAndAwaitAdvance();
}
}