From 1d05097869a7fc8eca52c0b69fe20f36674c53d0 Mon Sep 17 00:00:00 2001 From: Major- Date: Sun, 3 Nov 2013 04:49:00 +0000 Subject: [PATCH] Add npc synchronization and spawning plugin. --- data/plugins/cmd-npc/plugin.xml | 14 + data/plugins/cmd-npc/spawn.rb | 70 +++ .../event/impl/NpcSynchronizationEvent.java | 72 +++ src/org/apollo/game/model/Character.java | 470 +++++++------- src/org/apollo/game/model/Player.java | 573 +++++++++--------- src/org/apollo/game/model/World.java | 197 +++--- src/org/apollo/game/model/WorldConstants.java | 8 +- .../game/sync/ParallelClientSynchronizer.java | 32 +- .../sync/SequentialClientSynchronizer.java | 22 +- .../game/sync/block/TransformBlock.java | 34 ++ .../apollo/game/sync/seg/AddNpcSegment.java | 75 +++ ...cterSegment.java => AddPlayerSegment.java} | 4 +- .../sync/task/NpcSynchronizationTask.java | 88 +++ .../sync/task/PlayerSynchronizationTask.java | 4 +- .../sync/task/PostNpcSynchronizationTask.java | 32 + .../sync/task/PreNpcSynchronizationTask.java | 31 + .../r317/NpcSynchronizationEventEncoder.java | 302 +++++++++ .../PlayerSynchronizationEventEncoder.java | 6 +- .../apollo/net/release/r317/Release317.java | 2 + .../r377/NpcSynchronizationEventEncoder.java | 301 +++++++++ .../PlayerSynchronizationEventEncoder.java | 6 +- .../apollo/net/release/r377/Release377.java | 2 + 22 files changed, 1749 insertions(+), 596 deletions(-) create mode 100644 data/plugins/cmd-npc/plugin.xml create mode 100644 data/plugins/cmd-npc/spawn.rb create mode 100644 src/org/apollo/game/event/impl/NpcSynchronizationEvent.java create mode 100644 src/org/apollo/game/sync/block/TransformBlock.java create mode 100644 src/org/apollo/game/sync/seg/AddNpcSegment.java rename src/org/apollo/game/sync/seg/{AddCharacterSegment.java => AddPlayerSegment.java} (84%) create mode 100644 src/org/apollo/game/sync/task/NpcSynchronizationTask.java create mode 100644 src/org/apollo/game/sync/task/PostNpcSynchronizationTask.java create mode 100644 src/org/apollo/game/sync/task/PreNpcSynchronizationTask.java create mode 100644 src/org/apollo/net/release/r317/NpcSynchronizationEventEncoder.java create mode 100644 src/org/apollo/net/release/r377/NpcSynchronizationEventEncoder.java diff --git a/data/plugins/cmd-npc/plugin.xml b/data/plugins/cmd-npc/plugin.xml new file mode 100644 index 00000000..4cca1a6e --- /dev/null +++ b/data/plugins/cmd-npc/plugin.xml @@ -0,0 +1,14 @@ + + + cmd-npc + 1 + Npc Commands + Adds npc-related commands. + + Major + + + + + + \ No newline at end of file diff --git a/data/plugins/cmd-npc/spawn.rb b/data/plugins/cmd-npc/spawn.rb new file mode 100644 index 00000000..c317d43b --- /dev/null +++ b/data/plugins/cmd-npc/spawn.rb @@ -0,0 +1,70 @@ +require 'java' + +java_import 'org.apollo.game.model.def.NpcDefinition' +java_import 'org.apollo.game.model.Npc' +java_import 'org.apollo.game.model.World' +java_import 'org.apollo.game.model.Position' + +blacklist = [] + +on :command, :spawn, RIGHTS_ADMIN do |player, command| + args = command.arguments + id = args[0].to_i + unless [1, 3].include? args.length and id > -1 + player.send_message("Invalid syntax - ::spawn [npc id] [x] [y]") + return + end + + if blacklist.include? id + player.send_message("Sorry, that npc is blacklisted!") + return + end + + definition = NpcDefinition.lookup(id) + position = args.length == 1 ? player.position : Position.new(args[1].to_i, args[2].to_i, player.position.height) + + World.world.register(Npc.new(definition, position)) +end + + +on :command, :mass, RIGHTS_ADMIN do |player, command| + args = command.arguments + unless args.length == 2 + player.send_message("Invalid syntax - ::spawn [npc id] [range (1-5)]") + return + end + + id = args[0].to_i + range = args[1].to_i + + unless (id > -1 and (1..5).include? range) + return player.send_message("Invalid syntax - ::spawn [npc id] [range (1-5)]") + end + + if blacklist.include? id + player.send_message("Sorry, that npc is blacklisted!") + return + end + + center_position = player.position + + minX = center_position.x - range + minY = center_position.y - range + maxX = center_position.x + range + maxY = center_position.y + range + + for x in minX..maxX do + for y in minY..maxY do + World.world.register(Npc.new(NpcDefinition.lookup(id), Position.new(x, y, center_position.height))) + end + end + player.send_message("Mass spawning npcs with id #{id}.") +end + +on :command, :clearnpcs, RIGHTS_ADMIN do |player, command| + iterator = World.world.npc_repository.iterator + while iterator.has_next + World.world.unregister(iterator.next) + end + player.send_message("All npcs removed.") +end \ No newline at end of file diff --git a/src/org/apollo/game/event/impl/NpcSynchronizationEvent.java b/src/org/apollo/game/event/impl/NpcSynchronizationEvent.java new file mode 100644 index 00000000..ae6eaa32 --- /dev/null +++ b/src/org/apollo/game/event/impl/NpcSynchronizationEvent.java @@ -0,0 +1,72 @@ +package org.apollo.game.event.impl; + +import java.util.List; + +import org.apollo.game.event.Event; +import org.apollo.game.model.Npc; +import org.apollo.game.model.Position; +import org.apollo.game.sync.seg.SynchronizationSegment; + +/** + * An event which is sent to synchronize npcs with players. + * + * @author Major + */ +public class NpcSynchronizationEvent extends Event { + + /** + * The npc's position. + */ + private final Position position; + + /** + * A list of segments. + */ + private final List segments; + + /** + * The amount of local npcs. + */ + private final int localNpcs; + + /** + * Creates a new {@link NpcSynchronizationEvent}. + * + * @param position The position of the {@link Npc}. + * @param segments The list of segments. + * @param localNpcs The amount of local npcs. + */ + public NpcSynchronizationEvent(Position position, List segments, int localNpcs) { + this.position = position; + this.segments = segments; + this.localNpcs = localNpcs; + } + + /** + * Gets the number of local npcs. + * + * @return The number of local npcs. + */ + public int getLocalNpcCount() { + return localNpcs; + } + + /** + * Gets the npc's position. + * + * @return The npc's position. + */ + public Position getPosition() { + return position; + } + + /** + * Gets the synchronization segments. + * + * @return The segments. + */ + public List getSegments() { + return segments; + } + +} \ No newline at end of file diff --git a/src/org/apollo/game/model/Character.java b/src/org/apollo/game/model/Character.java index 399f7c43..d73d9e87 100644 --- a/src/org/apollo/game/model/Character.java +++ b/src/org/apollo/game/model/Character.java @@ -19,11 +19,72 @@ import org.apollo.util.CharacterRepository; */ public abstract class Character { + /** + * The character's current action. + */ + private Action action; // TODO + + /** + * The character's bank. + */ + private final Inventory bank = new Inventory(InventoryConstants.BANK_CAPACITY, StackMode.STACK_ALWAYS); + + /** + * A set of {@link SynchronizationBlock}s. + */ + private SynchronizationBlockSet blockSet = new SynchronizationBlockSet(); + + /** + * The character's {@link NpcDefinition). This is only used by an instance of the {@link Player} class if they are + * appearing as an npc in-game. + */ + private NpcDefinition definition; + + /** + * The character's equipment. + */ + private final Inventory equipment = new Inventory(InventoryConstants.EQUIPMENT_CAPACITY, StackMode.STACK_ALWAYS); + + /** + * The first direction. + */ + private Direction firstDirection = Direction.NONE; + /** * The index of this character in the {@link CharacterRepository} it belongs to. */ private int index = -1; + /** + * The character's inventory. + */ + private final Inventory inventory = new Inventory(InventoryConstants.INVENTORY_CAPACITY); + + /** + * The list of local npcs. + */ + private final List localNpcs = new ArrayList(); + + /** + * A list of local players. + */ + private final List localPlayers = new ArrayList(); + + /** + * The current position of this character. + */ + private Position position; + + /** + * The second direction. + */ + private Direction secondDirection = Direction.NONE; + + /** + * The character's skill set. + */ + private final SkillSet skillSet = new SkillSet(); + /** * Teleportation flag. */ @@ -34,62 +95,6 @@ public abstract class Character { */ private final WalkingQueue walkingQueue = new WalkingQueue(this); - /** - * The first direction. - */ - private Direction firstDirection = Direction.NONE; - - /** - * The second direction. - */ - private Direction secondDirection = Direction.NONE; - - /** - * The current position of this character. - */ - private Position position; - - /** - * A list of local players. - */ - private final List localPlayers = new ArrayList(); // TODO make a specialized collection? - - /** - * A set of {@link SynchronizationBlock}s. - */ - private SynchronizationBlockSet blockSet = new SynchronizationBlockSet(); - - /** - * The character's current action. - */ - private Action action; // TODO - - /** - * The character's inventory. - */ - private final Inventory inventory = new Inventory(InventoryConstants.INVENTORY_CAPACITY); - - /** - * The character's equipment. - */ - private final Inventory equipment = new Inventory(InventoryConstants.EQUIPMENT_CAPACITY, StackMode.STACK_ALWAYS); - - /** - * The character's bank. - */ - private final Inventory bank = new Inventory(InventoryConstants.BANK_CAPACITY, StackMode.STACK_ALWAYS); - - /** - * The character's skill set. - */ - private final SkillSet skillSet = new SkillSet(); - - /** - * The character's {@link NpcDefinition). This is only used by an instance of the {@link Player} class if they are - * appearing as an npc in-game. - */ - private NpcDefinition definition; - /** * Creates a new character with the specified initial position. * @@ -100,31 +105,6 @@ public abstract class Character { init(); } - /** - * Initialises this character. - */ - private void init() { - World.getWorld().schedule(new SkillNormalizationTask(this)); - } - - /** - * Gets the character's inventory. - * - * @return The character's inventory. - */ - public Inventory getInventory() { - return inventory; - } - - /** - * Gets the character's equipment. - * - * @return The character's equipment. - */ - public Inventory getEquipment() { - return equipment; - } - /** * Gets the character's bank. * @@ -135,68 +115,12 @@ public abstract class Character { } /** - * Gets the local player list. + * Gets the {@link SynchronizationBlockSet}. * - * @return The local player list. + * @return The block set. */ - public List getLocalPlayerList() { - return localPlayers; - } - - /** - * Checks if this player is currently teleporting. - * - * @return {@code true} if so, {@code false} if not. - */ - public boolean isTeleporting() { - return teleporting; - } - - /** - * Sets the teleporting flag. - * - * @param teleporting {@code true} if the player is teleporting, {@code false} if not. - */ - public void setTeleporting(boolean teleporting) { - this.teleporting = teleporting; - } - - /** - * Gets the walking queue. - * - * @return The walking queue. - */ - public WalkingQueue getWalkingQueue() { - return walkingQueue; - } - - /** - * Sets the next directions for this character. - * - * @param first The first direction. - * @param second The second direction. - */ - public void setDirections(Direction first, Direction second) { - this.firstDirection = first; - this.secondDirection = second; - } - - /** - * Gets the first direction. - * - * @return The first direction. - */ - public Direction getFirstDirection() { - return firstDirection; - } - - /** - * Gets the second direction. - * - * @return The second direction. - */ - public Direction getSecondDirection() { - return secondDirection; + public SynchronizationBlockSet getBlockSet() { + return blockSet; } /** @@ -217,30 +141,21 @@ public abstract class Character { } /** - * Gets the position of this character. + * Gets the character's equipment. * - * @return The position of this character. + * @return The character's equipment. */ - public Position getPosition() { - return position; + public Inventory getEquipment() { + return equipment; } /** - * Sets the position of this character. + * Gets the first direction. * - * @param position The position of this character. + * @return The first direction. */ - public void setPosition(Position position) { - this.position = position; - } - - /** - * Checks if this character is active. - * - * @return {@code true} if so, {@code false} if not. - */ - public boolean isActive() { - return index != -1; + public Direction getFirstDirection() { + return firstDirection; } /** @@ -255,23 +170,118 @@ public abstract class Character { } /** - * Sets the index of this character. + * Gets the character's inventory. * - * @param index The index of this character. + * @return The character's inventory. */ - public void setIndex(int index) { - synchronized (this) { - this.index = index; - } + public Inventory getInventory() { + return inventory; } /** - * Gets the {@link SynchronizationBlockSet}. + * Gets the local npc list. * - * @return The block set. + * @return The local npc list. */ - public SynchronizationBlockSet getBlockSet() { - return blockSet; + public List getLocalNpcList() { + return localNpcs; + } + + /** + * Gets the local player list. + * + * @return The local player list. + */ + public List getLocalPlayerList() { + return localPlayers; + } + + /** + * Gets this character's {@link NpcDefinition}. + * + * @param definition The definition. + */ + public NpcDefinition getNpcDefinition() { + return definition; + } + + /** + * Gets the position of this character. + * + * @return The position of this character. + */ + public Position getPosition() { + return position; + } + + /** + * Gets the second direction. + * + * @return The second direction. + */ + public Direction getSecondDirection() { + return secondDirection; + } + + /** + * Gets the character's skill set. + * + * @return The character's skill set. + */ + public SkillSet getSkillSet() { + return skillSet; + } + + /** + * Gets the walking queue. + * + * @return The walking queue. + */ + public WalkingQueue getWalkingQueue() { + return walkingQueue; + } + + /** + * Initialises this character. + */ + private void init() { + World.getWorld().schedule(new SkillNormalizationTask(this)); + } + + /** + * Checks if this character is active. + * + * @return {@code true} if so, {@code false} if not. + */ + public boolean isActive() { + return index != -1; + } + + /** + * Checks if this player is currently teleporting. + * + * @return {@code true} if so, {@code false} if not. + */ + public boolean isTeleporting() { + return teleporting; + } + + /** + * Plays the specified animation. + * + * @param animation The animation. + */ + public void playAnimation(Animation animation) { + blockSet.add(SynchronizationBlock.createAnimationBlock(animation)); + } + + /** + * Plays the specified graphic. + * + * @param graphic The graphic. + */ + public void playGraphic(Graphic graphic) { + blockSet.add(SynchronizationBlock.createGraphicBlock(graphic)); } /** @@ -293,56 +303,52 @@ public abstract class Character { public abstract void send(Event event); /** - * Teleports this character to the specified position, setting the appropriate flags and clearing the walking queue. + * Sets this character's {@link NpcDefinition}. * - * @param position The position. + * @param definition The definition. */ - public void teleport(Position position) { - this.teleporting = true; + public void setDefinition(NpcDefinition definition) { + this.definition = definition; + } + + /** + * Sets the next directions for this character. + * + * @param first The first direction. + * @param second The second direction. + */ + public void setDirections(Direction first, Direction second) { + this.firstDirection = first; + this.secondDirection = second; + } + + /** + * Sets the index of this character. + * + * @param index The index of this character. + */ + public void setIndex(int index) { + synchronized (this) { + this.index = index; + } + } + + /** + * Sets the position of this character. + * + * @param position The position of this character. + */ + public void setPosition(Position position) { this.position = position; - this.walkingQueue.clear(); - this.stopAction(); // TODO do it on any movement is a must.. walking queue perhaps? } /** - * Plays the specified animation. + * Sets the teleporting flag. * - * @param animation The animation. + * @param teleporting {@code true} if the player is teleporting, {@code false} if not. */ - public void playAnimation(Animation animation) { - blockSet.add(SynchronizationBlock.createAnimationBlock(animation)); - } - - /** - * Stops the current animation. - */ - public void stopAnimation() { - playAnimation(Animation.STOP_ANIMATION); - } - - /** - * Plays the specified graphic. - * - * @param graphic The graphic. - */ - public void playGraphic(Graphic graphic) { - blockSet.add(SynchronizationBlock.createGraphicBlock(graphic)); - } - - /** - * Stops the current graphic. - */ - public void stopGraphic() { - playGraphic(Graphic.STOP_GRAPHIC); - } - - /** - * Gets the character's skill set. - * - * @return The character's skill set. - */ - public SkillSet getSkillSet() { - return skillSet; + public void setTeleporting(boolean teleporting) { + this.teleporting = teleporting; } /** @@ -373,6 +379,32 @@ public abstract class Character { } } + /** + * Stops the current animation. + */ + public void stopAnimation() { + playAnimation(Animation.STOP_ANIMATION); + } + + /** + * Stops the current graphic. + */ + public void stopGraphic() { + playGraphic(Graphic.STOP_GRAPHIC); + } + + /** + * Teleports this character to the specified position, setting the appropriate flags and clearing the walking queue. + * + * @param position The position. + */ + public void teleport(Position position) { + this.teleporting = true; + this.position = position; + this.walkingQueue.clear(); + this.stopAction(); // TODO do it on any movement is a must.. walking queue perhaps? + } + /** * Turns the character to face the specified position. * @@ -391,22 +423,4 @@ public abstract class Character { blockSet.add(SynchronizationBlock.createInteractingCharacterBlock(index)); } - /** - * Gets this character's {@link NpcDefinition}. - * - * @param definition The definition. - */ - public NpcDefinition getNpcDefinition() { - return definition; - } - - /** - * Sets this character's {@link NpcDefinition}. - * - * @param definition The definition. - */ - public void setDefinition(NpcDefinition definition) { - this.definition = definition; - } - } \ No newline at end of file diff --git a/src/org/apollo/game/model/Player.java b/src/org/apollo/game/model/Player.java index 612af965..a635745c 100644 --- a/src/org/apollo/game/model/Player.java +++ b/src/org/apollo/game/model/Player.java @@ -35,9 +35,9 @@ public final class Player extends Character { public enum PrivilegeLevel { /** - * A standard (rights 0) account. + * An administrator (rights 2) account. */ - STANDARD(0), + ADMINISTRATOR(2), /** * A player moderator (rights 1) account. @@ -45,9 +45,9 @@ public final class Player extends Character { MODERATOR(1), /** - * An administrator (rights 2) account. + * A standard (rights 0) account. */ - ADMINISTRATOR(2); + STANDARD(0); /** * Gets the privilege level for the specified numerical level. @@ -89,46 +89,8 @@ public final class Player extends Character { } } + - /** - * A temporary queue of events sent during the login process. - */ - private final Queue queuedEvents = new ArrayDeque(); - - /** - * The player's credentials. - */ - private PlayerCredentials credentials; - - /** - * The privilege level. - */ - private PrivilegeLevel privilegeLevel = PrivilegeLevel.STANDARD; - - /** - * The membership flag. - */ - private boolean members = false; - - /** - * A flag indicating if the player has designed their character. - */ - private boolean designedCharacter = false; - - /** - * The {@link GameSession} currently attached to this {@link Player}. - */ - private GameSession session; - - /** - * The centre of the last region the client has loaded. - */ - private Position lastKnownRegion; - - /** - * A flag indicating if the region changed in the last cycle. - */ - private boolean regionChanged = false; /** * The player's appearance. @@ -136,9 +98,19 @@ public final class Player extends Character { private Appearance appearance = Appearance.DEFAULT_APPEARANCE; /** - * The current maximum viewing distance of this player. + * The player's credentials. */ - private int viewingDistance = 1; + private PlayerCredentials credentials; + + /** + * A flag indicating if the player has designed their character. + */ + private boolean designedCharacter = false; + + /** + * A flag which indicates there are npcs that couldn't be added. + */ + private boolean excessiveNpcs = false; /** * A flag which indicates there are players that couldn't be added. @@ -150,15 +122,50 @@ public final class Player extends Character { */ private int headIcon = -1; + /** + * This player's interface set. + */ + private final InterfaceSet interfaceSet = new InterfaceSet(this); + + /** + * The centre of the last region the client has loaded. + */ + private Position lastKnownRegion; + + /** + * The membership flag. + */ + private boolean members = false; + /** * This player's prayer icon. */ private int prayerIcon = -1; /** - * This player's interface set. + * The privilege level. */ - private final InterfaceSet interfaceSet = new InterfaceSet(this); + private PrivilegeLevel privilegeLevel = PrivilegeLevel.STANDARD; + + /** + * A temporary queue of events sent during the login process. + */ + private final Queue queuedEvents = new ArrayDeque(); + + /** + * A flag indicating if the region changed in the last cycle. + */ + private boolean regionChanged = false; + + /** + * The {@link GameSession} currently attached to this {@link Player}. + */ + private GameSession session; + + /** + * The current maximum viewing distance of this player. + */ + private int viewingDistance = 1; /** * A flag indicating if the player is withdrawing items as notes. @@ -178,21 +185,19 @@ public final class Player extends Character { } /** - * Gets this player's interface set. - * - * @return The interface set for this player. + * Decrements this player's viewing distance if it is greater than 1. */ - public InterfaceSet getInterfaceSet() { - return interfaceSet; + public void decrementViewingDistance() { + if (viewingDistance > 1) { // TODO should it be 0? + viewingDistance--; + } } /** - * Checks if there are excessive players. - * - * @return {@code true} if so, {@code false} if not. + * Sets the excessive npcs flag. */ - public boolean isExcessivePlayersSet() { - return excessivePlayers; + public void flagExcessiveNpcs() { + this.excessiveNpcs = true; } /** @@ -203,53 +208,48 @@ public final class Player extends Character { } /** - * Resets the excessive players flag. - */ - public void resetExcessivePlayers() { - excessivePlayers = false; - } - - /** - * Resets this player's viewing distance. - */ - public void resetViewingDistance() { - viewingDistance = 1; - } - - /** - * Gets this player's viewing distance. + * Gets the player's appearance. * - * @return The viewing distance. + * @return The appearance. */ - public int getViewingDistance() { - return viewingDistance; + public Appearance getAppearance() { + return appearance; } /** - * Increments this player's viewing distance if it is less than the maximum viewing distance. - */ - public void incrementViewingDistance() { - if (viewingDistance < Position.MAX_DISTANCE) { - viewingDistance++; - } - } - - /** - * Decrements this player's viewing distance if it is greater than 1. - */ - public void decrementViewingDistance() { - if (viewingDistance > 1) { // TODO should it be 0? - viewingDistance--; - } - } - - /** - * Checks if this player has ever known a region. + * Gets the player's credentials. * - * @return {@code true} if so, {@code false} if not. + * @return The player's credentials. */ - public boolean hasLastKnownRegion() { - return lastKnownRegion != null; + public PlayerCredentials getCredentials() { + return credentials; + } + + /** + * Gets the player's name, encoded as a long. + * + * @return The encoded player name. + */ + public long getEncodedName() { + return credentials.getEncodedUsername(); + } + + /** + * Gets the player's head icon. + * + * @return The head icon. + */ + public int getHeadIcon() { + return headIcon; + } + + /** + * Gets this player's interface set. + * + * @return The interface set for this player. + */ + public InterfaceSet getInterfaceSet() { + return interfaceSet; } /** @@ -262,12 +262,21 @@ public final class Player extends Character { } /** - * Sets the last known region. + * Gets the player's name. * - * @param lastKnownRegion The last known region. + * @return The player's name. */ - public void setLastKnownRegion(Position lastKnownRegion) { - this.lastKnownRegion = lastKnownRegion; + public String getName() { + return credentials.getUsername(); + } + + /** + * Gets the player's prayer icon. + * + * @return The prayer icon. + */ + public int getPrayerIcon() { + return prayerIcon; } /** @@ -280,67 +289,56 @@ public final class Player extends Character { } /** - * Sets the privilege level. + * Gets the game session. * - * @param privilegeLevel The privilege level. + * @return The game session. */ - public void setPrivilegeLevel(PrivilegeLevel privilegeLevel) { - this.privilegeLevel = privilegeLevel; + public GameSession getSession() { + return session; } /** - * Checks if this player account has membership. + * Gets this player's viewing distance. + * + * @return The viewing distance. + */ + public int getViewingDistance() { + return viewingDistance; + } + + /** + * Checks if the player has designed their character. + * + * @return A flag indicating if the player has designed their character. + */ + public boolean hasDesignedCharacter() { + return designedCharacter; + } + + /** + * Checks if this player has ever known a region. * * @return {@code true} if so, {@code false} if not. */ - public boolean isMembers() { - return members; + public boolean hasLastKnownRegion() { + return lastKnownRegion != null; } /** - * Changes the membership status of this player. + * Checks if the region has changed. * - * @param members The new membership flag. + * @return {@code true} if so, {@code false} if not. */ - public void setMembers(boolean members) { - this.members = members; + public boolean hasRegionChanged() { + return regionChanged; } /** - * Sets the player's {@link GameSession}. - * - * @param session The player's {@link GameSession}. - * @param reconnecting The reconnecting flag. + * Increments this player's viewing distance if it is less than the maximum viewing distance. */ - public void setSession(GameSession session, boolean reconnecting) { - this.session = session; - if (!reconnecting) { - sendInitialEvents(); - } - getBlockSet().add(SynchronizationBlock.createAppearanceBlock(this)); - } - - /** - * Gets the player's credentials. - * - * @return The player's credentials. - */ - public PlayerCredentials getCredentials() { - return credentials; - } - - @Override - public void send(Event event) { - if (isActive()) { - if (!queuedEvents.isEmpty()) { - for (Event queuedEvent : queuedEvents) { - session.dispatchEvent(queuedEvent); - } - queuedEvents.clear(); - } - session.dispatchEvent(event); - } else { - queuedEvents.add(event); + public void incrementViewingDistance() { + if (viewingDistance < Position.MAX_DISTANCE) { + viewingDistance++; } } @@ -352,23 +350,6 @@ public final class Player extends Character { initSkills(); } - /** - * Initialises the player's skills. - */ - private void initSkills() { - SkillSet skills = getSkillSet(); - - // synchronization listener - SkillListener syncListener = new SynchronizationSkillListener(this); - - // level up listener - SkillListener levelUpListener = new LevelUpSkillListener(this); - - // add the listeners - skills.addListener(syncListener); - skills.addListener(levelUpListener); - } - /** * Initialises the player's inventories. */ @@ -406,6 +387,95 @@ public final class Player extends Character { equipment.addListener(fullEquipmentListener); } + /** + * Initialises the player's skills. + */ + private void initSkills() { + SkillSet skills = getSkillSet(); + + // synchronization listener + SkillListener syncListener = new SynchronizationSkillListener(this); + + // level up listener + SkillListener levelUpListener = new LevelUpSkillListener(this); + + // add the listeners + skills.addListener(syncListener); + skills.addListener(levelUpListener); + } + + /** + * Checks if there are excessive npcs. + * + * @return {@code true} if so, {@code false} if not. + */ + public boolean isExcessiveNpcsSet() { + return excessiveNpcs; + } + + /** + * Checks if there are excessive players. + * + * @return {@code true} if so, {@code false} if not. + */ + public boolean isExcessivePlayersSet() { + return excessivePlayers; + } + + /** + * Checks if this player account has membership. + * + * @return {@code true} if so, {@code false} if not. + */ + public boolean isMembers() { + return members; + } + + /** + * Gets the withdrawing notes flag. + * + * @return The flag. + */ + public boolean isWithdrawingNotes() { + return withdrawingNotes; + } + + /** + * Logs the player out, if possible. + */ + public void logout() { + send(new LogoutEvent()); + } + + /** + * Resets the excessive players flag. + */ + public void resetExcessivePlayers() { + excessivePlayers = false; + } + + /** + * Resets this player's viewing distance. + */ + public void resetViewingDistance() { + viewingDistance = 1; + } + + @Override + public void send(Event event) { + if (isActive()) { + if (!queuedEvents.isEmpty()) { + for (Event queuedEvent : queuedEvents) { + session.dispatchEvent(queuedEvent); + } + queuedEvents.clear(); + } + session.dispatchEvent(event); + } else { + queuedEvents.add(event); + } + } + /** * Sends the initial events. */ @@ -438,37 +508,13 @@ public final class Player extends Character { getSkillSet().forceRefresh(); } - @Override - public String toString() { - return Player.class.getName() + " [username=" + credentials.getUsername() + ", privilegeLevel=" - + privilegeLevel + "]"; - } - /** - * Sets the region changed flag. + * Sends a message to the character. * - * @param regionChanged A flag indicating if the region has changed. + * @param message The message. */ - public void setRegionChanged(boolean regionChanged) { - this.regionChanged = regionChanged; - } - - /** - * Checks if the region has changed. - * - * @return {@code true} if so, {@code false} if not. - */ - public boolean hasRegionChanged() { - return regionChanged; - } - - /** - * Gets the player's appearance. - * - * @return The appearance. - */ - public Appearance getAppearance() { - return appearance; + public void sendMessage(String message) { + send(new ServerMessageEvent(message)); } /** @@ -481,40 +527,6 @@ public final class Player extends Character { this.getBlockSet().add(SynchronizationBlock.createAppearanceBlock(this)); } - /** - * Gets the player's name. - * - * @return The player's name. - */ - public String getName() { - return credentials.getUsername(); - } - - /** - * Gets the player's name, encoded as a long. - * - * @return The encoded player name. - */ - public long getEncodedName() { - return credentials.getEncodedUsername(); - } - - /** - * Logs the player out, if possible. - */ - public void logout() { - send(new LogoutEvent()); - } - - /** - * Gets the game session. - * - * @return The game session. - */ - public GameSession getSession() { - return session; - } - /** * Sets the character design flag. * @@ -525,21 +537,71 @@ public final class Player extends Character { } /** - * Checks if the player has designed their character. + * Sets the player's head icon. * - * @return A flag indicating if the player has designed their character. + * @param headIcon The head icon. */ - public boolean hasDesignedCharacter() { - return designedCharacter; + public void setHeadIcon(int headIcon) { + this.headIcon = headIcon; } /** - * Gets the withdrawing notes flag. + * Sets the last known region. * - * @return The flag. + * @param lastKnownRegion The last known region. */ - public boolean isWithdrawingNotes() { - return withdrawingNotes; + public void setLastKnownRegion(Position lastKnownRegion) { + this.lastKnownRegion = lastKnownRegion; + } + + /** + * Changes the membership status of this player. + * + * @param members The new membership flag. + */ + public void setMembers(boolean members) { + this.members = members; + } + + /** + * Sets the player's prayer icon. + * + * @param prayerIcon The prayer icon. + */ + public void setPrayerIcon(int prayerIcon) { + this.prayerIcon = prayerIcon; + } + + /** + * Sets the privilege level. + * + * @param privilegeLevel The privilege level. + */ + public void setPrivilegeLevel(PrivilegeLevel privilegeLevel) { + this.privilegeLevel = privilegeLevel; + } + + /** + * Sets the region changed flag. + * + * @param regionChanged A flag indicating if the region has changed. + */ + public void setRegionChanged(boolean regionChanged) { + this.regionChanged = regionChanged; + } + + /** + * Sets the player's {@link GameSession}. + * + * @param session The player's {@link GameSession}. + * @param reconnecting The reconnecting flag. + */ + public void setSession(GameSession session, boolean reconnecting) { + this.session = session; + if (!reconnecting) { + sendInitialEvents(); + } + getBlockSet().add(SynchronizationBlock.createAppearanceBlock(this)); } /** @@ -557,49 +619,10 @@ public final class Player extends Character { interfaceSet.close(); // TODO: should this be done if size == 0? } - /** - * Gets the player's prayer icon. - * - * @return The prayer icon. - */ - public int getPrayerIcon() { - return prayerIcon; - } - - /** - * Gets the player's head icon. - * - * @return The head icon. - */ - public int getHeadIcon() { - return headIcon; - } - - /** - * Sends a message to the character. - * - * @param message The message. - */ - public void sendMessage(String message) { - send(new ServerMessageEvent(message)); - } - - /** - * Sets the player's head icon. - * - * @param headIcon The head icon. - */ - public void setHeadIcon(int headIcon) { - this.headIcon = headIcon; - } - - /** - * Sets the player's prayer icon. - * - * @param prayerIcon The prayer icon. - */ - public void setPrayerIcon(int prayerIcon) { - this.prayerIcon = prayerIcon; + @Override + public String toString() { + return Player.class.getName() + " [username=" + credentials.getUsername() + ", privilegeLevel=" + + privilegeLevel + "]"; } } \ No newline at end of file diff --git a/src/org/apollo/game/model/World.java b/src/org/apollo/game/model/World.java index bf390254..dc7ae797 100644 --- a/src/org/apollo/game/model/World.java +++ b/src/org/apollo/game/model/World.java @@ -33,16 +33,6 @@ import org.apollo.util.plugin.PluginManager; */ public final class World { - /** - * The logger for this class. - */ - private static final Logger logger = Logger.getLogger(World.class.getName()); - - /** - * The world. - */ - private static final World world = new World(); - /** * Represents the different status codes for registering a player. * @@ -50,11 +40,6 @@ public final class World { */ public enum RegistrationStatus { - /** - * Indicates the world is full. - */ - WORLD_FULL, - /** * Indicates that the player is already online. */ @@ -63,9 +48,24 @@ public final class World { /** * Indicates that the player was registered successfully. */ - OK; + OK, + + /** + * Indicates the world is full. + */ + WORLD_FULL; } + /** + * The logger for this class. + */ + private static final Logger logger = Logger.getLogger(World.class.getName()); + + /** + * The world. + */ + private static final World world = new World(); + /** * Gets the world. * @@ -75,20 +75,16 @@ public final class World { return world; } - /** - * The scheduler. - */ - // TODO: better place than here? - private final Scheduler scheduler = new Scheduler(); - /** * The command dispatcher. */ // TODO: better place than here? private final CommandDispatcher dispatcher = new CommandDispatcher(); - // TODO: better place than here!! - private PluginManager pluginManager; + /** + * The {@link CharacterRepository} of {@link Npc}s. + */ + private final CharacterRepository npcRepository = new CharacterRepository(WorldConstants.MAXIMUM_NPCS); /** * The {@link CharacterRepository} of {@link Player}s. @@ -96,6 +92,15 @@ public final class World { private final CharacterRepository playerRepository = new CharacterRepository( WorldConstants.MAXIMUM_PLAYERS); + // TODO: better place than here!! + private PluginManager pluginManager; + + /** + * The scheduler. + */ + // TODO: better place than here? + private final Scheduler scheduler = new Scheduler(); + /** * Creates the world. */ @@ -103,6 +108,47 @@ public final class World { } + /** + * Gets the command dispatcher. TODO should this be here? + * + * @return The command dispatcher. + */ + public CommandDispatcher getCommandDispatcher() { + return dispatcher; + } + + /** + * Gets the npc repository. + * + * @return The npc repository. + */ + public CharacterRepository getNpcRepository() { + return npcRepository; + } + + /** + * Gets the character repository. NOTE: {@link CharacterRepository#add(Character)} and + * {@link CharacterRepository#remove(Character)} should not be called directly! These mutation methods are not + * guaranteed to work in future releases! + *

+ * Instead, use the {@link World#register(Player)} and {@link World#unregister(Player)} methods which do the same + * thing and will continue to work as normal in future releases. + * + * @return The character repository. + */ + public CharacterRepository getPlayerRepository() { + return playerRepository; + } + + /** + * Gets the plugin manager. TODO should this be here? + * + * @return The plugin manager. + */ + public PluginManager getPluginManager() { + return pluginManager; + } + /** * Initialises the world by loading definitions from the specified file system. * @@ -147,17 +193,42 @@ public final class World { } /** - * Gets the character repository. NOTE: {@link CharacterRepository#add(Character)} and - * {@link CharacterRepository#remove(Character)} should not be called directly! These mutation methods are not - * guaranteed to work in future releases! - *

- * Instead, use the {@link World#register(Player)} and {@link World#unregister(Player)} methods which do the same - * thing and will continue to work as normal in future releases. + * Checks if the specified player is online. * - * @return The character repository. + * @param name The player's name. + * @return {@code true} if so, {@code false} if not. */ - public CharacterRepository getPlayerRepository() { - return playerRepository; + public boolean isPlayerOnline(String name) { + // TODO: use a hash set or map in the future? + for (Player player : playerRepository) { + if (player.getName().equalsIgnoreCase(name)) { + return true; + } + } + return false; + } + + /** + * Calls the {@link Scheduler#pulse()} method. + */ + public void pulse() { + scheduler.pulse(); + } + + /** + * Registers the specified npc. + * + * @param npc The npc. + * @return {@code true} if the npc registered successfully, otherwise {@code false}. + */ + public boolean register(final Npc npc) { + boolean success = npcRepository.add(npc); + if (success) { + logger.info("Registered npc: " + npc + " [online=" + npcRepository.size() + "]"); + } else { + logger.warning("Failed to register npc, repository capacity reached: [online=" + npcRepository.size() + "]"); + } + return success; } /** @@ -183,19 +254,25 @@ public final class World { } /** - * Checks if the specified player is online. + * Schedules a new task. * - * @param name The player's name. - * @return {@code true} if so, {@code false} if not. + * @param task The {@link ScheduledTask}. */ - public boolean isPlayerOnline(String name) { - // TODO: use a hash set or map in the future? - for (Player player : playerRepository) { - if (player.getName().equalsIgnoreCase(name)) { - return true; - } + public void schedule(ScheduledTask task) { + scheduler.schedule(task); + } + + /** + * Unregisters the specified {@link Npc}. + * + * @param npc The npc. + */ + public void unregister(Npc npc) { + if (npcRepository.remove(npc)) { + logger.info("Unregistered npc: " + npc + " [online=" + npcRepository.size() + "]"); + } else { + logger.warning("Could not find npc " + npc + " to unregister!"); } - return false; } /** @@ -211,38 +288,4 @@ public final class World { } } - /** - * Schedules a new task. - * - * @param task The {@link ScheduledTask}. - */ - public void schedule(ScheduledTask task) { - scheduler.schedule(task); - } - - /** - * Calls the {@link Scheduler#pulse()} method. - */ - public void pulse() { - scheduler.pulse(); - } - - /** - * Gets the command dispatcher. TODO should this be here? - * - * @return The command dispatcher. - */ - public CommandDispatcher getCommandDispatcher() { - return dispatcher; - } - - /** - * Gets the plugin manager. TODO should this be here? - * - * @return The plugin manager. - */ - public PluginManager getPluginManager() { - return pluginManager; - } - } diff --git a/src/org/apollo/game/model/WorldConstants.java b/src/org/apollo/game/model/WorldConstants.java index 43629c00..1ec864f1 100644 --- a/src/org/apollo/game/model/WorldConstants.java +++ b/src/org/apollo/game/model/WorldConstants.java @@ -7,6 +7,11 @@ package org.apollo.game.model; */ public final class WorldConstants { + /** + * The maximum number of npcs. + */ + public static final int MAXIMUM_NPCS = 2000; + /** * The maximum number of players. */ @@ -16,7 +21,6 @@ public final class WorldConstants { * Default private constructor to prevent instantiation by other classes. */ private WorldConstants() { - } -} +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/ParallelClientSynchronizer.java b/src/org/apollo/game/sync/ParallelClientSynchronizer.java index 5377fe23..76dcf4e5 100644 --- a/src/org/apollo/game/sync/ParallelClientSynchronizer.java +++ b/src/org/apollo/game/sync/ParallelClientSynchronizer.java @@ -6,11 +6,15 @@ import java.util.concurrent.Phaser; import java.util.concurrent.ThreadFactory; import org.apollo.game.GameService; -import org.apollo.game.model.Player; +import org.apollo.game.model.Npc; import org.apollo.game.model.World; +import org.apollo.game.model.Player; +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.PostNpcSynchronizationTask; import org.apollo.game.sync.task.PostPlayerSynchronizationTask; +import org.apollo.game.sync.task.PreNpcSynchronizationTask; import org.apollo.game.sync.task.PrePlayerSynchronizationTask; import org.apollo.game.sync.task.SynchronizationTask; import org.apollo.util.CharacterRepository; @@ -24,6 +28,7 @@ import org.apollo.util.NamedThreadFactory; * will work. * * @author Graham + * @author Major */ public final class ParallelClientSynchronizer extends ClientSynchronizer { @@ -50,7 +55,9 @@ public final class ParallelClientSynchronizer extends ClientSynchronizer { @Override public void synchronize() { CharacterRepository players = World.getWorld().getPlayerRepository(); + CharacterRepository npcs = World.getWorld().getNpcRepository(); int playerCount = players.size(); + int npcCount = npcs.size(); phaser.bulkRegister(playerCount); for (Player player : players) { @@ -59,6 +66,13 @@ public final class ParallelClientSynchronizer extends ClientSynchronizer { } phaser.arriveAndAwaitAdvance(); + phaser.bulkRegister(npcCount); + for (Npc npc : npcs) { + SynchronizationTask task = new PreNpcSynchronizationTask(npc); + executor.submit(new PhasedSynchronizationTask(phaser, task)); + } + phaser.arriveAndAwaitAdvance(); + phaser.bulkRegister(playerCount); for (Player player : players) { SynchronizationTask task = new PlayerSynchronizationTask(player); @@ -66,12 +80,26 @@ public final class ParallelClientSynchronizer extends ClientSynchronizer { } phaser.arriveAndAwaitAdvance(); + phaser.bulkRegister(playerCount); + for (Player player : players) { + SynchronizationTask task = new NpcSynchronizationTask(player); + 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(); + + phaser.bulkRegister(npcCount); + for (Npc npc : npcs) { + SynchronizationTask task = new PostNpcSynchronizationTask(npc); + executor.submit(new PhasedSynchronizationTask(phaser, task)); + } + phaser.arriveAndAwaitAdvance(); } -} +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/SequentialClientSynchronizer.java b/src/org/apollo/game/sync/SequentialClientSynchronizer.java index 46f6558b..f68a28c5 100644 --- a/src/org/apollo/game/sync/SequentialClientSynchronizer.java +++ b/src/org/apollo/game/sync/SequentialClientSynchronizer.java @@ -1,10 +1,14 @@ package org.apollo.game.sync; import org.apollo.game.GameService; -import org.apollo.game.model.Player; +import org.apollo.game.model.Npc; import org.apollo.game.model.World; +import org.apollo.game.model.Player; +import org.apollo.game.sync.task.NpcSynchronizationTask; import org.apollo.game.sync.task.PlayerSynchronizationTask; +import org.apollo.game.sync.task.PostNpcSynchronizationTask; import org.apollo.game.sync.task.PostPlayerSynchronizationTask; +import org.apollo.game.sync.task.PreNpcSynchronizationTask; import org.apollo.game.sync.task.PrePlayerSynchronizationTask; import org.apollo.game.sync.task.SynchronizationTask; import org.apollo.util.CharacterRepository; @@ -16,27 +20,41 @@ import org.apollo.util.CharacterRepository; * cores/processors, however, both classes will work. * * @author Graham + * @author Major */ public final class SequentialClientSynchronizer extends ClientSynchronizer { @Override public void synchronize() { CharacterRepository players = World.getWorld().getPlayerRepository(); + CharacterRepository npcs = World.getWorld().getNpcRepository(); for (Player player : players) { SynchronizationTask task = new PrePlayerSynchronizationTask(player); task.run(); } + for (Npc npc : npcs) { + SynchronizationTask task = new PreNpcSynchronizationTask(npc); + task.run(); + } + for (Player player : players) { SynchronizationTask task = new PlayerSynchronizationTask(player); task.run(); + task = new NpcSynchronizationTask(player); + task.run(); } for (Player player : players) { SynchronizationTask task = new PostPlayerSynchronizationTask(player); task.run(); } + + for (Npc npc : npcs) { + SynchronizationTask task = new PostNpcSynchronizationTask(npc); + task.run(); + } } -} +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/block/TransformBlock.java b/src/org/apollo/game/sync/block/TransformBlock.java new file mode 100644 index 00000000..c0552315 --- /dev/null +++ b/src/org/apollo/game/sync/block/TransformBlock.java @@ -0,0 +1,34 @@ +package org.apollo.game.sync.block; + +/** + * The transform {@link SynchronizationBlock}. This is an npc-only block that updates the npc's definition in the + * client, and thus its animations, size, etc. + * + * @author Major + */ +public final class TransformBlock extends SynchronizationBlock { + + /** + * The new id. + */ + private final int id; + + /** + * Creates a new transform block. + * + * @param id The id. + */ + public TransformBlock(int id) { + this.id = id; + } + + /** + * Gets the id. + * + * @return The id. + */ + public int getId() { + return id; + } + +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/seg/AddNpcSegment.java b/src/org/apollo/game/sync/seg/AddNpcSegment.java new file mode 100644 index 00000000..d88c0c27 --- /dev/null +++ b/src/org/apollo/game/sync/seg/AddNpcSegment.java @@ -0,0 +1,75 @@ +package org.apollo.game.sync.seg; + +import org.apollo.game.model.Position; +import org.apollo.game.sync.block.SynchronizationBlockSet; + +/** + * A {@link SynchronizationSegment} that adds an npc. + * + * @author Major + */ +public final class AddNpcSegment extends SynchronizationSegment { + + /** + * The index. + */ + private final int index; + + /** + * The position. + */ + private final Position position; + + /** + * The id of the npc. + */ + private final int npcId; + + /** + * Creates the add npc segment. + * + * @param blockSet The block set. + * @param index The characters's index. + * @param position The position. + * @param npcId The id of the npc. + */ + public AddNpcSegment(SynchronizationBlockSet blockSet, int index, Position position, int npcId) { + super(blockSet); + this.index = index; + this.position = position; + this.npcId = npcId; + } + + /** + * Gets the character's index. + * + * @return The index. + */ + public int getIndex() { + return index; + } + + /** + * Gets the npc id. + * + * @return The npcId + */ + public int getNpcId() { + return npcId; + } + + /** + * Gets the position. + * + * @return The position. + */ + public Position getPosition() { + return position; + } + + @Override + public SegmentType getType() { + return SegmentType.ADD_CHARACTER; + } + +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/seg/AddCharacterSegment.java b/src/org/apollo/game/sync/seg/AddPlayerSegment.java similarity index 84% rename from src/org/apollo/game/sync/seg/AddCharacterSegment.java rename to src/org/apollo/game/sync/seg/AddPlayerSegment.java index f4d4b597..91bf1159 100644 --- a/src/org/apollo/game/sync/seg/AddCharacterSegment.java +++ b/src/org/apollo/game/sync/seg/AddPlayerSegment.java @@ -8,7 +8,7 @@ import org.apollo.game.sync.block.SynchronizationBlockSet; * * @author Graham */ -public final class AddCharacterSegment extends SynchronizationSegment { +public final class AddPlayerSegment extends SynchronizationSegment { /** * The index. @@ -27,7 +27,7 @@ public final class AddCharacterSegment extends SynchronizationSegment { * @param index The characters's index. * @param position The position. */ - public AddCharacterSegment(SynchronizationBlockSet blockSet, int index, Position position) { + public AddPlayerSegment(SynchronizationBlockSet blockSet, int index, Position position) { super(blockSet); this.index = index; this.position = position; diff --git a/src/org/apollo/game/sync/task/NpcSynchronizationTask.java b/src/org/apollo/game/sync/task/NpcSynchronizationTask.java new file mode 100644 index 00000000..69e09e84 --- /dev/null +++ b/src/org/apollo/game/sync/task/NpcSynchronizationTask.java @@ -0,0 +1,88 @@ +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.AddNpcSegment; +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; + +/** + * A {@link SynchronizationTask} which synchronizes npcs with the specified {@link Player}. + * + * @author Major + */ +public final class NpcSynchronizationTask extends SynchronizationTask { + + /** + * The maximum number of npcs 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_NPCS_PER_CYCLE = 20; + + /** + * The player. + */ + private final Player player; + + /** + * Creates the {@link NpcSynchronizationTask} for the specified player. + * + * @param player The player. + */ + public NpcSynchronizationTask(Player player) { + this.player = player; + } + + @Override + public void run() { + SynchronizationBlockSet blockSet = player.getBlockSet(); + List localNpcs = player.getLocalNpcList(); + int oldLocalNpcs = localNpcs.size(); + List segments = new ArrayList(); + + for (Iterator it = localNpcs.iterator(); it.hasNext();) { + Npc npc = it.next(); + if (!npc.isActive() || npc.isTeleporting() + || npc.getPosition().getLongestDelta(player.getPosition()) > player.getViewingDistance()) { + it.remove(); + segments.add(new RemoveCharacterSegment()); + } else { + segments.add(new MovementSegment(npc.getBlockSet(), npc.getDirections())); + } + } + + int added = 0; + + CharacterRepository repository = World.getWorld().getNpcRepository(); + for (Npc npc : repository) { + if (localNpcs.size() >= 255) { + player.flagExcessiveNpcs(); + break; + } else if (added >= NEW_NPCS_PER_CYCLE) { + break; + } + + if (npc.getPosition().isWithinDistance(player.getPosition(), player.getViewingDistance()) + && !localNpcs.contains(npc)) { + localNpcs.add(npc); + added++; + blockSet = npc.getBlockSet(); + segments.add(new AddNpcSegment(blockSet, npc.getIndex(), npc.getPosition(), npc.getNpcDefinition() + .getId())); + } + + } + NpcSynchronizationEvent event = new NpcSynchronizationEvent(player.getPosition(), segments, oldLocalNpcs); + player.send(event); + } + +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java b/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java index af6de2e4..6fa4370c 100644 --- a/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java +++ b/src/org/apollo/game/sync/task/PlayerSynchronizationTask.java @@ -12,7 +12,7 @@ import org.apollo.game.sync.block.AppearanceBlock; import org.apollo.game.sync.block.ChatBlock; import org.apollo.game.sync.block.SynchronizationBlock; import org.apollo.game.sync.block.SynchronizationBlockSet; -import org.apollo.game.sync.seg.AddCharacterSegment; +import org.apollo.game.sync.seg.AddPlayerSegment; import org.apollo.game.sync.seg.RemoveCharacterSegment; import org.apollo.game.sync.seg.MovementSegment; import org.apollo.game.sync.seg.SynchronizationSegment; @@ -104,7 +104,7 @@ public final class PlayerSynchronizationTask extends SynchronizationTask { blockSet.add(SynchronizationBlock.createAppearanceBlock(p)); } - segments.add(new AddCharacterSegment(blockSet, p.getIndex(), p.getPosition())); + segments.add(new AddPlayerSegment(blockSet, p.getIndex(), p.getPosition())); } } diff --git a/src/org/apollo/game/sync/task/PostNpcSynchronizationTask.java b/src/org/apollo/game/sync/task/PostNpcSynchronizationTask.java new file mode 100644 index 00000000..c6e956bb --- /dev/null +++ b/src/org/apollo/game/sync/task/PostNpcSynchronizationTask.java @@ -0,0 +1,32 @@ +package org.apollo.game.sync.task; + +import org.apollo.game.model.Npc; + +/** + * A {@link SynchronizationTask} which does post-synchronization work for the specified {@link Npc}. + * + * @author Major + */ +public final class PostNpcSynchronizationTask extends SynchronizationTask { + + /** + * The npc. + */ + private final Npc npc; + + /** + * Creates the {@link PostNpcSynchronizationTask} for the specified player. + * + * @param npc The npc. + */ + public PostNpcSynchronizationTask(Npc npc) { + this.npc = npc; + } + + @Override + public void run() { + npc.setTeleporting(false); + npc.resetBlockSet(); + } + +} \ No newline at end of file diff --git a/src/org/apollo/game/sync/task/PreNpcSynchronizationTask.java b/src/org/apollo/game/sync/task/PreNpcSynchronizationTask.java new file mode 100644 index 00000000..f6a0a33b --- /dev/null +++ b/src/org/apollo/game/sync/task/PreNpcSynchronizationTask.java @@ -0,0 +1,31 @@ +package org.apollo.game.sync.task; + +import org.apollo.game.model.Npc; + +/** + * A {@link SynchronizationTask} which does pre-synchronization work for the specified npc. + * + * @author Major + */ +public final class PreNpcSynchronizationTask extends SynchronizationTask { + + /** + * The npc. + */ + private final Npc npc; + + /** + * Creates the {@link PreNpcSynchronizationTask} for the specified npc. + * + * @param npc The npc. + */ + public PreNpcSynchronizationTask(Npc npc) { + this.npc = npc; + } + + @Override + public void run() { + npc.getWalkingQueue().pulse(); + } + +} \ No newline at end of file diff --git a/src/org/apollo/net/release/r317/NpcSynchronizationEventEncoder.java b/src/org/apollo/net/release/r317/NpcSynchronizationEventEncoder.java new file mode 100644 index 00000000..d0d84c4c --- /dev/null +++ b/src/org/apollo/net/release/r317/NpcSynchronizationEventEncoder.java @@ -0,0 +1,302 @@ +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.ForceChatBlock; +import org.apollo.game.sync.block.GraphicBlock; +import org.apollo.game.sync.block.HitUpdateBlock; +import org.apollo.game.sync.block.InteractingCharacterBlock; +import org.apollo.game.sync.block.SecondHitUpdateBlock; +import org.apollo.game.sync.block.SynchronizationBlockSet; +import org.apollo.game.sync.block.TransformBlock; +import org.apollo.game.sync.block.TurnToPositionBlock; +import org.apollo.game.sync.seg.AddNpcSegment; +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.DataTransformation; +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; + +/** + * An {@link EventEncoder} for the {@link NpcSynchronizationEvent}. + * + * @author Major + */ +public class NpcSynchronizationEventEncoder extends EventEncoder { + + @Override + public GamePacket encode(NpcSynchronizationEvent event) { + GamePacketBuilder builder = new GamePacketBuilder(65, PacketType.VARIABLE_SHORT); + builder.switchToBitAccess(); + + GamePacketBuilder blockBuilder = new GamePacketBuilder(); + + builder.putBits(8, event.getLocalNpcCount()); + + for (SynchronizationSegment segment : event.getSegments()) { + SegmentType type = segment.getType(); + if (type == SegmentType.REMOVE_CHARACTER) { + putRemoveCharacterUpdate(builder); + } else if (type == SegmentType.ADD_CHARACTER) { + putAddNpcUpdate((AddNpcSegment) segment, event, builder); + putBlocks(segment, blockBuilder); + } else { + putMovementUpdate(segment, event, builder); + putBlocks(segment, blockBuilder); + } + } + + if (blockBuilder.getLength() > 0) { + builder.putBits(14, 16383); + builder.switchToByteAccess(); + builder.putRawBuilder(blockBuilder); + } else { + builder.switchToByteAccess(); + } + + return builder.toGamePacket(); + } + + /** + * Puts an add character update. + * + * @param seg The segment. + * @param event The event. + * @param builder The builder. + */ + private void putAddNpcUpdate(AddNpcSegment 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, other.getY() - npc.getY()); + builder.putBits(5, other.getX() - npc.getX()); + builder.putBits(1, 0); // discard walking queue + builder.putBits(12, seg.getNpcId()); + builder.putBits(1, updateRequired ? 1 : 0); + } + + /** + * 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()); + } + + /** + * Puts the blocks for the specified segment. + * + * @param segment The segment. + * @param blockBuilder The block builder. + */ + private void putBlocks(SynchronizationSegment segment, GamePacketBuilder blockBuilder) { + SynchronizationBlockSet blockSet = segment.getBlockSet(); + if (blockSet.size() > 0) { + int mask = 0; + + if (blockSet.contains(AnimationBlock.class)) { + mask |= 0x10; + } + + if (blockSet.contains(HitUpdateBlock.class)) { + mask |= 0x8; + } + + if (blockSet.contains(GraphicBlock.class)) { + mask |= 0x80; + } + + if (blockSet.contains(InteractingCharacterBlock.class)) { + mask |= 0x20; + } + + if (blockSet.contains(ForceChatBlock.class)) { + mask |= 0x1; + } + + if (blockSet.contains(SecondHitUpdateBlock.class)) { + mask |= 0x40; + } + + if (blockSet.contains(TransformBlock.class)) { + mask |= 0x2; + } + + if (blockSet.contains(TurnToPositionBlock.class)) { + mask |= 0x4; + } + + blockBuilder.put(DataType.BYTE, mask); + + if (blockSet.contains(AnimationBlock.class)) { + putAnimationBlock(blockSet.get(AnimationBlock.class), blockBuilder); + } + + if (blockSet.contains(HitUpdateBlock.class)) { + putHitUpdateBlock(blockSet.get(HitUpdateBlock.class), blockBuilder); + } + + if (blockSet.contains(GraphicBlock.class)) { + putGraphicBlock(blockSet.get(GraphicBlock.class), blockBuilder); + } + + if (blockSet.contains(InteractingCharacterBlock.class)) { + putInteractingCharacterBlock(blockSet.get(InteractingCharacterBlock.class), blockBuilder); + } + + if (blockSet.contains(ForceChatBlock.class)) { + putForceChatBlock(blockSet.get(ForceChatBlock.class), blockBuilder); + } + + if (blockSet.contains(SecondHitUpdateBlock.class)) { + putSecondHitUpdateBlock(blockSet.get(SecondHitUpdateBlock.class), blockBuilder); + } + + if (blockSet.contains(TransformBlock.class)) { + putTransformBlock(blockSet.get(TransformBlock.class), blockBuilder); + } + + if (blockSet.contains(TurnToPositionBlock.class)) { + putTurnToPositionBlock(blockSet.get(TurnToPositionBlock.class), blockBuilder); + } + } + } + + /** + * Puts a force chat block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putForceChatBlock(ForceChatBlock block, GamePacketBuilder builder) { + builder.putString(block.getMessage()); + } + + /** + * 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 a hit update block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putHitUpdateBlock(HitUpdateBlock block, GamePacketBuilder builder) { + builder.put(DataType.BYTE, DataTransformation.ADD, block.getDamage()); + builder.put(DataType.BYTE, DataTransformation.NEGATE, block.getType()); + builder.put(DataType.BYTE, DataTransformation.ADD, block.getCurrentHealth()); + builder.put(DataType.BYTE, block.getMaximumHealth()); + } + + /** + * Puts an interacting character block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putInteractingCharacterBlock(InteractingCharacterBlock block, GamePacketBuilder builder) { + builder.put(DataType.SHORT, block.getInteractingCharacterIndex()); + } + + /** + * Puts a movement update for the specified segment. + * + * @param segment The segment. + * @param event The event. + * @param builder The builder. + */ + private void putMovementUpdate(SynchronizationSegment segment, NpcSynchronizationEvent event, + GamePacketBuilder builder) { + boolean updateRequired = segment.getBlockSet().size() > 0; + if (segment.getType() == SegmentType.RUN) { + Direction[] directions = ((MovementSegment) segment).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 (segment.getType() == SegmentType.WALK) { + Direction[] directions = ((MovementSegment) segment).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); + } + } + } + + /** + * Puts a remove character update. + * + * @param builder The builder. + */ + private void putRemoveCharacterUpdate(GamePacketBuilder builder) { + builder.putBits(1, 1); + builder.putBits(2, 3); + } + + /** + * Puts a second hit update block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putSecondHitUpdateBlock(SecondHitUpdateBlock block, GamePacketBuilder builder) { + builder.put(DataType.BYTE, DataTransformation.NEGATE, block.getDamage()); + builder.put(DataType.BYTE, DataTransformation.SUBTRACT, block.getType()); + builder.put(DataType.BYTE, DataTransformation.SUBTRACT, block.getCurrentHealth()); + builder.put(DataType.BYTE, DataTransformation.NEGATE, block.getMaximumHealth()); + } + + /** + * Puts a transform block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putTransformBlock(TransformBlock block, GamePacketBuilder builder) { + builder.put(DataType.SHORT, DataOrder.LITTLE, DataTransformation.ADD, block.getId()); + } + + /** + * 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); + } + +} \ No newline at end of file diff --git a/src/org/apollo/net/release/r317/PlayerSynchronizationEventEncoder.java b/src/org/apollo/net/release/r317/PlayerSynchronizationEventEncoder.java index 676373b1..179e81f9 100644 --- a/src/org/apollo/net/release/r317/PlayerSynchronizationEventEncoder.java +++ b/src/org/apollo/net/release/r317/PlayerSynchronizationEventEncoder.java @@ -22,7 +22,7 @@ import org.apollo.game.sync.block.InteractingCharacterBlock; import org.apollo.game.sync.block.SecondHitUpdateBlock; 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.AddPlayerSegment; import org.apollo.game.sync.seg.MovementSegment; import org.apollo.game.sync.seg.SegmentType; import org.apollo.game.sync.seg.SynchronizationSegment; @@ -60,7 +60,7 @@ public final class PlayerSynchronizationEventEncoder extends EventEncoder 0; Position player = event.getPosition(); diff --git a/src/org/apollo/net/release/r317/Release317.java b/src/org/apollo/net/release/r317/Release317.java index 8ae65b1d..55f11bd7 100644 --- a/src/org/apollo/net/release/r317/Release317.java +++ b/src/org/apollo/net/release/r317/Release317.java @@ -4,6 +4,7 @@ import org.apollo.game.event.impl.CloseInterfaceEvent; import org.apollo.game.event.impl.EnterAmountEvent; import org.apollo.game.event.impl.IdAssignmentEvent; import org.apollo.game.event.impl.LogoutEvent; +import org.apollo.game.event.impl.NpcSynchronizationEvent; import org.apollo.game.event.impl.OpenInterfaceEvent; import org.apollo.game.event.impl.OpenInterfaceSidebarEvent; import org.apollo.game.event.impl.PlayerSynchronizationEvent; @@ -105,6 +106,7 @@ public final class Release317 extends Release { register(OpenInterfaceSidebarEvent.class, new OpenInterfaceSidebarEventEncoder()); register(EnterAmountEvent.class, new EnterAmountEventEncoder()); register(SetInterfaceTextEvent.class, new SetInterfaceTextEventEncoder()); + register(NpcSynchronizationEvent.class, new NpcSynchronizationEventEncoder()); } } \ No newline at end of file diff --git a/src/org/apollo/net/release/r377/NpcSynchronizationEventEncoder.java b/src/org/apollo/net/release/r377/NpcSynchronizationEventEncoder.java new file mode 100644 index 00000000..0c32ddda --- /dev/null +++ b/src/org/apollo/net/release/r377/NpcSynchronizationEventEncoder.java @@ -0,0 +1,301 @@ +package org.apollo.net.release.r377; + +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.ForceChatBlock; +import org.apollo.game.sync.block.GraphicBlock; +import org.apollo.game.sync.block.HitUpdateBlock; +import org.apollo.game.sync.block.InteractingCharacterBlock; +import org.apollo.game.sync.block.SecondHitUpdateBlock; +import org.apollo.game.sync.block.SynchronizationBlockSet; +import org.apollo.game.sync.block.TransformBlock; +import org.apollo.game.sync.block.TurnToPositionBlock; +import org.apollo.game.sync.seg.AddNpcSegment; +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.DataTransformation; +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; + +/** + * An {@link EventEncoder} for the {@link NpcSynchronizationEvent}. + * + * @author Major + */ +public class NpcSynchronizationEventEncoder extends EventEncoder { + + @Override + public GamePacket encode(NpcSynchronizationEvent event) { + GamePacketBuilder builder = new GamePacketBuilder(71, PacketType.VARIABLE_SHORT); + builder.switchToBitAccess(); + + GamePacketBuilder blockBuilder = new GamePacketBuilder(); + + builder.putBits(8, event.getLocalNpcCount()); + + for (SynchronizationSegment segment : event.getSegments()) { + SegmentType type = segment.getType(); + if (type == SegmentType.REMOVE_CHARACTER) { + putRemoveCharacterUpdate(builder); + } else if (type == SegmentType.ADD_CHARACTER) { + putAddNpcUpdate((AddNpcSegment) segment, event, builder); + putBlocks(segment, blockBuilder); + } else { + putMovementUpdate(segment, event, builder); + putBlocks(segment, blockBuilder); + } + } + + if (blockBuilder.getLength() > 0) { + builder.putBits(14, 16383); + builder.switchToByteAccess(); + builder.putRawBuilder(blockBuilder); + } else { + builder.switchToByteAccess(); + } + + return builder.toGamePacket(); + } + + /** + * Puts an add character update. + * + * @param seg The segment. + * @param event The event. + * @param builder The builder. + */ + private void putAddNpcUpdate(AddNpcSegment 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(1, updateRequired ? 1 : 0); + builder.putBits(5, other.getY() - npc.getY()); // these might be + builder.putBits(5, other.getX() - npc.getX()); // the wrong way around + builder.putBits(1, 0); // discard walking queue + builder.putBits(13, seg.getNpcId()); + } + + /** + * 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, animation.getId()); + blockBuilder.put(DataType.BYTE, DataTransformation.SUBTRACT, animation.getDelay()); + } + + /** + * Puts the blocks for the specified segment. + * + * @param segment The segment. + * @param blockBuilder The block builder. + */ + private void putBlocks(SynchronizationSegment segment, GamePacketBuilder blockBuilder) { + SynchronizationBlockSet blockSet = segment.getBlockSet(); + if (blockSet.size() > 0) { + int mask = 0; + + if (blockSet.contains(TransformBlock.class)) { + mask |= 0x1; + } + + if (blockSet.contains(InteractingCharacterBlock.class)) { + mask |= 0x40; + } + + if (blockSet.contains(HitUpdateBlock.class)) { + mask |= 0x80; + } + + if (blockSet.contains(GraphicBlock.class)) { + mask |= 0x4; + } + + if (blockSet.contains(ForceChatBlock.class)) { + mask |= 0x20; + } + + if (blockSet.contains(TurnToPositionBlock.class)) { + mask |= 0x8; + } + + if (blockSet.contains(AnimationBlock.class)) { + mask |= 0x2; + } + + if (blockSet.contains(SecondHitUpdateBlock.class)) { + mask |= 0x10; + } + + blockBuilder.put(DataType.BYTE, mask); + + if (blockSet.contains(TransformBlock.class)) { + putTransformBlock(blockSet.get(TransformBlock.class), blockBuilder); + } + + if (blockSet.contains(InteractingCharacterBlock.class)) { + putInteractingCharacterBlock(blockSet.get(InteractingCharacterBlock.class), blockBuilder); + } + + if (blockSet.contains(HitUpdateBlock.class)) { + putHitUpdateBlock(blockSet.get(HitUpdateBlock.class), blockBuilder); + } + + if (blockSet.contains(GraphicBlock.class)) { + putGraphicBlock(blockSet.get(GraphicBlock.class), blockBuilder); + } + + if (blockSet.contains(ForceChatBlock.class)) { + putForceChatBlock(blockSet.get(ForceChatBlock.class), blockBuilder); + } + + if (blockSet.contains(TurnToPositionBlock.class)) { + putTurnToPositionBlock(blockSet.get(TurnToPositionBlock.class), blockBuilder); + } + + if (blockSet.contains(AnimationBlock.class)) { + putAnimationBlock(blockSet.get(AnimationBlock.class), blockBuilder); + } + + if (blockSet.contains(SecondHitUpdateBlock.class)) { + putSecondHitUpdateBlock(blockSet.get(SecondHitUpdateBlock.class), blockBuilder); + } + } + } + + /** + * Puts a force chat block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putForceChatBlock(ForceChatBlock block, GamePacketBuilder builder) { + builder.putString(block.getMessage()); + } + + /** + * 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, DataOrder.MIDDLE, graphic.getDelay()); + } + + /** + * Puts a hit update block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putHitUpdateBlock(HitUpdateBlock block, GamePacketBuilder builder) { + builder.put(DataType.BYTE, DataTransformation.ADD, block.getDamage()); + builder.put(DataType.BYTE, DataTransformation.ADD, block.getType()); + builder.put(DataType.BYTE, block.getCurrentHealth()); + builder.put(DataType.BYTE, DataTransformation.SUBTRACT, block.getMaximumHealth()); + } + + /** + * Puts an interacting character block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putInteractingCharacterBlock(InteractingCharacterBlock block, GamePacketBuilder builder) { + builder.put(DataType.SHORT, DataOrder.LITTLE, block.getInteractingCharacterIndex()); + } + + /** + * Puts a movement update for the specified segment. + * + * @param seg The segment. + * @param event The event. + * @param builder The builder. + */ + 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); + } + } + } + + /** + * Puts a remove character update. + * + * @param builder The builder. + */ + private void putRemoveCharacterUpdate(GamePacketBuilder builder) { + builder.putBits(1, 1); + builder.putBits(2, 3); + } + + /** + * Puts a second hit update block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putSecondHitUpdateBlock(SecondHitUpdateBlock block, GamePacketBuilder builder) { + builder.put(DataType.BYTE, DataTransformation.SUBTRACT, block.getDamage()); + builder.put(DataType.BYTE, DataTransformation.SUBTRACT, block.getType()); + builder.put(DataType.BYTE, block.getCurrentHealth()); + builder.put(DataType.BYTE, DataTransformation.NEGATE, block.getMaximumHealth()); + } + + /** + * Puts a transform block into the specified builder. + * + * @param block The block. + * @param builder The builder. + */ + private void putTransformBlock(TransformBlock block, GamePacketBuilder builder) { + builder.put(DataType.SHORT, DataTransformation.ADD, block.getId()); + } + + /** + * 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, DataTransformation.ADD, pos.getX() * 2 + 1); + blockBuilder.put(DataType.SHORT, DataOrder.LITTLE, pos.getY() * 2 + 1); + } + +} \ No newline at end of file diff --git a/src/org/apollo/net/release/r377/PlayerSynchronizationEventEncoder.java b/src/org/apollo/net/release/r377/PlayerSynchronizationEventEncoder.java index a245193a..5b7a9103 100644 --- a/src/org/apollo/net/release/r377/PlayerSynchronizationEventEncoder.java +++ b/src/org/apollo/net/release/r377/PlayerSynchronizationEventEncoder.java @@ -22,7 +22,7 @@ import org.apollo.game.sync.block.InteractingCharacterBlock; import org.apollo.game.sync.block.SecondHitUpdateBlock; 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.AddPlayerSegment; import org.apollo.game.sync.seg.MovementSegment; import org.apollo.game.sync.seg.SegmentType; import org.apollo.game.sync.seg.SynchronizationSegment; @@ -60,7 +60,7 @@ public final class PlayerSynchronizationEventEncoder extends EventEncoder 0; Position player = event.getPosition(); diff --git a/src/org/apollo/net/release/r377/Release377.java b/src/org/apollo/net/release/r377/Release377.java index 425910ec..9b70a264 100644 --- a/src/org/apollo/net/release/r377/Release377.java +++ b/src/org/apollo/net/release/r377/Release377.java @@ -4,6 +4,7 @@ import org.apollo.game.event.impl.CloseInterfaceEvent; import org.apollo.game.event.impl.EnterAmountEvent; import org.apollo.game.event.impl.IdAssignmentEvent; import org.apollo.game.event.impl.LogoutEvent; +import org.apollo.game.event.impl.NpcSynchronizationEvent; import org.apollo.game.event.impl.OpenInterfaceEvent; import org.apollo.game.event.impl.OpenInterfaceSidebarEvent; import org.apollo.game.event.impl.PlayerSynchronizationEvent; @@ -105,6 +106,7 @@ public final class Release377 extends Release { register(OpenInterfaceSidebarEvent.class, new OpenInterfaceSidebarEventEncoder()); register(EnterAmountEvent.class, new EnterAmountEventEncoder()); register(SetInterfaceTextEvent.class, new SetInterfaceTextEventEncoder()); + register(NpcSynchronizationEvent.class, new NpcSynchronizationEventEncoder()); } }