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