RuneHive-Game
Loading...
Searching...
No Matches
SendNpcUpdate.java
Go to the documentation of this file.
1package com.runehive.net.packet.out;
2
3import com.runehive.Config;
4import com.runehive.game.Animation;
5import com.runehive.game.Graphic;
6import com.runehive.game.world.World;
7import com.runehive.game.world.entity.combat.hit.Hit;
8import com.runehive.game.world.entity.combat.hit.Hitsplat;
9import com.runehive.game.world.entity.mob.Direction;
10import com.runehive.game.world.entity.mob.Mob;
11import com.runehive.game.world.entity.mob.UpdateFlag;
12import com.runehive.game.world.entity.mob.Viewport;
13import com.runehive.game.world.entity.mob.movement.Movement;
14import com.runehive.game.world.entity.mob.npc.Npc;
15import com.runehive.game.world.entity.mob.player.Player;
16import com.runehive.game.world.position.Position;
17import com.runehive.net.codec.AccessType;
18import com.runehive.net.codec.ByteModification;
19import com.runehive.net.codec.ByteOrder;
20import com.runehive.net.packet.OutgoingPacket;
21import com.runehive.net.packet.PacketBuilder;
22import com.runehive.net.packet.PacketType;
23
24import java.util.Arrays;
25import java.util.EnumSet;
26import java.util.Iterator;
27
28/**
29 * The packet that's responsible for updating npcs.
30 *
31 * @author nshusa
32 */
33public final class SendNpcUpdate extends OutgoingPacket {
34
35 public SendNpcUpdate() {
36 super(65, PacketType.VAR_SHORT);
37 }
38
39 @Override
40 public boolean encode(Player player) {
42 try {
43 builder.initializeAccess(AccessType.BIT);
44 builder.writeBits(8, player.viewport.getNpcsInViewport().size());
45
46 for (Iterator<Npc> itr = player.viewport.getNpcsInViewport().iterator(); itr.hasNext(); ) {
47
48 Npc npc = itr.next();
49
50 if (player.viewport.shouldRemove(npc)) {
51 if (npc.atomicPlayerCount.decrementAndGet() < 0) {
52 npc.atomicPlayerCount.set(0);
53 }
54 itr.remove();
55 builder.writeBits(1, 1);
56 builder.writeBits(2, 3);
57 } else {
59
60 if (npc.isUpdateRequired()) {
61 updateNpc(maskBuf, npc);
62 }
63 }
64
65 }
66
67 int npcsAdded = 0;
68
69 for (Npc localNpc : World.getRegions().getLocalNpcs(player)) {
70
71 if (player.viewport.getNpcsInViewport().size() >= Viewport.CAPACITY || npcsAdded == Viewport.ADD_THRESHOLD) {
72 break;
73 }
74
75 if (player.viewport.add(localNpc)) {
76 npcsAdded++;
77 addNewNpc(builder, player, localNpc);
78 updateNpc(maskBuf, localNpc);
79
80 if (localNpc.atomicPlayerCount.incrementAndGet() < 0) {
81 localNpc.atomicPlayerCount.set(0);
82 }
83 }
84
85 }
86
87 if (maskBuf.content().readableBytes() > 0) {
88 builder.writeBits(16, 65535);
89 builder.initializeAccess(AccessType.BYTE);
90 builder.writeBytes(maskBuf.content());
91 } else {
92 builder.initializeAccess(AccessType.BYTE);
93 }
94
95 } catch (Exception ex) {
96 ex.printStackTrace();
97 } finally {
98 maskBuf.release();
99 }
100 return true;
101 }
102
103 private static void addNewNpc(PacketBuilder packet, Player player, Npc npc) {
104 packet.writeBits(16, npc.getIndex()); // 16 bits needed since server supports 32000 NPCs
105 packet.writeBits(5, npc.getPosition().getY() - player.getPosition().getY());
106 packet.writeBits(5, npc.getPosition().getX() - player.getPosition().getX());
107 packet.writeBit(false); // discard walking queue
108 packet.writeBits(Config.NPC_BITS, npc.id);
109 packet.writeBit(npc.isUpdateRequired());
110 }
111
112 private static void updateMovement(PacketBuilder packet, Npc npc) {
113 final boolean updateRequired = npc.isUpdateRequired();
114
115 final Movement movement = npc.movement;
116 final int runDirection = movement.getRunningDirection();
117 final int walkDirection = movement.getWalkingDirection();
118
119 if (runDirection != -1) {
120 packet.writeBit(true)
121 .writeBits(2, 2)
122 .writeBits(3, walkDirection)
123 .writeBits(3, runDirection)
124 .writeBit(updateRequired);
125 } else if (walkDirection != -1) {
126 packet.writeBit(true)
127 .writeBits(2, 1)
128 .writeBits(3, walkDirection)
129 .writeBit(updateRequired);
130 } else {
131 packet.writeBit(updateRequired);
132 if (updateRequired) {
133 packet.writeBits(2, 0);
134 }
135 }
136 }
137
138 private static void updateNpc(PacketBuilder maskBuf, Npc npc) {
139 if (!npc.isUpdateRequired()) {
140 return;
141 }
142
143 int mask = 0;
144
145 final EnumSet<UpdateFlag> updateFlags = npc.updateFlags;
146 for (UpdateFlag flag : UpdateFlag.npcOrder) {
147 if (updateFlags.contains(flag) && flag.canApply(npc)) {
148 mask |= flag.npcMask;
149 }
150 }
151
152 maskBuf.writeByte(mask);
153
154 // TODO replace the checks.
155 /* for (UpdateFlag flag : UpdateFlag.npcOrder) {
156 if (UpdateFlag.containsNpc(mask, flag)) {
157
158 }
159 }*/
160
162 appendAnimationMask(npc, maskBuf);
163 }
164
166 appendGfxMask(npc, maskBuf);
167 }
168
170 appendFaceEntityMask(npc, maskBuf);
171 }
172
174 appendForceChatMask(npc, maskBuf);
175 }
176
178 appendFirstHitMask(npc, maskBuf);
179 }
180
182 appendSecondHitMask(npc, maskBuf);
183 }
184
186 appendTransformationMask(npc, maskBuf);
187 }
188
190 appendFaceCoordinateMask(npc, maskBuf);
191 }
192
193 }
194
195 private static void appendAnimationMask(Npc npc, PacketBuilder maskBuf) {
196 Animation anim = npc.getAnimation().orElse(Animation.RESET);
197 maskBuf.writeShort(anim.getId(), ByteOrder.LE)
198 .writeByte(anim.getDelay());
199 }
200
201 private static void appendGfxMask(Npc npc, PacketBuilder maskBuf) {
202 Graphic gfx = npc.getGraphic().orElse(Graphic.RESET);
203 maskBuf.writeShort(gfx.getId())
204 .writeInt(gfx.getDelay() | gfx.getHeight());
205 }
206
207 private static void appendFaceEntityMask(Npc npc, PacketBuilder maskBuf) {
208 Mob mob = npc.interactingWith;
209 int index = 65535;
210 if (mob != null) {
211 index = mob.getIndex();
212 if (mob.isPlayer()) {
213 index += 32768;
214 }
215 maskBuf.writeShort(index);
216 } else {
217 maskBuf.writeShort(index);
218 }
219 }
220
221 private static void appendForceChatMask(Npc npc, PacketBuilder maskBuf) {
222 maskBuf.writeString(npc.forceChat);
223 }
224
225 private static void appendFaceCoordinateMask(Npc npc, PacketBuilder maskBuf) {
226 Position loc = npc.facePosition;
227 if (loc == null) {
228 Position currentPos = npc.getPosition();
229 Direction currentDir = npc.movement.lastDirection;
230 maskBuf.writeShort(((currentPos.getX() + currentDir.getDirectionX()) << 1) + 1, ByteOrder.LE)
231 .writeShort(((currentPos.getY() + currentDir.getDirectionY()) << 1) + 1, ByteOrder.LE);
232 } else {
233 maskBuf.writeShort((loc.getX() << 1) + 1, ByteOrder.LE)
234 .writeShort((loc.getY() << 1) + 1, ByteOrder.LE);
235 }
236 }
237
238 private static void appendTransformationMask(Npc npc, PacketBuilder maskBuf) {
240 }
241
242 private static void appendFirstHitMask(final Npc npc, final PacketBuilder updateBlock) {
243 Hit hit = npc.firstHit;
244
245 boolean multipleHits = hit.getMultipleHits() != null;
246 updateBlock.writeByte(multipleHits ? 1 : 0);
247
248 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
249 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
250 if (health > max) health = max;
251
252 if(multipleHits) {
253 System.out.println("total 1 = " + hit.getMultipleHits().length);
254 updateBlock.writeByte(hit.getMultipleHits().length);
255
256 for(int index = 0; index < hit.getMultipleHits().length; index++) {
257 Hit currentHit = hit.getMultipleHits()[index];
258
259 int id = currentHit.getHitsplat().getId();
260
261 if (currentHit.getHitsplat() == Hitsplat.NORMAL && currentHit.getDamage() > 0) {
262 id++;
263 }
264
265 updateBlock.writeByte(currentHit.getDamage());
266 updateBlock.writeByte(id);
267 updateBlock.writeByte(currentHit.getHitIcon().getId());
268 }
269
270 } else {
271
272 int id = hit.getHitsplat().getId();
273
274 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
275 id++;
276 }
277
278 updateBlock.writeByte(hit.getDamage());
279 updateBlock.writeByte(id);
280 updateBlock.writeByte(hit.getHitIcon().getId());
281 }
282
283 updateBlock.writeByte(health);
284 updateBlock.writeByte(max);
285
286 /*int id = hit.getHitsplat().getId();
287 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
288 int health = max * npc.getCurrentHealth() / npc.getMaximumHealth();
289 if (health > max) health = max;
290
291 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
292 id++;
293 }
294
295 updateBlock.writeByte(hit.getDamage());
296 updateBlock.writeByte(id, ByteModification.ADD);
297 updateBlock.writeByte(hit.getHitIcon().getId());
298 updateBlock.writeByte(health);
299 updateBlock.writeByte(max, ByteModification.NEG);*/
300 }
301
302 private static void appendSecondHitMask(final Npc npc, final PacketBuilder updateBlock) {
303 Hit hit = npc.secondHit;
304
305 Hit[] multipleHits = hit.getMultipleHits();
306 boolean isMultipleHits = multipleHits != null;
307 Hit[] multipleHitsArray = isMultipleHits ? Arrays.copyOf(multipleHits, multipleHits.length) : null;
308 updateBlock.writeByte(isMultipleHits ? 1 : 0);
309
310 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
311 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
312 if (health > max) health = max;
313
314 if(isMultipleHits) {
315 updateBlock.writeByte(multipleHitsArray.length);
316 for (Hit currentHit : multipleHitsArray) {
317 int id = currentHit.getHitsplat().getId();
318
319 if (currentHit.getHitsplat() == Hitsplat.NORMAL
320 && currentHit.getDamage() > 0) {
321 id++;
322 }
323
324 updateBlock.writeByte(currentHit.getDamage());
325 updateBlock.writeByte(id);
326 updateBlock.writeByte(currentHit.getHitIcon().getId());
327 }
328 } else {
329
330 int id = hit.getHitsplat().getId();
331
332 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
333 id++;
334 }
335
336 updateBlock.writeByte(hit.getDamage());
337 updateBlock.writeByte(id);
338 updateBlock.writeByte(hit.getHitIcon().getId());
339 }
340
341 updateBlock.writeByte(health);
342 updateBlock.writeByte(max);
343 /*int id = hit.getHitsplat().getId();
344 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
345 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
346 if (health > max) health = max;
347
348 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
349 id++;
350 }
351
352 updateBlock.writeByte(hit.getDamage());
353 updateBlock.writeByte(id, ByteModification.SUB);
354 updateBlock.writeByte(hit.getHitIcon().getId());
355 updateBlock.writeByte(health);
356 updateBlock.writeByte(max, ByteModification.NEG);*/
357 }
358
359}
The class that contains setting-related constants for the server.
Definition Config.java:24
static final int NPC_BITS
The npc bits for the server which can execute 6755 npcs.
Definition Config.java:184
Class that models a single animation used by an entity.
int getId()
Gets the animation id.
static final Animation RESET
int getDelay()
Gets the animation delay.
Represents a single graphic that can be used by entities.
Definition Graphic.java:10
static final Graphic RESET
Definition Graphic.java:17
int getDelay()
Gets the delay of this graphic.
Definition Graphic.java:167
int getId()
Gets the id of this graphic.
Definition Graphic.java:185
int getHeight()
Gets the height of this graphic.
Definition Graphic.java:176
Represents the game world.
Definition World.java:46
static RegionManager getRegions()
Definition World.java:552
A Hit object holds the damage amount and hitsplat data.
Definition Hit.java:10
int getDamage()
Gets the damage amount.
Definition Hit.java:121
Hitsplat getHitsplat()
Gets the damage type.
Definition Hit.java:130
HitIcon getHitIcon()
Gets the hit icon.
Definition Hit.java:139
Handles the mob class.
Definition Mob.java:66
Optional< Graphic > getGraphic()
Definition Mob.java:712
final EnumSet< UpdateFlag > updateFlags
Definition Mob.java:94
boolean isUpdateRequired()
Checks if mob requires an update.
Definition Mob.java:543
Optional< Animation > getAnimation()
Definition Mob.java:708
final boolean isPlayer()
Check if an entity is a player.
Definition Mob.java:564
Represents a viewport in which a Player can see.
Definition Viewport.java:15
boolean add(Mob other)
Adds a Mob to this viewport.
Definition Viewport.java:52
List< Npc > getNpcsInViewport()
The collection of npcs in this viewport.
boolean shouldRemove(Mob other)
Determines if a Mob should be removed from this viewport.
Definition Viewport.java:93
static final int ADD_THRESHOLD
The amount of entities that can be added to this viewport in a single tick.
Definition Viewport.java:18
static final int CAPACITY
The amount of entities that can be visible at all at once 255 players and 255 npcs.
Definition Viewport.java:21
Handles the movement for the player.
Definition Movement.java:25
Direction lastDirection
The last direction the mob walked in.
Definition Movement.java:45
Represents a non-player character in the in-game world.
Definition Npc.java:29
final AtomicInteger atomicPlayerCount
Definition Npc.java:44
This class represents a character controlled by a player.
Definition Player.java:125
Represents a single tile on the game world.
Definition Position.java:14
int getY()
Gets the absolute y coordinate.
Definition Position.java:46
int getX()
Gets the absolute x coordinate.
Definition Position.java:41
List< Npc > getLocalNpcs(Mob mob)
Gets the local npcs around an entity.
OutgoingPacket(int opcode, int capacity)
The implementation that functions as a dynamic buffer wrapper backed by a ByteBuf that is used for re...
static PacketBuilder alloc(int initialCapacity, int maxCapacity)
PacketBuilder writeString(String string)
static void appendAnimationMask(Npc npc, PacketBuilder maskBuf)
static void appendTransformationMask(Npc npc, PacketBuilder maskBuf)
static void updateMovement(PacketBuilder packet, Npc npc)
static void appendGfxMask(Npc npc, PacketBuilder maskBuf)
static void appendSecondHitMask(final Npc npc, final PacketBuilder updateBlock)
static void appendFaceCoordinateMask(Npc npc, PacketBuilder maskBuf)
static void updateNpc(PacketBuilder maskBuf, Npc npc)
static void addNewNpc(PacketBuilder packet, Player player, Npc npc)
static void appendFaceEntityMask(Npc npc, PacketBuilder maskBuf)
static void appendFirstHitMask(final Npc npc, final PacketBuilder updateBlock)
static void appendForceChatMask(Npc npc, PacketBuilder maskBuf)
final int getId()
Gets the identification for this hit type.
Definition HitIcon.java:45
final int getId()
Gets the identification for this hit type.
Definition Hitsplat.java:34
Represents the enumerated directions an entity can walk or face.
boolean canApply(Player player, Player other, SendPlayerUpdate.UpdateState state)
static boolean containsNpc(int masks, UpdateFlag flag)
Represents the different forms data can be written in.
BIT
The type that denotes bits can be written as bytes.
BYTE
the type that denotes bytes can be written directly.
Represents RuneScape's custom value types.
ADD
Adds 128 to the value when written, subtracts 128 from the rarity when read.
Represents the order in which bytes are written.
Definition ByteOrder.java:8
LE
Represents Little-endian.
Represents a type of packet.
VAR_SHORT
A variable packet where the size is indicated by a short.