Code:
package server.world.entity.player;
import java.util.Iterator;
import server.core.net.Session;
import server.core.net.packet.PacketBuffer;
import server.core.net.packet.PacketBuffer.ByteOrder;
import server.core.net.packet.PacketBuffer.ValueType;
import server.core.net.packet.PacketBuffer.WriteBuffer;
import server.core.worker.TaskFactory;
import server.util.Misc;
import server.world.World;
import server.world.entity.UpdateFlags.Flag;
import server.world.entity.player.skill.SkillManager;
import server.world.entity.player.skill.SkillManager.SkillConstant;
import server.world.map.Position;
/**
* Provides static utility methods for updating Players.
*
* @author blakeman8192
* @author lare96
*/
public final class PlayerUpdate {
/**
* Updates the player.
*
* @param player
* the player to update.
*/
public static void update(Player player) throws Exception {
PacketBuffer.WriteBuffer out = PacketBuffer.newWriteBuffer(16384);
PacketBuffer.WriteBuffer block = PacketBuffer.newWriteBuffer(8192);
/** Initialize the update packet. */
out.writeVariableShortPacketHeader(81);
out.setAccessType(PacketBuffer.AccessType.BIT_ACCESS);
/** Update this player. */
PlayerUpdate.updateLocalPlayerMovement(player, out);
if (player.getFlags().isUpdateRequired()) {
PlayerUpdate.updateState(player, player, block, false, true);
}
/** Update other local players. */
out.writeBits(8, player.getPlayers().size());
for (Iterator<Player> i = player.getPlayers().iterator(); i.hasNext();) {
Player other = i.next();
if (other.getPosition().isViewableFrom(player.getPosition()) && other.getSession().getStage() == Session.Stage.LOGGED_IN && !other.isNeedsPlacement() && other.isVisible()) {
PlayerUpdate.updateOtherPlayerMovement(other, out);
if (other.getFlags().isUpdateRequired()) {
PlayerUpdate.updateState(other, player, block, false, false);
}
} else {
out.writeBit(true);
out.writeBits(2, 3);
i.remove();
}
}
int added = 0;
/** Update the local player list. */
for (int i = 0; i < World.getPlayers().getCapacity(); i++) {
if (added == 10 || player.getPlayers().size() >= 255) {
/** Player limit has been reached. */
break;
}
Player other = World.getPlayers().get(i);
if (other == null || other == player || other.getSession().getStage() != Session.Stage.LOGGED_IN || !other.isVisible()) {
continue;
}
if (!player.getPlayers().contains(other) && other.getPosition().isViewableFrom(player.getPosition())) {
added++;
player.getPlayers().add(other);
PlayerUpdate.addPlayer(out, player, other);
PlayerUpdate.updateState(other, player, block, true, false);
}
}
/** Append the attributes block to the main packet. */
if (block.getBuffer().position() > 0) {
out.writeBits(11, 2047);
out.setAccessType(PacketBuffer.AccessType.BYTE_ACCESS);
out.writeBytes(block.getBuffer());
} else {
out.setAccessType(PacketBuffer.AccessType.BYTE_ACCESS);
}
/** Finish the packet and send it. */
out.finishVariableShortPacketHeader();
player.getSession().encode(out);
}
/**
* Appends the state of a player's chat to a buffer.
*
* @param player
* the player.
* @param out
* the buffer.
*/
public static void appendChat(Player player, PacketBuffer.WriteBuffer out) {
out.writeShort(((player.getChatColor() & 0xff) << 8) + (player.getChatEffects() & 0xff), PacketBuffer.ByteOrder.LITTLE);
out.writeByte(player.getStaffRights());
out.writeByte(player.getChatText().length, PacketBuffer.ValueType.C);
out.writeBytesReverse(player.getChatText());
}
/**
* Appends the state of a player's appearance to a buffer.
*
* @param player
* the player.
* @param out
* the buffer.
*/
public static void appendAppearance(Player player, PacketBuffer.WriteBuffer out) {
PacketBuffer.WriteBuffer block = PacketBuffer.newWriteBuffer(128);
/** Gender. */
block.writeByte(player.getGender()); // Gender
/** Head icon. */
block.writeByte(player.getHeadIcon());
/** Skull icon. */
block.writeByte(player.getSkullIcon());
if (player.getNpcAppearanceId() == -1) {
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HEAD) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HEAD));
} else {
block.writeByte(0);
}
/** Cape. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_CAPE) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_CAPE));
} else {
block.writeByte(0);
}
/** Amulet. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_AMULET) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_AMULET));
} else {
block.writeByte(0);
}
/** Weapon. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_WEAPON) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_WEAPON));
} else {
block.writeByte(0);
}
/** Chest. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_CHEST) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_CHEST));
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_CHEST]);
}
/** Shield. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_SHIELD) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_SHIELD));
} else {
block.writeByte(0);
}
/** Arms. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_CHEST) > 1) {
if (!player.getEquipment().getContainer().getItem(Misc.EQUIPMENT_SLOT_CHEST).getDefinition().isFullBody()) {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_ARMS]);
} else {
block.writeByte(0);
}
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_ARMS]);
}
/** Legs. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_LEGS) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_LEGS));
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_LEGS]);
}
/** Head. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HEAD) > 1 && player.getEquipment().getContainer().getItem(Misc.EQUIPMENT_SLOT_HEAD).getDefinition().isFullMask()) {
block.writeByte(0);
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_HEAD]);
}
/** Hands. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HANDS) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HANDS));
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_HANDS]);
}
/** Feet. */
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_FEET) > 1) {
block.writeShort(0x200 + player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_FEET));
} else {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_FEET]);
}
/** Beard. */
if (player.getGender() == Misc.GENDER_MALE) {
if (player.getEquipment().getContainer().getItemId(Misc.EQUIPMENT_SLOT_HEAD) > 1 && !player.getEquipment().getContainer().getItem(Misc.EQUIPMENT_SLOT_HEAD).getDefinition().isFullMask() || player.getEquipment().getContainer().isSlotFree(Misc.EQUIPMENT_SLOT_HEAD)) {
block.writeShort(0x100 + player.getAppearance()[Misc.APPEARANCE_SLOT_BEARD]);
} else {
block.writeByte(0);
}
}
} else {
block.writeShort(-1);
block.writeShort(player.getNpcAppearanceId());
}
/** Player colors */
block.writeByte(player.getColors()[0]);
block.writeByte(player.getColors()[1]);
block.writeByte(player.getColors()[2]);
block.writeByte(player.getColors()[3]);
block.writeByte(player.getColors()[4]);
/** Movement animations */
block.writeShort(player.getUpdateAnimation().getStandingAnimation() == -1 ? PlayerAnimation.getStandEmote() : player.getUpdateAnimation().getStandingAnimation()); // stand
block.writeShort(PlayerAnimation.getStandTurnEmote()); // stand turn
block.writeShort(player.getUpdateAnimation().getWalkingAnimation() == -1 ? PlayerAnimation.getWalkEmote() : player.getUpdateAnimation().getWalkingAnimation()); // walk
block.writeShort(PlayerAnimation.getTurn180Emote()); // turn 180
block.writeShort(PlayerAnimation.getTurn90CWEmote()); // turn 90 cw
block.writeShort(PlayerAnimation.getTurn90CCWEmote()); // turn 90 ccw
block.writeShort(player.getUpdateAnimation().getRunningAnimation() == -1 ? PlayerAnimation.getRunEmote() : player.getUpdateAnimation().getRunningAnimation()); // run
/** Player context menus */
block.writeLong(player.getUsernameHash());
block.writeByte(player.getCombatLevel());
block.writeShort(0);
/** Append the block length and the block to the packet. */
out.writeByte(block.getBuffer().position(), PacketBuffer.ValueType.C);
out.writeBytes(block.getBuffer());
}
/**
* Adds a player to the local player list of another player.
*
* @param out
* the packet to write to.
* @param player
* the host player.
* @param other
* the player being added.
*/
public static void addPlayer(PacketBuffer.WriteBuffer out, Player player, Player other) {
out.writeBits(11, other.getSlot()); // Server slot.
out.writeBit(true); // Yes, an update is required.
out.writeBit(true); // Discard walking queue(?)
// Write the relative position.
Position delta = Misc.delta(player.getPosition(), other.getPosition());
out.writeBits(5, delta.getY());
out.writeBits(5, delta.getX());
}
/**
* Updates movement for this local player. The difference between this
* method and the other player method is that this will make use of sector
* 2,3 to place the player in a specific position while sector 2,3 is not
* present in updating of other players (it simply flags local list removal
* instead).
*
* @param player
* the player to update movement for.
* @param out
* the packet to write to.
*/
public static void updateLocalPlayerMovement(Player player, PacketBuffer.WriteBuffer out) {
boolean updateRequired = player.getFlags().isUpdateRequired();
if (player.isNeedsPlacement()) { // Do they need placement?
out.writeBit(true); // Yes, there is an update.
int posX = player.getPosition().getLocalX(player.getCurrentRegion());
int posY = player.getPosition().getLocalY(player.getCurrentRegion());
appendPlacement(out, posX, posY, player.getPosition().getZ(), player.isResetMovementQueue(), updateRequired);
// player.setNeedsPlacement(false);
} else { // No placement update, check for movement.
int pDir = player.getPrimaryDirection();
int sDir = player.getSecondaryDirection();
if (pDir != -1) { // If they moved.
out.writeBit(true); // Yes, there is an update.
if (sDir != -1) { // If they ran.
appendRun(out, pDir, sDir, updateRequired);
} else { // Movement but no running - they walked.
appendWalk(out, pDir, updateRequired);
}
} else { // No movement.
if (updateRequired) { // Does the state need to be updated?
out.writeBit(true); // Yes, there is an update.
appendStand(out);
} else { // No update whatsoever.
out.writeBit(false);
}
}
}
}
/**
* Updates the movement of a player for another player (does not make use of
* sector 2,3).
*
* @param player
* the player to update movement for.
* @param out
* the packet to write to.
*/
public static void updateOtherPlayerMovement(Player player, PacketBuffer.WriteBuffer out) {
boolean updateRequired = player.getFlags().isUpdateRequired();
int pDir = player.getPrimaryDirection();
int sDir = player.getSecondaryDirection();
if (pDir != -1) { // If they moved.
out.writeBit(true); // Yes, there is an update.
if (sDir != -1) { // If they ran.
appendRun(out, pDir, sDir, updateRequired);
} else { // Movement but no running - they walked.
appendWalk(out, pDir, updateRequired);
}
} else { // No movement.
if (updateRequired) { // Does the state need to be updated?
out.writeBit(true); // Yes, there is an update.
appendStand(out);
} else { // No update whatsoever.
out.writeBit(false);
}
}
}
/**
* Updates the state of a player.
*
* @param player
* the player being constructed.
* @param thisPlayer
* the player being constructed for.
* @param block
* the update block.
*/
public static void updateState(Player player, Player thisPlayer, PacketBuffer.WriteBuffer block, boolean forceAppearance, boolean noChat) throws Exception {
/** Block if no update is required. */
if (!player.getFlags().isUpdateRequired() && !forceAppearance) {
return;
}
/** Send the cached update block if we are able to. */
if (player.getCachedUpdateBlock() != null && player != thisPlayer && !forceAppearance && !noChat) {
block.getBuffer().put(player.getCachedUpdateBlock().array());
return;
}
/** Create the buffer we are going to cache. */
WriteBuffer cachedBuffer = PacketBuffer.newWriteBuffer();
/** First we build the update mask. */
int mask = 0x0;
if (player.getFlags().get(Flag.GRAPHICS)) {
mask |= 0x100;
}
if (player.getFlags().get(Flag.ANIMATION)) {
mask |= 8;
}
if (player.getFlags().get(Flag.FORCED_CHAT)) {
mask |= 4;
}
if (player.getFlags().get(Flag.CHAT) && !noChat) {
mask |= 0x80;
}
if (player.getFlags().get(Flag.APPEARANCE) || forceAppearance) {
mask |= 0x10;
}
if (player.getFlags().get(Flag.FACE_ENTITY)) {
mask |= 1;
}
if (player.getFlags().get(Flag.FACE_COORDINATE)) {
mask |= 2;
}
if (player.getFlags().get(Flag.HIT)) {
mask |= 0x20;
}
if (player.getFlags().get(Flag.HIT_2)) {
mask |= 0x200;
}
/** Then we write the built mask. */
if (mask >= 0x100) {
mask |= 0x40;
cachedBuffer.writeShort(mask, PacketBuffer.ByteOrder.LITTLE);
} else {
cachedBuffer.writeByte(mask);
}
/** Then we add the attribute data to the block. */
// Graphics
if (player.getFlags().get(Flag.GRAPHICS)) {
appendGfx(player, cachedBuffer);
}
// Animation
if (player.getFlags().get(Flag.ANIMATION)) {
appendAnimation(player, cachedBuffer);
}
// Forced chat
if (player.getFlags().get(Flag.FORCED_CHAT)) {
appendForcedChat(player, cachedBuffer);
}
// Regular chat
if (player.getFlags().get(Flag.CHAT) && !noChat) {
appendChat(player, cachedBuffer);
}
// Face entity
if (player.getFlags().get(Flag.FACE_ENTITY)) {
appendFaceEntity(player, cachedBuffer);
}
// Appearance
if (player.getFlags().get(Flag.APPEARANCE) || forceAppearance) {
appendAppearance(player, cachedBuffer);
}
// Face coordinates
if (player.getFlags().get(Flag.FACE_COORDINATE)) {
appendFaceCoordinate(player, cachedBuffer);
}
// Primary hit
if (player.getFlags().get(Flag.HIT)) {
appendPrimaryHit(player, cachedBuffer);
}
// Secondary hit
if (player.getFlags().get(Flag.HIT_2)) {
appendSecondaryHit(player, cachedBuffer);
}
/** Cache the block if possible. */
if (player != thisPlayer && !forceAppearance && !noChat) {
player.setCachedUpdateBlock(cachedBuffer.getBuffer());
}
/** Add the cached block to the update block. */
block.writeBytes(cachedBuffer.getBuffer());
}
/**
* Update the forced chat block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendForcedChat(Player player, PacketBuffer.WriteBuffer out) {
out.writeString(player.getForcedText());
}
/**
* Update the face entity block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendFaceEntity(Player player, PacketBuffer.WriteBuffer out) {
out.writeShort(player.getFaceIndex(), ByteOrder.LITTLE);
}
/**
* Update the face coordinate block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendFaceCoordinate(Player player, PacketBuffer.WriteBuffer out) {
out.writeShort(player.getFaceCoordinates().getX(), ValueType.A, ByteOrder.LITTLE);
out.writeShort(player.getFaceCoordinates().getY(), ByteOrder.LITTLE);
}
/**
* Update the animation block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendAnimation(Player player, PacketBuffer.WriteBuffer out) {
out.writeShort(player.getAnimation().getId(), ByteOrder.LITTLE);
out.writeByte(player.getAnimation().getDelay(), ValueType.C);
}
/**
* Update the primary hitmark block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendPrimaryHit(Player player, PacketBuffer.WriteBuffer out) throws Exception {
out.writeByte(player.getPrimaryHit().getDamage());
out.writeByte(player.getPrimaryHit().getType().getId(), ValueType.A);
if (!player.isHasDied()) {
if (player.getSkills()[Misc.HITPOINTS].getLevel() <= 0) {
player.getSkills()[Misc.HITPOINTS].setLevel(0);
player.setHasDied(true);
TaskFactory.getFactory().submit(player.death());
}
}
out.writeByte(player.getSkills()[Misc.HITPOINTS].getLevel(), ValueType.C);
out.writeByte(player.getSkills()[Misc.HITPOINTS].getLevelForExperience());
}
/**
* Update the secondary hitmark block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendSecondaryHit(Player player, PacketBuffer.WriteBuffer out) throws Exception {
out.writeByte(player.getSecondaryHit().getDamage());
out.writeByte(player.getSecondaryHit().getType().getId(), ValueType.S);
if (!player.isHasDied()) {
if (player.getSkills()[Misc.HITPOINTS].getLevel() <= 0) {
player.getSkills()[Misc.HITPOINTS].setLevel(0);
player.setHasDied(true);
TaskFactory.getFactory().submit(player.death());
}
}
out.writeByte(player.getSkills()[Misc.HITPOINTS].getLevel());
out.writeByte(player.getSkills()[Misc.HITPOINTS].getLevelForExperience(), ValueType.C);
SkillManager.refresh(player, SkillConstant.HITPOINTS);
}
/**
* Update the graphics block.
*
* @param player
* the player to update for.
* @param out
* the packet to write to.
*/
private static void appendGfx(Player player, PacketBuffer.WriteBuffer out) {
out.writeShort(player.getGfx().getId(), ByteOrder.LITTLE);
out.writeInt(player.getGfx().getDelay());
}
/**
* Appends the stand version of the movement section of the update packet
* (sector 2,0). Appending this (instead of just a zero bit) automatically
* assumes that there is a required attribute update afterwards.
*
* @param out
* the buffer to append to.
*/
public static void appendStand(PacketBuffer.WriteBuffer out) {
out.writeBits(2, 0); // 0 - no movement.
}
/**
* Appends the walk version of the movement section of the update packet
* (sector 2,1).
*
* @param out
* the buffer to append to
* @param direction
* the walking direction
* @param attributesUpdate
* whether or not a player attributes update is required
*/
public static void appendWalk(PacketBuffer.WriteBuffer out, int direction, boolean attributesUpdate) {
out.writeBits(2, 1); // 1 - walking.
/** Append the actual sector. */
out.writeBits(3, direction);
out.writeBit(attributesUpdate);
}
/**
* Appends the walk version of the movement section of the update packet
* (sector 2,2).
*
* @param out
* the buffer to append to.
* @param direction
* the walking direction.
* @param direction2
* the running direction.
* @param attributesUpdate
* whether or not a player attributes update is required.
*/
public static void appendRun(PacketBuffer.WriteBuffer out, int direction, int direction2, boolean attributesUpdate) {
out.writeBits(2, 2); // 2 - running.
/** Append the actual sector. */
out.writeBits(3, direction);
out.writeBits(3, direction2);
out.writeBit(attributesUpdate);
}
/**
* Appends the player placement version of the movement section of the
* update packet (sector 2,3). Note that by others this was previously
* called the "teleport update".
*
* @param out
* the buffer to append to.
* @param localX
* the local X coordinate.
* @param localY
* the local Y coordinate.
* @param z
* the Z coordinate.
* @param discardMovementQueue
* whether or not the client should discard the movement queue.
* @param attributesUpdate
* whether or not a plater attributes update is required.
*/
public static void appendPlacement(PacketBuffer.WriteBuffer out, int localX, int localY, int z, boolean discardMovementQueue, boolean attributesUpdate) {
out.writeBits(2, 3); // 3 - placement.
/** Append the actual sector. */
out.writeBits(2, z);
out.writeBit(discardMovementQueue);
out.writeBit(attributesUpdate);
out.writeBits(7, localY);
out.writeBits(7, localX);
}
}