RuneHive-Tarnish
Neural OSRS Enhancement Framework
Loading...
Searching...
No Matches
SendPlayerUpdate.java
1package com.osroyale.net.packet.out;
2
3import com.osroyale.game.Animation;
4import com.osroyale.game.Graphic;
5import com.osroyale.game.world.World;
6import com.osroyale.game.world.entity.combat.hit.Hit;
7import com.osroyale.game.world.entity.combat.hit.Hitsplat;
8import com.osroyale.game.world.entity.mob.Direction;
9import com.osroyale.game.world.entity.mob.Mob;
10import com.osroyale.game.world.entity.mob.UpdateFlag;
11import com.osroyale.game.world.entity.mob.Viewport;
12import com.osroyale.game.world.entity.mob.player.ForceMovement;
13import com.osroyale.game.world.entity.mob.player.Player;
14import com.osroyale.game.world.entity.mob.player.appearance.Gender;
15import com.osroyale.game.world.entity.mob.player.relations.ChatMessage;
16import com.osroyale.game.world.items.Item;
17import com.osroyale.game.world.items.containers.equipment.Equipment;
18import com.osroyale.game.world.items.containers.equipment.EquipmentType;
19import com.osroyale.game.world.position.Position;
20import com.osroyale.net.codec.AccessType;
21import com.osroyale.net.codec.ByteModification;
22import com.osroyale.net.codec.ByteOrder;
23import com.osroyale.net.packet.OutgoingPacket;
24import com.osroyale.net.packet.PacketBuilder;
25import com.osroyale.net.packet.PacketType;
26import com.osroyale.util.Utility;
27import org.apache.logging.log4j.LogManager;
28import org.apache.logging.log4j.Logger;
29
30import java.util.Collection;
31import java.util.EnumSet;
32import java.util.Iterator;
33
69
70public final class SendPlayerUpdate extends OutgoingPacket {
71
72 private static final Logger logger = LogManager.getLogger(SendPlayerUpdate.class);
73
74 public SendPlayerUpdate() {
75 super(81, PacketType.VAR_SHORT);
76 }
77
78 @Override
79 public boolean encode(Player player) {
80 if (player.regionChange) {
81 player.send(new SendMapRegion());
82 }
83
84 final PacketBuilder blockBuf = PacketBuilder.alloc();
85 try {
86 builder.initializeAccess(AccessType.BIT);
87
88 updateMovement(player, builder);
89
90 if (player.isUpdateRequired()) {
91 updatePlayer(blockBuf, player, player, UpdateState.UPDATE_SELF);
92 }
93
94 final Collection<Player> localPlayers = World.getRegions().getLocalPlayers(player);
95
96 builder.writeBits(8, player.viewport.getPlayersInViewport().size());
97
98 for (Iterator<Player> itr = player.viewport.getPlayersInViewport().iterator(); itr.hasNext();) {
99 Player other = itr.next();
100
101 if (player.viewport.shouldRemove(other)) {
102 builder.writeBit(true);
103 builder.writeBits(2, 3);
104 itr.remove();
105 } else {
106 updateMovement(other, builder);
107
108 if (other.isUpdateRequired()) {
109 updatePlayer(blockBuf, player, other, UpdateState.UPDATE_LOCAL);
110 }
111 }
112
113 }
114
115 int added = 0;
116
117 for (Player localPlayer : localPlayers) {
118
119 if (player.viewport.getPlayersInViewport().size() >= Viewport.CAPACITY || added >= Viewport.ADD_THRESHOLD) {
120 break;
121 }
122
123 if (player.viewport.add(localPlayer)) {
124 added++;
125 addNewPlayer(builder, player, localPlayer);
126 updatePlayer(blockBuf, player, localPlayer, UpdateState.ADD_LOCAL);
127 }
128
129 }
130
131 if (blockBuf.content().readableBytes() > 0) {
132 builder.writeBits(11, 2047);
133 builder.initializeAccess(AccessType.BYTE);
134 builder.writeBuffer(blockBuf.content());
135 } else {
136 builder.initializeAccess(AccessType.BYTE);
137 }
138 } catch (Exception ex) {
139 logger.error(String.format("error updating player=%s", player), ex);
140 } finally {
141 blockBuf.release();
142 }
143 return true;
144 }
145
146 private static void addNewPlayer(PacketBuilder packetBuf, Player player, Player other) {
147 packetBuf.writeBits(11, other.getIndex())
148 .writeBit(true) // isUpdateRequired
149 .writeBit(true) // discardWalkingQueue
150 .writeBits(5, other.getY() - player.getY())
151 .writeBits(5, other.getX() - player.getX());
152 }
153
154 private static void updatePlayer(PacketBuilder blockBuf, Player player, Player other, UpdateState state) {
155 if (!other.isUpdateRequired()) {
156 return;
157 }
158
159 int mask = 0;
160
161 final EnumSet<UpdateFlag> updateFlags = other.updateFlags;
162 for (UpdateFlag flag : UpdateFlag.playerOrder) {
163 if (updateFlags.contains(flag) && flag.canApply(player, other, state)) {
164 mask |= flag.playerMask;
165 }
166 }
167
168 if (mask >= 0x100) {
169 mask |= 0x40;
170 blockBuf.writeByte(mask & 0xFF);
171 blockBuf.writeByte(mask >> 8);
172 } else {
173 blockBuf.writeByte(mask);
174 }
175
176 // TODO replace the checks.
177 /* for (UpdateFlag flag : UpdateFlag.playerOrder) {
178 if (UpdateFlag.containsPlayer(mask, flag)) {
179
180 }
181 }*/
182
183 if (UpdateFlag.containsPlayer(mask, UpdateFlag.FORCE_MOVEMENT)) {
184 appendForceMovementMask(player, other, blockBuf);
185 }
186
187 if (UpdateFlag.containsPlayer(mask, UpdateFlag.GRAPHICS)) {
188 appendGraphicMask(other, blockBuf);
189 }
190
191 if (UpdateFlag.containsPlayer(mask, UpdateFlag.ANIMATION)) {
192 appendAnimationMask(other, blockBuf);
193 }
194
195 if (UpdateFlag.containsPlayer(mask, UpdateFlag.FORCED_CHAT)) {
196 appendForceChatMask(other, blockBuf);
197 }
198
199 if (UpdateFlag.containsPlayer(mask, UpdateFlag.CHAT)) {
200 appendChatMask(blockBuf, other);
201 }
202
203 if (UpdateFlag.containsPlayer(mask, UpdateFlag.INTERACT)) {
204 appendFaceEntityMask(other, blockBuf);
205 }
206
207 if (UpdateFlag.containsPlayer(mask, UpdateFlag.APPEARANCE)) {
208 appendAppearanceMask(other, blockBuf);
209 }
210
211 if (UpdateFlag.containsPlayer(mask, UpdateFlag.FACE_COORDINATE)) {
212 appendFaceCoordinteMask(other, blockBuf);
213 }
214
215 if (UpdateFlag.containsPlayer(mask, UpdateFlag.FIRST_HIT)) {
216 appendHitMask(other, blockBuf);
217 }
218
219 if (UpdateFlag.containsPlayer(mask, UpdateFlag.SECOND_HIT)) {
220 appendSecondHitMask(other, blockBuf);
221 }
222
223 }
224
225 private static void appendForceMovementMask(Player player, Player other, PacketBuilder blockBuf) {
226 final ForceMovement fm = other.getForceMovement();
227
228 final Position lastPosition = player.lastPosition;
229 final Position otherPosition = other.getPosition();
230 final int startX = otherPosition.getLocalX(lastPosition);
231 final int startY = otherPosition.getLocalY(lastPosition);
232
233 final Position end = fm.getEnd();
234 final int endX = end.getX();
235 final int endY = end.getY();
236
237 blockBuf.writeByte(startX, ByteModification.SUB)
238 .writeByte(startY, ByteModification.SUB)
239 .writeByte(startX + endX, ByteModification.SUB)
240 .writeByte(startY + endY, ByteModification.SUB)
241 .writeShort(fm.getSpeed(), ByteModification.ADD, ByteOrder.LE)
242 .writeShort(fm.getReverseSpeed(), ByteModification.ADD)
243 .writeByte(fm.getDirection(), ByteModification.SUB);
244 }
245
246 private static void appendForceChatMask(Player other, PacketBuilder blockBuf) {
247 blockBuf.writeString(other.forceChat);
248 }
249
250 private static void appendFaceEntityMask(Player other, PacketBuilder blockBuf) {
251 Mob mob = other.interactingWith;
252 if (mob != null) {
253 int index = mob.getIndex();
254 if (mob.isPlayer()) {
255 index += -32768;
256 }
257 blockBuf.writeShort(index, ByteOrder.LE);
258 } else {
259 blockBuf.writeShort(65535, ByteOrder.LE);
260 }
261 }
262
263 private static void appendFaceCoordinteMask(Player other, PacketBuilder blockBuf) {
264 Position loc = other.facePosition;
265
266 if (loc == null) {
267 Position currentPos = other.getPosition();
268 Direction currentDir = other.movement.lastDirection;
269 blockBuf.writeShort(((currentPos.getX() + currentDir.getDirectionX()) << 1), ByteModification.ADD, ByteOrder.LE)
270 .writeShort(((currentPos.getY() + currentDir.getDirectionY()) << 1) + 1, ByteOrder.LE);
271 } else {
272 blockBuf.writeShort((loc.getX() << 1) + 1, ByteModification.ADD, ByteOrder.LE)
273 .writeShort((loc.getY() << 1) + 1, ByteOrder.LE);
274 }
275 }
276
277 private static void appendAnimationMask(Player other, PacketBuilder maskBuf) {
278 Animation anim = other.getAnimation().orElse(Animation.RESET);
279 maskBuf.writeShort(anim.getId(), ByteOrder.LE);
280 maskBuf.writeByte(anim.getDelay(), ByteModification.NEG);
281 }
282
283 private static void appendGraphicMask(Player other, PacketBuilder blockBuf) {
284 Graphic graphic = other.getGraphic().orElse(Graphic.RESET);
285 blockBuf.writeShort(graphic.getId(), ByteOrder.LE)
286 .writeInt(graphic.getDelay() | graphic.getHeight());
287 }
288
289 private static void appendChatMask(PacketBuilder blockBuf, Player other) {
290 final ChatMessage message = other.getChatMessage().orElse(ChatMessage.create("Cabbage"));
291
292 final byte[] encoded = message.getEncoded();
293 blockBuf.writeShort(((message.getColor().getCode() & 0xFF) << 8) | (message.getEffect().getCode() & 0xFF), ByteOrder.LE)
294 .writeByte(other.right.getCrown())
295 .writeByte(encoded.length, ByteModification.NEG)
296 .writeBytesReverse(encoded);
297 }
298
299 private static void appendAppearanceMask(Player other, PacketBuilder blockBuf) {
300 final PacketBuilder tempBuf = PacketBuilder.alloc();
301 final var overrides = other.overrides;
302 try {
303 tempBuf.writeByte(other.appearance.getGender().ordinal())
304 .writeByte(other.headIcon)
305 .writeByte(other.skulling.getHeadIconType().getCode())
306 .writeByte(other.valueIcon);
307
308 if (other.id != -1) {
309 tempBuf.writeShort(-1);
310 tempBuf.writeShort(other.id);
311 } else {
312 Item helm = other.equipment.get(Equipment.HEAD_SLOT);
313 if (overrides.hasOverride(Equipment.HEAD_SLOT)) {
314 tempBuf.writeShort(0x200 + overrides.get(Equipment.HELM_SLOT).getId());
315 } else if (helm != null && helm.getId() > 1) {
316 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.HELM_SLOT).getId());
317 } else {
318 tempBuf.writeByte(0);
319 }
320
321 if (overrides.hasOverride(Equipment.CAPE_SLOT)) {
322 tempBuf.writeShort(0x200 + overrides.get(Equipment.CAPE_SLOT).getId());
323 } else if (other.equipment.get(Equipment.CAPE_SLOT) != null) {
324 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.CAPE_SLOT).getId());
325 } else {
326 tempBuf.writeByte(0);
327 }
328
329 if (overrides.hasOverride(Equipment.AMULET_SLOT)) {
330 tempBuf.writeShort(0x200 + overrides.get(Equipment.AMULET_SLOT).getId());
331 } else if (other.equipment.get(Equipment.AMULET_SLOT) != null) {
332 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.AMULET_SLOT).getId());
333 } else {
334 tempBuf.writeByte(0);
335 }
336
337 if (overrides.hasOverride(Equipment.WEAPON_SLOT)) {
338 tempBuf.writeShort(0x200 + overrides.get(Equipment.WEAPON_SLOT).getId());
339 } else if (other.equipment.get(Equipment.WEAPON_SLOT) != null) {
340 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.WEAPON_SLOT).getId());
341 } else {
342 tempBuf.writeByte(0);
343 }
344
345 Item torso = other.equipment.get(Equipment.CHEST_SLOT);
346 if (overrides.hasOverride(Equipment.CHEST_SLOT)) {
347 tempBuf.writeShort(0x200 + overrides.get(Equipment.CHEST_SLOT).getId());
348 } else if (torso != null && torso.getId() > 1) {
349 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.CHEST_SLOT).getId());
350 } else {
351 tempBuf.writeShort(0x100 + other.appearance.getTorso());
352 }
353
354 if (overrides.hasOverride(Equipment.SHIELD_SLOT)) {
355 tempBuf.writeShort(0x200 + overrides.get(Equipment.SHIELD_SLOT).getId());
356 } else if (other.equipment.get(Equipment.SHIELD_SLOT) != null) {
357 if (overrides.hasOverride(Equipment.WEAPON_SLOT) && overrides.get(Equipment.WEAPON_SLOT).isTwoHanded()) {
358 tempBuf.writeByte(0);
359 } else {
360 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.SHIELD_SLOT).getId());
361 }
362 } else {
363 tempBuf.writeByte(0);
364 }
365
366 if (torso != null && torso.getId() > 1 && torso.getEquipmentType().equals(EquipmentType.BODY) ||
367 overrides.hasOverride(Equipment.CHEST_SLOT) && overrides.get(Equipment.CHEST_SLOT).getEquipmentType().equals(EquipmentType.BODY)) {
368 tempBuf.writeByte(0);
369 } else {
370 tempBuf.writeShort(0x100 + other.appearance.getArms());
371 }
372
373 if (overrides.hasOverride(Equipment.LEGS_SLOT)) {
374 tempBuf.writeShort(0x200 + overrides.get(Equipment.LEGS_SLOT).getId());
375 } else if (other.equipment.get(Equipment.LEGS_SLOT) != null) {
376 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.LEGS_SLOT).getId());
377 } else {
378 tempBuf.writeShort(0x100 + other.appearance.getLegs());
379 }
380
381 boolean head = true;
382 boolean beard = true;
383
384 if (helm != null && helm.getId() > 1) {
385 EquipmentType type = helm.getEquipmentType();
386 head = type.equals(EquipmentType.MASK) || type.equals(EquipmentType.HAT);
387 beard = type.equals(EquipmentType.FACE) || type.equals(EquipmentType.HAT);
388 }
389
390 if (overrides.hasOverride(Equipment.HEAD_SLOT)) {
391 final var override = overrides.get(Equipment.HEAD_SLOT).getEquipmentType();
392 head = override.equals(EquipmentType.MASK) || override.equals(EquipmentType.HAT);
393 beard = override.equals(EquipmentType.FACE) || override.equals(EquipmentType.HAT);
394 }
395
396 if (head) {
397 tempBuf.writeShort(0x100 + other.appearance.getHead());
398 } else {
399 tempBuf.writeByte(0);
400 }
401
402 if (overrides.hasOverride(Equipment.HANDS_SLOT)) {
403 tempBuf.writeShort(0x200 + overrides.get(Equipment.HANDS_SLOT).getId());
404 } else if (other.equipment.get(Equipment.HANDS_SLOT) != null) {
405 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.HANDS_SLOT).getId());
406 } else {
407 tempBuf.writeShort(0x100 + other.appearance.getHands());
408 }
409
410 if (overrides.hasOverride(Equipment.FEET_SLOT)) {
411 tempBuf.writeShort(0x200 + overrides.get(Equipment.FEET_SLOT).getId());
412 } else if (other.equipment.get(Equipment.FEET_SLOT) != null) {
413 tempBuf.writeShort(0x200 + other.equipment.get(Equipment.FEET_SLOT).getId());
414 } else {
415 tempBuf.writeShort(0x100 + other.appearance.getFeet());
416 }
417
418 if (other.appearance.getGender().equals(Gender.MALE)) {
419 if (beard) {
420 tempBuf.writeShort(0x100 + other.appearance.getBeard());
421 } else {
422 tempBuf.writeByte(0);
423 }
424 } else {
425 tempBuf.writeByte(0);
426 }
427 }
428
429 tempBuf
430 .writeByte(other.appearance.getHairColor())
431 .writeByte(other.appearance.getTorsoColor())
432 .writeByte(other.appearance.getLegsColor())
433 .writeByte(other.appearance.getFeetColor())
434 .writeByte(other.appearance.getSkinColor())
435 .writeShort(other.mobAnimation.getStand())
436 .writeShort(other.mobAnimation.getTurn())
437 .writeShort(other.mobAnimation.getWalk())
438 .writeShort(other.mobAnimation.getTurn180())
439 .writeShort(other.mobAnimation.getTurn90CW())
440 .writeShort(other.mobAnimation.getTurn90CCW())
441 .writeShort(other.mobAnimation.getRun())
442 .writeLong(Utility.hash(other.getName()))
443 .writeString(other.playerTitle.getTitle())
444 .writeInt(other.playerTitle.getColor())
445 .writeString(other.clanChannel == null ? "" : other.clanChannel.getOwner())
446 .writeString(other.clanTag)
447 .writeString(other.clanTagColor)
448 .writeLong(Double.doubleToLongBits(other.skills.getCombatLevel()))
449 .writeByte(other.right.getCrown())
450 .writeShort(0);
451 blockBuf.writeByte(tempBuf.content().writerIndex(), ByteModification.NEG);
452 blockBuf.writeBytes(tempBuf.content());
453 } finally {
454 tempBuf.release();
455 }
456 }
457
458 private static void appendHitMask(final Player player, final PacketBuilder blockBuf) {
459 Hit hit = player.firstHit;
460
461 boolean multipleHits = hit.getMultipleHits() != null;
462 blockBuf.writeByte(multipleHits ? 1 : 0);
463
464
465 int max = player.getMaximumHealth() >= 500 ? 200 : 100;
466 int health = player.getCurrentHealth() * max / player.getMaximumHealth();
467 if (health > max) health = max;
468
469 if(multipleHits) {
470 blockBuf.writeByte(hit.getMultipleHits().length);
471
472 for(int index = 0; index < hit.getMultipleHits().length; index++) {
473 Hit currentHit = hit.getMultipleHits()[index];
474
475 int id = currentHit.getHitsplat().getId();
476
477 if (currentHit.getHitsplat() == Hitsplat.NORMAL && currentHit.getDamage() > 0) {
478 id++;
479 }
480
481 blockBuf.writeByte(currentHit.getDamage());
482 blockBuf.writeByte(id, ByteModification.ADD);
483 blockBuf.writeByte(currentHit.getHitIcon().getId());
484 }
485
486 } else {
487
488 int id = hit.getHitsplat().getId();
489
490 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
491 id++;
492 }
493
494 blockBuf.writeByte(hit.getDamage());
495 blockBuf.writeByte(id, ByteModification.ADD);
496 blockBuf.writeByte(hit.getHitIcon().getId());
497 }
498
499 blockBuf.writeByte(health);
500 blockBuf.writeByte(max, ByteModification.NEG);
501 }
502
503 private static void appendSecondHitMask(final Player player, final PacketBuilder blockBuf) {
504 Hit hit = player.secondHit;
505
506 boolean multipleHits = hit.getMultipleHits() != null;
507 blockBuf.writeByte(multipleHits ? 1 : 0);
508
509 int max = player.getMaximumHealth() >= 500 ? 200 : 100;
510 int health = player.getCurrentHealth() * max / player.getMaximumHealth();
511 if (health > max) health = max;
512
513 if(multipleHits) {
514 blockBuf.writeByte(hit.getMultipleHits().length);
515
516 for(int index = 0; index < hit.getMultipleHits().length; index++) {
517 Hit currentHit = hit.getMultipleHits()[index];
518
519 int id = currentHit.getHitsplat().getId();
520
521 if (currentHit.getHitsplat() == Hitsplat.NORMAL && currentHit.getDamage() > 0) {
522 id++;
523 }
524
525 blockBuf.writeByte(currentHit.getDamage());
526 blockBuf.writeByte(id, ByteModification.ADD);
527 blockBuf.writeByte(currentHit.getHitIcon().getId());
528 }
529 } else {
530 int id = hit.getHitsplat().getId();
531
532 if (hit.getHitsplat() == Hitsplat.NORMAL && hit.getDamage() > 0) {
533 id++;
534 }
535
536 blockBuf.writeByte(hit.getDamage());
537 blockBuf.writeByte(id, ByteModification.SUB);
538 blockBuf.writeByte(hit.getHitIcon().getId());
539 }
540
541 blockBuf.writeByte(health);
542 blockBuf.writeByte(max, ByteModification.NEG);
543 }
544
545 private static void updateMovement(Player player, PacketBuilder packetBuf) {
546 final boolean teleported = player.positionChange || player.teleportRegion;
547 final boolean updateRequired = player.isUpdateRequired();
548 if (teleported) {
549 packetBuf.writeBit(true)
550 .writeBits(2, 3)
551 .writeBits(2, player.getHeight())
552 .writeBits(1, player.regionChange ? 0 : 1)
553 .writeBit(updateRequired)
554 .writeBits(7, player.getPosition().getLocalY(player.lastPosition))
555 .writeBits(7, player.getPosition().getLocalX(player.lastPosition));
556 } else if (player.movement.getRunningDirection() != -1) {
557 packetBuf.writeBit(true)
558 .writeBits(2, 2)
559 .writeBits(3, player.movement.getWalkingDirection())
560 .writeBits(3, player.movement.getRunningDirection())
561 .writeBit(player.isUpdateRequired());
562 } else if (player.movement.getWalkingDirection() != -1) {
563 packetBuf.writeBit(true)
564 .writeBits(2, 1)
565 .writeBits(3, player.movement.getWalkingDirection())
566 .writeBit(player.isUpdateRequired());
567 } else {
568 if (updateRequired) {
569 packetBuf.writeBit(true)
570 .writeBits(2, 0);
571 } else {
572 packetBuf.writeBit(false);
573 }
574 }
575 }
576
577public enum UpdateState {
578 UPDATE_SELF,
579 UPDATE_LOCAL,
580 ADD_LOCAL
581 }
582
583}
SkullHeadIconType getHeadIconType()