Add npc synchronization and spawning plugin.

This commit is contained in:
Major-
2013-11-03 04:49:00 +00:00
parent 918c356e40
commit 1d05097869
22 changed files with 1749 additions and 596 deletions
+14
View File
@@ -0,0 +1,14 @@
<?xml version="1.0"?>
<plugin>
<id>cmd-npc</id>
<version>1</version>
<name>Npc Commands</name>
<description>Adds npc-related commands.</description>
<authors>
<author>Major</author>
</authors>
<scripts>
<script>spawn.rb</script>
</scripts>
<dependencies />
</plugin>
+70
View File
@@ -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
@@ -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<SynchronizationSegment> 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<SynchronizationSegment> 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<SynchronizationSegment> getSegments() {
return segments;
}
}
+242 -228
View File
@@ -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<Npc> localNpcs = new ArrayList<Npc>();
/**
* A list of local players.
*/
private final List<Player> localPlayers = new ArrayList<Player>();
/**
* 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<Player> localPlayers = new ArrayList<Player>(); // 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<Player> 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<Npc> getLocalNpcList() {
return localNpcs;
}
/**
* Gets the local player list.
*
* @return The local player list.
*/
public List<Player> 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;
}
}
+298 -275
View File
@@ -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<Event> queuedEvents = new ArrayDeque<Event>();
/**
* 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<Event> queuedEvents = new ArrayDeque<Event>();
/**
* 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 + "]";
}
}
+120 -77
View File
@@ -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<Npc> npcRepository = new CharacterRepository<Npc>(WorldConstants.MAXIMUM_NPCS);
/**
* The {@link CharacterRepository} of {@link Player}s.
@@ -96,6 +92,15 @@ public final class World {
private final CharacterRepository<Player> playerRepository = new CharacterRepository<Player>(
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<Npc> 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!
* <p>
* 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<Player> 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!
* <p>
* 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<Player> 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;
}
}
@@ -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() {
}
}
}
@@ -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<Player> players = World.getWorld().getPlayerRepository();
CharacterRepository<Npc> 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();
}
}
}
@@ -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<Player> players = World.getWorld().getPlayerRepository();
CharacterRepository<Npc> 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();
}
}
}
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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;
@@ -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<Npc> localNpcs = player.getLocalNpcList();
int oldLocalNpcs = localNpcs.size();
List<SynchronizationSegment> segments = new ArrayList<SynchronizationSegment>();
for (Iterator<Npc> 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<Npc> 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);
}
}
@@ -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()));
}
}
@@ -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();
}
}
@@ -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();
}
}
@@ -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<NpcSynchronizationEvent> {
@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);
}
}
@@ -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<Player
if (type == SegmentType.REMOVE_CHARACTER) {
putRemoveCharacterUpdate(builder);
} else if (type == SegmentType.ADD_CHARACTER) {
putAddCharacterUpdate((AddCharacterSegment) segment, event, builder);
putAddCharacterUpdate((AddPlayerSegment) segment, event, builder);
putBlocks(segment, blockBuilder);
} else {
putMovementUpdate(segment, event, builder);
@@ -86,7 +86,7 @@ public final class PlayerSynchronizationEventEncoder extends EventEncoder<Player
* @param event The event.
* @param builder The builder.
*/
private void putAddCharacterUpdate(AddCharacterSegment seg, PlayerSynchronizationEvent event,
private void putAddCharacterUpdate(AddPlayerSegment seg, PlayerSynchronizationEvent event,
GamePacketBuilder builder) {
boolean updateRequired = seg.getBlockSet().size() > 0;
Position player = event.getPosition();
@@ -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());
}
}
@@ -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<NpcSynchronizationEvent> {
@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);
}
}
@@ -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<Player
if (type == SegmentType.REMOVE_CHARACTER) {
putRemoveCharacterUpdate(builder);
} else if (type == SegmentType.ADD_CHARACTER) {
putAddCharacterUpdate((AddCharacterSegment) segment, event, builder);
putAddCharacterUpdate((AddPlayerSegment) segment, event, builder);
putBlocks(segment, blockBuilder);
} else {
putMovementUpdate(segment, event, builder);
@@ -86,7 +86,7 @@ public final class PlayerSynchronizationEventEncoder extends EventEncoder<Player
* @param event The event.
* @param builder The builder.
*/
private void putAddCharacterUpdate(AddCharacterSegment seg, PlayerSynchronizationEvent event,
private void putAddCharacterUpdate(AddPlayerSegment seg, PlayerSynchronizationEvent event,
GamePacketBuilder builder) {
boolean updateRequired = seg.getBlockSet().size() > 0;
Position player = event.getPosition();
@@ -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());
}
}