RuneHive-Tarnish
Neural OSRS Enhancement Framework
Loading...
Searching...
No Matches
SendNpcUpdate.java
1package com.osroyale.net.packet.out;
2
3import com.osroyale.Config;
4import com.osroyale.game.Animation;
5import com.osroyale.game.Graphic;
6import com.osroyale.game.world.World;
7import com.osroyale.game.world.entity.combat.hit.Hit;
8import com.osroyale.game.world.entity.combat.hit.Hitsplat;
9import com.osroyale.game.world.entity.mob.Direction;
10import com.osroyale.game.world.entity.mob.Mob;
11import com.osroyale.game.world.entity.mob.UpdateFlag;
12import com.osroyale.game.world.entity.mob.Viewport;
13import com.osroyale.game.world.entity.mob.movement.Movement;
14import com.osroyale.game.world.entity.mob.npc.Npc;
15import com.osroyale.game.world.entity.mob.player.Player;
16import com.osroyale.game.world.position.Position;
17import com.osroyale.net.codec.AccessType;
18import com.osroyale.net.codec.ByteModification;
19import com.osroyale.net.codec.ByteOrder;
20import com.osroyale.net.packet.OutgoingPacket;
21import com.osroyale.net.packet.PacketBuilder;
22import com.osroyale.net.packet.PacketType;
23
24import java.util.Arrays;
25import java.util.EnumSet;
26import java.util.Iterator;
27
63
64public final class SendNpcUpdate extends OutgoingPacket {
65
66 public SendNpcUpdate() {
67 super(65, PacketType.VAR_SHORT);
68 }
69
70 @Override
71 public boolean encode(Player player) {
72 PacketBuilder maskBuf = PacketBuilder.alloc();
73 try {
74 builder.initializeAccess(AccessType.BIT);
75 builder.writeBits(8, player.viewport.getNpcsInViewport().size());
76
77 for (Iterator<Npc> itr = player.viewport.getNpcsInViewport().iterator(); itr.hasNext(); ) {
78
79 Npc npc = itr.next();
80
81 if (player.viewport.shouldRemove(npc)) {
82 if (npc.atomicPlayerCount.decrementAndGet() < 0) {
83 npc.atomicPlayerCount.set(0);
84 }
85 itr.remove();
86 builder.writeBits(1, 1);
87 builder.writeBits(2, 3);
88 } else {
89 updateMovement(builder, npc);
90
91 if (npc.isUpdateRequired()) {
92 updateNpc(maskBuf, npc);
93 }
94 }
95
96 }
97
98 int npcsAdded = 0;
99
100 for (Npc localNpc : World.getRegions().getLocalNpcs(player)) {
101
102 if (player.viewport.getNpcsInViewport().size() >= Viewport.CAPACITY || npcsAdded == Viewport.ADD_THRESHOLD) {
103 break;
104 }
105
106 if (player.viewport.add(localNpc)) {
107 npcsAdded++;
108 addNewNpc(builder, player, localNpc);
109 updateNpc(maskBuf, localNpc);
110
111 if (localNpc.atomicPlayerCount.incrementAndGet() < 0) {
112 localNpc.atomicPlayerCount.set(0);
113 }
114 }
115
116 }
117
118 if (maskBuf.content().readableBytes() > 0) {
119 builder.writeBits(16, 65535);
120 builder.initializeAccess(AccessType.BYTE);
121 builder.writeBytes(maskBuf.content());
122 } else {
123 builder.initializeAccess(AccessType.BYTE);
124 }
125
126 } catch (Exception ex) {
127 ex.printStackTrace();
128 } finally {
129 maskBuf.release();
130 }
131 return true;
132 }
133
134 private static void addNewNpc(PacketBuilder packet, Player player, Npc npc) {
135 packet.writeBits(16, npc.getIndex()); // 16 bits needed since server supports 32000 NPCs
136 packet.writeBits(5, npc.getPosition().getY() - player.getPosition().getY());
137 packet.writeBits(5, npc.getPosition().getX() - player.getPosition().getX());
138 packet.writeBit(false); // discard walking queue
139 packet.writeBits(Config.NPC_BITS, npc.id);
140 packet.writeBit(npc.isUpdateRequired());
141 }
142
143 private static void updateMovement(PacketBuilder packet, Npc npc) {
144 final boolean updateRequired = npc.isUpdateRequired();
145
146 final Movement movement = npc.movement;
147 final int runDirection = movement.getRunningDirection();
148 final int walkDirection = movement.getWalkingDirection();
149
150 if (runDirection != -1) {
151 packet.writeBit(true)
152 .writeBits(2, 2)
153 .writeBits(3, walkDirection)
154 .writeBits(3, runDirection)
155 .writeBit(updateRequired);
156 } else if (walkDirection != -1) {
157 packet.writeBit(true)
158 .writeBits(2, 1)
159 .writeBits(3, walkDirection)
160 .writeBit(updateRequired);
161 } else {
162 packet.writeBit(updateRequired);
163 if (updateRequired) {
164 packet.writeBits(2, 0);
165 }
166 }
167 }
168
169 private static void updateNpc(PacketBuilder maskBuf, Npc npc) {
170 if (!npc.isUpdateRequired()) {
171 return;
172 }
173
174 int mask = 0;
175
176 final EnumSet<UpdateFlag> updateFlags = npc.updateFlags;
177 for (UpdateFlag flag : UpdateFlag.npcOrder) {
178 if (updateFlags.contains(flag) && flag.canApply(npc)) {
179 mask |= flag.npcMask;
180 }
181 }
182
183 maskBuf.writeByte(mask);
184
185 // TODO replace the checks.
186 /* for (UpdateFlag flag : UpdateFlag.npcOrder) {
187 if (UpdateFlag.containsNpc(mask, flag)) {
188
189 }
190 }*/
191
192 if (UpdateFlag.containsNpc(mask, UpdateFlag.ANIMATION)) {
193 appendAnimationMask(npc, maskBuf);
194 }
195
196 if (UpdateFlag.containsNpc(mask, UpdateFlag.GRAPHICS)) {
197 appendGfxMask(npc, maskBuf);
198 }
199
200 if (UpdateFlag.containsNpc(mask, UpdateFlag.INTERACT)) {
201 appendFaceEntityMask(npc, maskBuf);
202 }
203
204 if (UpdateFlag.containsNpc(mask, UpdateFlag.FORCED_CHAT)) {
205 appendForceChatMask(npc, maskBuf);
206 }
207
208 if (UpdateFlag.containsNpc(mask, UpdateFlag.FIRST_HIT)) {
209 appendFirstHitMask(npc, maskBuf);
210 }
211
212 if (UpdateFlag.containsNpc(mask, UpdateFlag.SECOND_HIT)) {
213 appendSecondHitMask(npc, maskBuf);
214 }
215
216 if (UpdateFlag.containsNpc(mask, UpdateFlag.TRANSFORM)) {
217 appendTransformationMask(npc, maskBuf);
218 }
219
220 if (UpdateFlag.containsNpc(mask, UpdateFlag.FACE_COORDINATE)) {
221 appendFaceCoordinateMask(npc, maskBuf);
222 }
223
224 }
225
226 private static void appendAnimationMask(Npc npc, PacketBuilder maskBuf) {
227 Animation anim = npc.getAnimation().orElse(Animation.RESET);
228 maskBuf.writeShort(anim.getId(), ByteOrder.LE)
229 .writeByte(anim.getDelay());
230 }
231
232 private static void appendGfxMask(Npc npc, PacketBuilder maskBuf) {
233 Graphic gfx = npc.getGraphic().orElse(Graphic.RESET);
234 maskBuf.writeShort(gfx.getId())
235 .writeInt(gfx.getDelay() | gfx.getHeight());
236 }
237
238 private static void appendFaceEntityMask(Npc npc, PacketBuilder maskBuf) {
239 Mob mob = npc.interactingWith;
240 int index = 65535;
241 if (mob != null) {
242 index = mob.getIndex();
243 if (mob.isPlayer()) {
244 index += 32768;
245 }
246 maskBuf.writeShort(index);
247 } else {
248 maskBuf.writeShort(index);
249 }
250 }
251
252 private static void appendForceChatMask(Npc npc, PacketBuilder maskBuf) {
253 maskBuf.writeString(npc.forceChat);
254 }
255
256 private static void appendFaceCoordinateMask(Npc npc, PacketBuilder maskBuf) {
257 Position loc = npc.facePosition;
258 if (loc == null) {
259 Position currentPos = npc.getPosition();
260 Direction currentDir = npc.movement.lastDirection;
261 maskBuf.writeShort(((currentPos.getX() + currentDir.getDirectionX()) << 1) + 1, ByteOrder.LE)
262 .writeShort(((currentPos.getY() + currentDir.getDirectionY()) << 1) + 1, ByteOrder.LE);
263 } else {
264 maskBuf.writeShort((loc.getX() << 1) + 1, ByteOrder.LE)
265 .writeShort((loc.getY() << 1) + 1, ByteOrder.LE);
266 }
267 }
268
269 private static void appendTransformationMask(Npc npc, PacketBuilder maskBuf) {
270 maskBuf.writeShort(npc.id, ByteModification.ADD, ByteOrder.LE);
271 }
272
273 private static void appendFirstHitMask(final Npc npc, final PacketBuilder updateBlock) {
274 Hit hit = npc.firstHit;
275
276 boolean multipleHits = hit.getMultipleHits() != null;
277 updateBlock.writeByte(multipleHits ? 1 : 0);
278
279 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
280 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
281 if (health > max) health = max;
282
283 if(multipleHits) {
284 System.out.println("total 1 = " + hit.getMultipleHits().length);
285 updateBlock.writeByte(hit.getMultipleHits().length);
286
287 for(int index = 0; index < hit.getMultipleHits().length; index++) {
288 Hit currentHit = hit.getMultipleHits()[index];
289
290 int id = currentHit.getHitsplat().getId();
291
292 if (currentHit.getHitsplat() == Hitsplat.NORMAL && currentHit.getDamage() > 0) {
293 id++;
294 }
295
296 updateBlock.writeByte(currentHit.getDamage());
297 updateBlock.writeByte(id);
298 updateBlock.writeByte(currentHit.getHitIcon().getId());
299 }
300
301 } else {
302
303 int id = hit.getHitsplat().getId();
304
305 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
306 id++;
307 }
308
309 updateBlock.writeByte(hit.getDamage());
310 updateBlock.writeByte(id);
311 updateBlock.writeByte(hit.getHitIcon().getId());
312 }
313
314 updateBlock.writeByte(health);
315 updateBlock.writeByte(max);
316
317 /*int id = hit.getHitsplat().getId();
318 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
319 int health = max * npc.getCurrentHealth() / npc.getMaximumHealth();
320 if (health > max) health = max;
321
322 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
323 id++;
324 }
325
326 updateBlock.writeByte(hit.getDamage());
327 updateBlock.writeByte(id, ByteModification.ADD);
328 updateBlock.writeByte(hit.getHitIcon().getId());
329 updateBlock.writeByte(health);
330 updateBlock.writeByte(max, ByteModification.NEG);*/
331 }
332
333 private static void appendSecondHitMask(final Npc npc, final PacketBuilder updateBlock) {
334 Hit hit = npc.secondHit;
335
336 Hit[] multipleHits = hit.getMultipleHits();
337 boolean isMultipleHits = multipleHits != null;
338 Hit[] multipleHitsArray = isMultipleHits ? Arrays.copyOf(multipleHits, multipleHits.length) : null;
339 updateBlock.writeByte(isMultipleHits ? 1 : 0);
340
341 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
342 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
343 if (health > max) health = max;
344
345 if(isMultipleHits) {
346 updateBlock.writeByte(multipleHitsArray.length);
347 for (Hit currentHit : multipleHitsArray) {
348 int id = currentHit.getHitsplat().getId();
349
350 if (currentHit.getHitsplat() == Hitsplat.NORMAL
351 && currentHit.getDamage() > 0) {
352 id++;
353 }
354
355 updateBlock.writeByte(currentHit.getDamage());
356 updateBlock.writeByte(id);
357 updateBlock.writeByte(currentHit.getHitIcon().getId());
358 }
359 } else {
360
361 int id = hit.getHitsplat().getId();
362
363 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
364 id++;
365 }
366
367 updateBlock.writeByte(hit.getDamage());
368 updateBlock.writeByte(id);
369 updateBlock.writeByte(hit.getHitIcon().getId());
370 }
371
372 updateBlock.writeByte(health);
373 updateBlock.writeByte(max);
374 /*int id = hit.getHitsplat().getId();
375 int max = npc.getMaximumHealth() >= 500 ? 200 : 100;
376 int health = npc.getCurrentHealth() * max / npc.getMaximumHealth();
377 if (health > max) health = max;
378
379 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
380 id++;
381 }
382
383 updateBlock.writeByte(hit.getDamage());
384 updateBlock.writeByte(id, ByteModification.SUB);
385 updateBlock.writeByte(hit.getHitIcon().getId());
386 updateBlock.writeByte(health);
387 updateBlock.writeByte(max, ByteModification.NEG);*/
388 }
389
390}
static final int NPC_BITS
Definition Config.java:222