Go Back

Rift - Entity Components

My previous blog basically showed you how I got all the entities from Rift, but it did not show you how to get the needed data from the entities. Thing's like 'what type of entity is it', 'where is it located', etc. In this blog I'll try to explain how I found out how Rift stores this data.

As with my previous blog, I wrote using an open-beta executable. For this part it is a must. The release-executable no longer contains the information that I will be talking about, which will make it a whole lot harder to find out which entity component your dealing with.

When analyzing Rift (when looking at how it handles entity data) you will often see code that looks like this:

v5 = *(_BYTE *)(dword_12055DC + entity + 24);
if ( v5 == -1 )
  v6 = 0;
else
  v6 = *(_DWORD *)(*(_DWORD *)(entity + 88) + 4 * v5);

The dword_12055DC usually varies, but the code is almost always the same. Although you might sometimes see it mixed up in other if-statements (usually it's only the first line of the above block).

Basically every entity in Rift is build up out of several components, the code that is shown gets the entity component address (if the component exists). From what I gather, each component has an id/index. When a component gets added to an entity, it's component index is used to look up the index of the address. Why the component index isn't directly used directly to get the address is a bit of a mystery to me.

The above code however does not get you very far, considering there are likely around 64 possible entity components. To find out what entity component the game is accessing you need an open-beta executable. They removed the string-names (for each component) from the release.

If we look at the cross reference of dword_12055DC, you will notice that there is one line of code that stands out "mov eax, offset dword_12055DC". This line is shared between all entity components. If we go to the function, you will notice that the only thing it does is return the offset. If we cross reference that function, we will notice its used in a v-table.

We're interested in the first function in the v-table, usually (always?) the constructor, to give us a clue on which component it is. The release executable has no info for you there (as said they removed all string references). However the above component had this constructor in the beta:

_UNKNOWN *__cdecl sub_615970()
{
  int v0; // eax@2

  if ( !(dword_1264178 & 1) )
  {
    dword_1264178 |= 1u;
    v0 = sub_5CB980(0);
    sub_A2B6C4((int)&unk_126414C, "ClientEntityComponentNPC", v0);
    atexit(sub_105B3B0);
  }
  return &unk_126414C;
}

Now isn't that lovely, we just got the name of the component. While it doesn't magically give you all the information that you need, it does help you understand what it is used for.

For me, 5 components where of interest:

  • The ClientEntityComponentActor component, it stores health of the entity
  • The ClientEntityComponentPlayer component, it stores the player's name
  • The ClientEntityComponentNPC component, it stores the NPC's name (but only if it is different from it's original name)
  • The ClientEntityComponentMod component, it stores the entities faction
  • The ClientEntityComponentTransform component, it stores the location of the entity

I will not go into detail on all of these components (heck I don't know half of the info that is stored in them), but I will go into two. The Transform component and the Player component.

The Transform component is the easiest of them all. We have to look up the /loc command. Using the same technique used in the previous blog. When looking at the code of SlashLoc, we notice that it uses an entity component (ClientEntityComponentTransform) and right after that it loads a couple of variables from the address.

v3 = *(_BYTE *)(ClientEntityComponentTransform + entity + 24);
if ( v3 == -1 )
  v4 = 0;
else
  v4 = *(_DWORD *)(*(_DWORD *)(entity + 88) + 4 * v3);
v6 = *(_DWORD *)(v4 + 0x3C);
v12 = *(_DWORD *)(v4 + 0x38);
v14 = *(_DWORD *)(v4 + 0x40);

You can probably guess what it loads. As I said, this was the easiest (and I wasn't kidding! :).

The Player component probably stores a lot more then "just the name" but I only needed the name. Unlike the Transform component, the player's name is stored 2 levels deep.

To find the player name we go back to the SlashTarget function from the previous blog, and we go into sub_64A500 again. We look through the function and dive into sub_5E5110 and then dive straight into the only function in there as well. If you look into that function, you will see three components being used. There's a big if-statement, checking if there is a player address or not. We are interested in the first function that gets called when there is a player address (sub_616270).

If you look into that function, the first code line reads as follows (in the o-beta exe) "sub_57B7E0((void *)(*(_DWORD *)(this + 0x2C) + 0xA0), (int)&v3);", and guess what. In the open beta, the pointer at "((playerNameAddress + 0x2C) + 0xA0)" pointed to a unicode string with the player name in it. In the release its 0x2C + 0x180 .

The use of entity components makes it a lot harder to find values that your interested in. I, for example, would love to find out the class of a player. But haven't gotten around to looking at where the game stores it. Health took a while to figure out, I found that using a debugger and simply back-tracing each of the pointer addresses.

For the standings/faction I used "SlashFollow", since you can't follow enemies the game must check the standings in "SlashFollow" (and it does).

I have no code example for this part, but if anyone is interested, I could make an example for it. Leave a comment if you are (or if you have questions).

One small warning, there's some extensive error checking inside Rift, crashing the client (or pausing the debugger too long when performing certain actions) can get you banned!  Be careful!

Posted by: Da_Teach on Monday, March 07, 2011  •  Pointers Rift IDA Pro

  • Facebook
  • Twitter
  • DZone It!
  • Digg It!
  • StumbleUpon
  • Technorati
  • Del.icio.us
  • NewsVine
  • Reddit
  • Blinklist
  • Add diigo bookmark
  • This is great reading.  Admittedly a bit over my head, but good motivation for me to pull "The Art of Assembly Language" and "The IDA Pro Book" back off my shelf and do a little self paced learning.

    Zifnab  •  07 Mar

  • Hey great reads! Thanks a lot, been working on getting better with IDA and this is a great way.

    You said that you had to use a debugger to get health, I fiddled my way around and I keep getting stumped.

    I'm curious if it is a multi-level like name was or what, because the debugging sometimes leaves me with two ptrs a 0x184 and a 0x24.

    Thanks, don't stop posting!

    Chuck  •  09 Mar

  • Health is multi-leveled, you also have 2 "types" of health. I'll look up the pointers tonight. However you can look at the function which uses the "aSethealth" variable. It's in there, but it is a bit hidden (yeay).

    The reason I said you have "2 healths" is because you have the health of each entity. It's updated rather slow (once a second? could be around that), and you have player & target health. Those two are not in the entity, their separate classes. Those are updated more frequently.

    I think there's also group health/mana/etc, but I haven't found those (yet).

    Da_Teach  •  09 Mar

  • I tried to reverse it but I kept getting stuck so I took the area where I was most comfortable and started poking around in IDA

    cmp dword ptr [edx+ebx*4],00

    Lead me to this fuction

      (*(void (__stdcall **)(_DWORD, int))(**(_DWORD **)(v6 + 0x120) + 0x9C))(*(_DWORD *)(v6 + 0x120), a2);
      v10 = v2 + 0x130;
      v9 = 0;
      if ( (*(_DWORD *)(v2 + 0x134) - *(_DWORD *)(v2 + 0x130)) >> 2 )
      {
        do
        {
          if ( *(_DWORD *)(*(_DWORD *)v10 + 4 * v9) )
            (*(void (__thiscall **)(_DWORD))(**(_DWORD **)(*(_DWORD *)v10 + 4 * v9) + 0x24))(*(_DWORD *)(*(_DWORD *)v10 + 4 * v9));
          ++v9;
        }
        while ( v9 < (*(_DWORD *)(v2 + 0x134) - *(_DWORD *)(v2 + 0x130)) >> 2 );
      }
      sub_798840(*(_DWORD *)(v2 + 0x128));

    No dice yet as far as figuring out to how it works. I'll keep at it, just waiting for you to give a hint or something ^_^

    Chuck  •  10 Mar

  • Nice.Is it possible to upload that beta binary somewhere?

    Thanks.

    Wrunning  •  20 Mar

  • [Quote]
    In the open beta, the pointer at "((playerNameAddress + 0x2C) + 0xA0)" pointed to a unicode string with the player name in it. In the release its 0x2C + 0x180 .
    [/Quote]


    the this pointer should be called again "playerComponentAddress" here to avoid confusion.

    .text:01535590 mov     ecx, [ecx+2Ch]
    .text:01535593 sub     esp, 1Ch
    .text:01535596 push    esi
    .text:01535597 lea     eax, [esp+20h+var_1C]
    .text:0153559B push    eax
    .text:0153559C add     ecx, 188h
    .text:015355A2 call    sub_149B5D0

    ext. version 1.0.0
    Built: Mar 23 2011 23:51:25 RWC-BUILD4

    MrLOL  •  28 Mar

  • From what I gather, each component has an id/index. When a component gets added to an entity, it's component index is used to look up the index of the address. Why the component index isn't directly used directly to get the address is a bit of a mystery to me.

    I believe that it is.   As you mentioned, there are 0x40 slots for components within each entity (although the most I've seen on a single entity thus far is 16.)   These 0x40 slots begin at Entity+0x18 and take 1 byte each.  

    My work at this point is based upon the fact that if Entity+0x18+n is not 0xff, then it will definitely point to a particular component.   Therefore, if I want to check for that particular component, then I would always check Entity+0x18+n.   The value of that location will be the index of the actual component within the component array, which is located later in the entity class.

    I've written some fairly complex debugging/spew routines to iterate through all possible component locations and tell me whether they exist or not on that particular entity, whether the index matches the component that I expect (by comparing the virtual function table offset with the one in my source), etc...


    Amadeus  •  10 Apr

  • Does anyone have the beta binary? looking for a valid copy from someone, if possible please email or link to jayswag01@muchomail.com

    I've been stuck on player rotation for some reason... does anyone know if rift rotation is based on radians like in WoW (0 and 6 meet at north).. Would be very helpful if anyone can provide any information regarding player base + rotation + etc .

    Thanks alot for the post Da_Teach, I'll be checking back hope to see more entries and proof of concept! Great work!

    -jay

    jay  •  11 Apr

  • It looks like they added a jump where the "ClientEntityComponentNPC" used to be on the newest release... Does anyone think this was put there on purpose to make it harder to find or just a qa-winky-dink? (lolsp) Interesting stuff! Im interested to see if they are all like that or just the ones you posted! (trions trolling lol )

    _UNKNOWN *__cdecl sub_5CC6D0()
    {
      return sub_A14A08(); //jmp     sub_A14A08
    }


    _UNKNOWN *__cdecl sub_A14A08()
    {
      if ( !(dword_1232A60 & 1) )
      {
        dword_1232A60 |= 1u;
        sub_A45D40((int)&unk_1232A34, "Entity::Component", 0);
        sub_E34880(sub_1066221);
      }
      return &unk_1232A34;
    }

    Jay  •  12 Apr

  • The end of rift

    Igar  •  21 Oct

Post a comment!
  1. Formatting options