Thread: [RuneScape 3] How Overhead Icons Work

Page 1 of 3 123 LastLast
Results 1 to 10 of 27
  1. #1 [RuneScape 3] How Overhead Icons Work 
    WVWVWVWVWVWVWVW

    _jordan's Avatar
    Join Date
    Nov 2012
    Posts
    3,046
    Thanks given
    111
    Thanks received
    1,848
    Rep Power
    5000
    I have yet to see anybody in the projects section (besides Catanai, Matrix and myself) able to figure out how overhead icons work in RuneScape 3. Assuming Jagex made an update to change overhead icons when RuneScape 3 went live, we all already know overhead icons were handled in the appearance. When decoding the appearance, the client requests 2 different bytes, one for wilderness icons, the others are prayer related. Here is an example from Matrix 718.

    Code:
    		stream.writeByte(player.hasSkull() ? player.getSkullId() : -1);
    		stream.writeByte(player.getPrayer().getPrayerHeadIcon());
    So, why doesn't RuneScape 3 projects have overhead icons? Simple enough, they changed. And to be honest, I like the new change.

    There are 2 masks used in player updating that look extremely alike...

    Code:
    		if (0 != (i_19_ & 0x80)) {
    			int i_21_ = class465_sub29_sub1.readUnsignedByteA(-1144846717);
    			byte[] is = new byte[i_21_];
    			Class465_Sub29 class465_sub29 = new Class465_Sub29(is);
    			class465_sub29_sub1.method16205(is, 0, i_21_, 1395385706);
    			Class61.aClass465_Sub29Array715[playerIndex] = class465_sub29;
    			class592_sub1_sub1_sub1_sub1.method18379(class465_sub29, -1965287251);
    		}
    
    
    
    		if (0 != (i_19_ & 0x100)) {
    			int i_36_ = class465_sub29_sub1.readUnsignedByteC((byte) -78);
    			byte[] is = new byte[i_36_];
    			Class465_Sub29 class465_sub29 = new Class465_Sub29(is);
    			class465_sub29_sub1.method16205(is, 0, i_36_, -1427150336);
    			Class61.aClass465_Sub29Array722[playerIndex] = class465_sub29;
    			class592_sub1_sub1_sub1_sub1.method18390(class465_sub29, (byte) -49);
    		}
    If you're familiar with your runescape clients, you would recognize the mask, obviously it's the appearance. Atleast, the top one is. Basically, Jagex took it upon themselves to remove the overhead icons from the appearance, and made them into their own separate mask. Thus, why 2 masks look identical.

    The way overhead icons are handled, are in the same fashion you would handle appearance. The decoding for overhead icons look like this..

    Code:
    		builder.writeByte(#);
    		builder.writeByte(#);
    		builder.writeShort(#);
    The first byte sent is still unknown to me, but it is always 4. The second byte sent is the icon id, then the short sent is the sprite index containing the icons. RuneScape takes the icon id sent, and locates that id from inside the sprite id inside the cache. If you dumped all components from sprite 440 inside the cache, you will get every overhead icon in the game. So here is an example of the actual class I used for overhead icons.

    Code:
    	private void sendOverheadIcon(Player player, RS3PacketBuilder builder) {
    		byte[] data = player.getAnimator().getOverheadIcon().getOverheadIconData();
    		sentDataLength += data.length;
    		cachedAppearances[player.getIndex()] = player.getAnimator().getOverheadIcon().getOverheadIconHash();
    		builder.writeByteC(data.length);
    		builder.writeReverseA(data, 0, data.length);
    	}
    Code:
    package org.astro.rs3.game.node.entity.render.action;
    
    import org.astro.rs3.network.gameserver.codec.RS3PacketBuilder;
    import org.astro.utilities.Utilities;
    
    /**
     * @author Jordan Abraham <[email protected]>
     * @date Dec 29, 2014
     */
    public class OverheadIcon {
    
    	/**
    	 * Represents the sprite id that contains the overhead icons.
    	 */
    	public static final int SPRITE_ID = 440;
    
    	/**
    	 * Represents the current prayers icon id of this {@code OverheadIcon}.
    	 */
    	private PrayerIcons prayerIcons;
    
    	/**
    	 * Represents the current curses icon id of this {@code OverheadIcon}.
    	 */
    	private CurseIcons curseIcons;
    
    	/**
    	 * Represents the data hash of the {@code OverheadIcon}.
    	 */
    	private byte[] overheadIconHash;
    
    	/**
    	 * Represents the raw data of the {@code OverheadIcon}.
    	 */
    	private byte[] overheadIconData;
    
    	/**
    	 * Constructs a new {@code OverheadIcon} {@link Object}.
    	 * @param prayerIcons Represents the id of the prayers icon to construct.
    	 */
    	public OverheadIcon(PrayerIcons prayerIcons) {
    		this.prayerIcons = prayerIcons;
    		this.curseIcons = null;
    	}
    
    	/**
    	 * Constructs a new {@code OverheadIcon} {@link Object}.
    	 * @param curseIcons Represents the id of the curses icon to construct.
    	 */
    	public OverheadIcon(CurseIcons curseIcons) {
    		this.curseIcons = curseIcons;
    		this.prayerIcons = null;
    	}
    
    	/**
    	 * Refreshes the {@code OverheadIcon} of the {@link Player}.
    	 * @param iconId Represents the id of the icon to use during refreshing.
    	 */
    	public void refreshOverheadIcon() {
    		RS3PacketBuilder builder = new RS3PacketBuilder();
    		builder.writeByte(4);
    		builder.writeByte(prayerIcons == null ? curseIcons.getIconId() : prayerIcons.getIconId());
    		builder.writeShort(SPRITE_ID);
    		byte[] data = new byte[builder.getOffset()];
    		System.arraycopy(builder.getBuffer(), 0, data, 0, data.length);
    		byte[] hash = Utilities.encrypt(data);
    		overheadIconData = data;
    		overheadIconHash = hash;
    	}
    
    	/**
    	 * Creates a {@code OverheadIcon}.
    	 * @param prayerIcons Represents the created id of the {@code OverheadIcon}.
    	 * @return The {@code OverheadIcon} created.
    	 */
    	public static OverheadIcon create(PrayerIcons prayerIcons) {
    		return new OverheadIcon(prayerIcons);
    	}
    
    	/**
    	 * Creates a {@code OverheadIcon}.
    	 * @param curseIcons Represents the created id of the {@code OverheadIcon}.
    	 * @return The {@code OverheadIcon} created.
    	 */
    	public static OverheadIcon create(CurseIcons curseIcons) {
    		return new OverheadIcon(curseIcons);
    	}
    
    	/**
    	 * Gets and returns the hash of the {@code OverheadIcon}.
    	 * @return the iconHash
    	 */
    	public byte[] getOverheadIconHash() {
    		return overheadIconHash;
    	}
    
    	/**
    	 * Sets the hash of the {@code OverheadIcon}.
    	 * @param iconHash the iconHash to set
    	 */
    	public void setOverheadIconHash(byte[] iconHash) {
    		this.overheadIconHash = iconHash;
    	}
    
    	/**
    	 * Gets and returns the data of the {@code OverheadIcon}.
    	 * @return the iconData
    	 */
    	public byte[] getOverheadIconData() {
    		return overheadIconData;
    	}
    
    	/**
    	 * Sets the data of the {@code OverheadIcon}.
    	 * @param iconData the iconData to set
    	 */
    	public void setOverheadIconData(byte[] iconData) {
    		this.overheadIconData = iconData;
    	}
    
    	/**
    	 * @author Jordan Abraham <[email protected]>
    	 * @date Dec 29, 2014
    	 */
    	public enum PrayerIcons {
    
    		PROTECT_FROM_MELEE(0), PROTECT_FROM_MISSILES(1), PROTECT_FROM_MAGIC(2), RETRIBUTION(3), SMITE(4), REDEMPTION(5), PROTECT_FROM_MAGIC_MISSILES_SUMMONING(6), PROTECT_FROM_SUMMONING(7), PROTECT_FROM_MELEE_SUMMONING(8), PROTECT_FROM_MISSILES_SUMMONING(9), PROTECT_FROM_MAGIC_SUMMONING(10);
    
    		/**
    		 * Represents the id of each {@code Prayers}.
    		 */
    		private int iconId;
    
    		/**
    		 * Constructs a new {@code Icon} {@link Object}.
    		 * @param iconId Represents the id of each {@code Prayers} to construct.
    		 */
    		private PrayerIcons(int iconId) {
    			this.iconId = iconId;
    		}
    
    		/**
    		 * Gets and returns the id of each constructed {@code Prayers}.
    		 * @return the iconId
    		 */
    		public int getIconId() {
    			return iconId;
    		}
    
    	}
    
    	/**
    	 * @author Jordan Abraham <[email protected]>
    	 * @date Dec 29, 2014
    	 */
    	public enum CurseIcons {
    		// 29 is the limit
    		DEFLECT_MELEE(12), DEFLECT_MAGIC(13), DEFLECT_MISSILES(14), DEFLECT_SUMMONING(15), DEFLECT_MELEE_SUMMONING(16), DEFLECT_MISSILES_SUMMONING(17), DEFLECT_MAGIC_SUMMONING(18), WRATH(19), SOUL_SPLIT(20);
    
    		/**
    		 * Represents the id of each {@code Curses}.
    		 */
    		private int iconId;
    
    		/**
    		 * Constructs a new {@code Icon} {@link Object}.
    		 * @param iconId Represents the id of each {@code Curses} to construct.
    		 */
    		private CurseIcons(int iconId) {
    			this.iconId = iconId;
    		}
    
    		/**
    		 * Gets and returns the id of each constructed {@code Curses}.
    		 * @return the iconId
    		 */
    		public int getIconId() {
    			return iconId;
    		}
    
    	}
    
    }


    So that is how overhead icons are used in RuneScape 3. Hope this helped you in some way


    Quote Originally Posted by Zephyrr View Post
    True, also now doesnt have to send all the bytes from appearance to make them appear.

    For anyone wondering how to write headicons on npc :

    Its almost same way,

    Code:
    	    if ((i_6_ & 0x200000) != 0) { // npc prays
    			int i_76_ = class464_sub17_sub2.readA(1223467414);
    			int[] is = new int[8];
    			short[] is_77_ = new short[8];
    			for (int i_78_ = 0; i_78_ < 8; i_78_++) {
    			    if (0 != (i_76_ & 1 << i_78_)) {
    				is[i_78_] = class464_sub17_sub2.method15827(-740808969);
    				is_77_[i_78_] = (short) class464_sub17_sub2.method15636(1516369682);
    			    } else {
    				is[i_78_] = -1;
    				is_77_[i_78_] = (short) -1;
    			    }
    			}
    			class601_sub1_sub3_sub1_sub1.aClass272_11776 = new Class272(is, is_77_);
    	    }
    ^ is for 834 though

    Code:
    		int mask = 0;
    		for(int i = 0; i < npc.getIcons().size(); i++)
    			mask |= 1 << i;
    		stream.putA(mask);
    		for(Icon icon : npc.getIcons()) {
    			stream.putIntSmart(icon.getSpriteId());
    			stream.putSmart(icon.getSpriteIconId());
    		}
    Dont ask me when it came out, Idk.

    Attached image
    Attached image
    Reply With Quote  
     

  2. Thankful users:


  3. #2  
    Registered Member

    Join Date
    Jan 2014
    Posts
    376
    Thanks given
    49
    Thanks received
    111
    Rep Power
    106
    The first byte is a series of bit flags representing the slots to use.
    For example, the value of 0x4 (which is bit 3) would mean that the packed icon should go in head icon slot 3.

    To provide an example, this is the encoding method I use:
    Code:
    int flags = 0;
    for (int slot=0; slot<spriteIDs.length; slot++) {
    	if (spriteIDs[slot] != -1) {
    		flags |= 1 << slot;
    	}
    }
    update.putByte(flags);
    for (int slot=0; slot<spriteIDs.length; slot++) {
    	if (spriteIDs[slot] != -1) {
    		update.putByte(subSpriteIDs[slot]);
    		update.putShort(spriteIDs[slot]);
    	}
    }
    Note that there is a maximum of 8 icons.
    Reply With Quote  
     

  4. #3  
    Reverse Engineering

    freeezr's Avatar
    Join Date
    Dec 2011
    Posts
    1,067
    Thanks given
    288
    Thanks received
    444
    Rep Power
    401
    Quote Originally Posted by _Jordan View Post
    So, why doesn't RuneScape 3 projects have overhead icons? Simple enough, they changed. And to be honest, I like the new change.
    wouldn't really say thats the reason why. none of the projects have needed to utilize it, as most projects stop before combat.

    Quote Originally Posted by _Jordan View Post
    have yet to see anybody in the projects section (besides Catanai, Matrix and myself) able to figure out how overhead icons work in RuneScape 3.
    o and btw, we have had this for a long time. just too lazy to post.


    nonetheless, gj i guess. should help a few that havent look at the update blocks in the client. ;;

    Attached image
    Reply With Quote  
     

  5. #4  
    Registered Member Taylor Moon's Avatar
    Join Date
    Aug 2012
    Posts
    2,565
    Thanks given
    625
    Thanks received
    1,303
    Rep Power
    66
    This is a fairly old change, although technically they still work the same they just moved it out of the appearance mask portion. By the way, like Sundays said, the unknown byte is just extra clarification telling the client where the icon is placed. Which in this case is the head. I'm sure this is because of content related reasons. I haven't played or developed Runescape in a long time so I may be somewhat wrong.
    Reply With Quote  
     

  6. #5  
    Registered Member

    Join Date
    Dec 2012
    Posts
    2,999
    Thanks given
    894
    Thanks received
    921
    Rep Power
    2555
    Code:
    RS3PacketBuilder builder = new RS3PacketBuilder();
    Have the packet buffer / stream classes changed or is that just shit naming
    Attached image
    Reply With Quote  
     

  7. #6  
    Registered Member Taylor Moon's Avatar
    Join Date
    Aug 2012
    Posts
    2,565
    Thanks given
    625
    Thanks received
    1,303
    Rep Power
    66
    Quote Originally Posted by Kaleem View Post
    Code:
    RS3PacketBuilder builder = new RS3PacketBuilder();
    Have the packet buffer / stream classes changed or is that just shit naming
    It's a class that builds the packet before it's sent to the client. Which is RS3. Being specific is always helpful when reading and writing code, especially when you might even have other packet builders or other classes that do the same thing but with different data. I don't know why that would be considered shit, it gives you a direct explanation of what that class is doing.
    Reply With Quote  
     

  8. #7  
    WVWVWVWVWVWVWVW

    _jordan's Avatar
    Join Date
    Nov 2012
    Posts
    3,046
    Thanks given
    111
    Thanks received
    1,848
    Rep Power
    5000
    Quote Originally Posted by Taylor Moon View Post
    This is a fairly old change, although technically they still work the same they just moved it out of the appearance mask portion. By the way, like Sundays said, the unknown byte is just extra clarification telling the client where the icon is placed. Which in this case is the head. I'm sure this is because of content related reasons. I haven't played or developed Runescape in a long time so I may be somewhat wrong.
    Yeah I know it's basically the same thing, just slightly different. I just like the idea that they made it into its own mask.
    Attached image
    Attached image
    Reply With Quote  
     

  9. #8  
    Registered Member

    Join Date
    Dec 2012
    Posts
    2,999
    Thanks given
    894
    Thanks received
    921
    Rep Power
    2555
    Quote Originally Posted by Taylor Moon View Post
    It's a class that builds the packet before it's sent to the client. Which is RS3. Being specific is always helpful when reading and writing code, especially when you might even have other packet builders or other classes that do the same thing but with different data. I don't know why that would be considered shit, it gives you a direct explanation of what that class is doing.
    Didn't answer my question, it being named RS3PacketBuilder implies it would only work on RS3, so naming it PacketBuilder would be better
    Attached image
    Reply With Quote  
     

  10. Thankful user:


  11. #9  
    Registered Member Taylor Moon's Avatar
    Join Date
    Aug 2012
    Posts
    2,565
    Thanks given
    625
    Thanks received
    1,303
    Rep Power
    66
    Quote Originally Posted by Kaleem View Post
    Didn't answer my question, it being named RS3PacketBuilder implies it would only work on RS3, so naming it PacketBuilder would be better
    You can't use RS3 packet builders on an RS2 source lol.

    EDIT: I just realize it wasn't doing what I thought it was. My fault. IO buffer classes are different. I thought it was an encoder reference.
    Reply With Quote  
     

  12. Thankful user:


  13. #10  
    Banned

    Join Date
    Jan 2012
    Age
    25
    Posts
    2,703
    Thanks given
    906
    Thanks received
    630
    Rep Power
    0
    For those who don't know, you haven't explained the old way.
    Reply With Quote  
     

Page 1 of 3 123 LastLast

Thread Information
Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)


User Tag List

Similar Threads

  1. Replies: 5
    Last Post: 09-10-2012, 09:12 PM
  2. Replies: 11
    Last Post: 10-29-2010, 04:22 PM
  3. Replies: 8
    Last Post: 11-01-2009, 05:09 PM
  4. How Do You Work With 000webhost
    By HyreScape in forum Help
    Replies: 6
    Last Post: 01-04-2009, 08:12 PM
  5. How To Add working Recoil
    By Concious in forum Tutorials
    Replies: 41
    Last Post: 03-11-2008, 01:20 AM
Posting Permissions
  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •