logo logo

 Back to main page

The NWNX Community Forum

 FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 
 
Hooking Player Attitude Changes for pvp
Goto page 1, 2, 3  Next
 
Post new topic   Reply to topic    nwnx.org Forum Index -> Windows development
View previous topic :: View next topic  
Author Message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 18:08    Post subject: Hooking Player Attitude Changes for pvp Reply with quote

Ok, I have a problem,
Im trying to enforce hostility between the two player factions on my server.
Vampires vs Humans.

To do this, I can use SetDislikesMe() from nwnscript, however, there is no function to actually prevent the player toggling back to LikesMe() after the script runs.

Sure, I can do a Loop Statement every heartbeat to detect changes in attitude, but doing a loop through all players, on all players, will be inefficient.

So, I was wondering if anyone could help me with a hook I was trying to get working.

Unfortunately, what I have so far, causes the server to crash.


Code:

void SetLikesMeHookProc( )
{
   
  _asm { pushad }
   if (!scriptRun)
   {
      
      _asm{
         mov eax, ecx
         mov eax, [eax+4]
         mov oPC, eax
      }
      if( oPC ){
         events.FireEvent(oPC, 17,"SetLikesMe");         
      }

   }
  _asm { popad }
  _asm { leave }
  if( events.nBypass == 1 ){
       _asm{ retn 4 }
  }
  else
  {

  }
  _asm { jmp SetLikesMeNextHook }
  events.nBypass = 0;
}


Unfortunately, this crashes, with and without bypass, I think the crash even occurs before it gets to FireEvent, because no event appears in the Logs.


The function I am attempting to hook is
Code:

void __thiscall CNWSCreature::SetPVPPlayerLikesMe(unsigned long, int, int)

From what I understand, perhaps in error, ECX is usually the Creature making the call (this).

From what I understand of what I have used, and witnessed in other hooks, it works like this

mov eax, ecx Move ECX into Eax
mov eax, [eax+4] Move Eax+4 bytes length into eax
mov oPC, eax Move Eax into a variable called oPC

and this should give us the player making the call.

This however, is where the crash occurs I think.
The crash always occurs in nwn_events.dll and never in nwnserver.exe

Anyone with better understanding of assembly able to give me a hand.

I need a way of just hooking onto the function, so that when it is called, I can trigger nwnscript to set the attitudes back to the way I want them.

Note - I probably wont be able to bypass the function completely, because this function is called as part of Party Join requests etc.
Joining a party calls this.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 18:45    Post subject: added some debug statements Reply with quote

Added in some debug prints to the module, to find out where it is crashing exactly.
At least this time, with the new code, it determined that oPC was a valid value, but unfortunately, from the FireEvent message, I can see it was the incorrect value.
Quote:

o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
Start of Likes me Hook
Push Double
Inside Script not run
After the ASM
oPC = Valid
o SetLikesMe (17): OBJECT_SELF: 0b49ea7c.



Code:

void SetLikesMeHookProc( )
{
fprintf(events.m_fFile, "Start of Likes me Hook\n");   
  _asm { pushad }
  fprintf(events.m_fFile, "Push Double\n");
   if (!scriptRun)
   {
      fprintf(events.m_fFile, "Inside Script not run\n");
      _asm { add ebx, 4 }
      _asm { mov oPC, ebx }
      _asm { sub ebx, 4 }
       fprintf(events.m_fFile, "After the ASM\n");
      if( oPC ){
         fprintf(events.m_fFile, "oPC = Valid \n");
         events.FireEvent(oPC, 17,"SetLikesMe");         
      }

   }
   fprintf(events.m_fFile, "Leaving ScriptNot Run \n");
  _asm { popad }
  _asm { leave }
  if( events.nBypass == 1 ){
       _asm{ retn 4 }
  }
  else
  {

  }
  fprintf(events.m_fFile, "jmp to NextHook \n");
  _asm { jmp SetLikesMeNextHook }
  events.nBypass = 0;
}
Back to top
View user's profile Send private message
Zebranky



Joined: 04 Jun 2006
Posts: 415

PostPosted: Thu Jul 29, 2010 19:33    Post subject: Reply with quote

Dagnabbit, stop using asm hooks Smile

In most cases, you can and should use the __fastcall trick:
Code:
void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
   SetLikesMeNextHook(pThis, NULL, a, b, c);
}


That will just call the original function. You can build off that.

As far as actually doing what you want, there are some tricky aspects here, most notably that you're dealing with two PCs, so you need to handle both of them somehow. Right now I don't have the chance to dig in and find a good way to grab and pass them both, so I'll leave that to you for now.

(Hint: looks like it's going to involve calling CServerExoApp::GetCreatureByGameObjectID)
_________________
Win32 SVN builds: http://www.mercuric.net/nwn/nwnx/

<Fluffy-Kooshy> NWNx plugin is to this as nuclear warheads are to getting rid of fire ants.

<ThriWork> whenever I hear nwn extender, I think what does NWN need a penis extender for?
Back to top
View user's profile Send private message Visit poster's website
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 19:41    Post subject: asm Reply with quote

Well, the reason I have been using asm, is because its the only real examples I have infront of me.

But in terms of this function, I think if I knew how to get the arguments correctly, it wouldnt be that hard to get the two player objects.


The function is called by CNWSCreature::
and from what you told me, ecx is usually considered to the 'this' in this case, 'this' should be the creature doing the attitude change.

and then there are 3 arguments passed into the function.

Ones a dWord - Im guessing this is the creature/player object, and the other two are int's, one of which becomes a 1 for hostile, and 0 for friendly. And the other, I have no idea what it does, doesnt seem to do anything really.


So, my question,

How do I get *this*, as well as the argument which relates to the target creature.
Code:

; public: void __thiscall CNWSCreature::SetPVPPlayerLikesMe(unsigned long, int, int)
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 19:50    Post subject: Wow Reply with quote

It actually built...

Code:

void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
      events.FireEvent(*(dword *)pThis, 17,"SetLikesMe");
      SetLikesMeNextHook(pThis, NULL, a, b, c);
}


Is this how the __fastcall should work?

Do you think this would be enough, to get the Script to fire, on pThis?
Back to top
View user's profile Send private message
Zebranky



Joined: 04 Jun 2006
Posts: 415

PostPosted: Thu Jul 29, 2010 20:43    Post subject: Reply with quote

That's how it works, yep. I don't know offhand if CNWSCreature is a valid type to run a script on, but there's certainly a chance it will work.

BTW, I'm pretty sure, in this case, 'this' is the *target* PC (i.e., not the one who initiated the change). The ulong is probably the object ID of the originating PC. (Check out 54603A and thereabouts. The call at 545FDE grabs an oid from the message, which ultimately gets turned into a CNWSCreature* and passed as 'this' to SetPVPPlayerLikesMe.)
_________________
Win32 SVN builds: http://www.mercuric.net/nwn/nwnx/

<Fluffy-Kooshy> NWNx plugin is to this as nuclear warheads are to getting rid of fire ants.

<ThriWork> whenever I hear nwn extender, I think what does NWN need a penis extender for?
Back to top
View user's profile Send private message Visit poster's website
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 20:49    Post subject: Reply with quote

Sad
Crashes the server at that function.
The Event it fired appeared odd...


But I guess it confirms what you say, that the CNWSCreature running this event is actually the one targetted.

Quote:

o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o SetLikesMe (17): OBJECT_SELF: 00633a24.


I think the crash came on the
RunScript call for the object being passed in to the FireEvent.


I will try this instead
Code:

void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
      events.FireEvent(*(unsigned long*)a, 17,"SetLikesMe");

      SetLikesMeNextHook(pThis, NULL, a, b, c);
}

This should get a as the Originator, and fire the event on them instead.
In theory. As far as fastcall'ing the internal methods to get object id etc, am I able to fastcall directly to it, or do I need to define every class between here and the function?
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 21:06    Post subject: Reply with quote

some progress I think.

Quote:

o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffb.
o StealthMode (10): OBJECT_SELF: 7ffffffb.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffb.
o SetLikesMe (17): OBJECT_SELF: 7ffffffd.


Still getting a crash though.
and Now I really dont know why.
Since the RunScript function is getting exactly the same type of data fed into it as any of the other hooks.
You can see here, that the SetLikesMe event, is indeed fired on the same OBJECT_SELF object as any of the other hooks.

Question - When using fast call method, do I need to specify memory address for the
SetLikesMeNextHook();

when using asm, I never had to do that, so its just occuring to me, perhaps with the fastcall method, I need to define where SetLikesMeNextHook is meant to begin so it can resume the function.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 21:17    Post subject: definitely the runscript Reply with quote

Quote:

o StealthMode (10): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffffb.
o StealthMode (10): OBJECT_SELF: 7ffffffd.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffffd.
o StealthMode (10): OBJECT_SELF: 7ffffff9.
o StealthMode (10): OBJECT_SELF: 7ffffff9.
o RemoveSanctuary (15): OBJECT_SELF: 7ffffff9.
Firing Event
o SetLikesMe (17): OBJECT_SELF: 7ffffffd.


Code:

void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
   fprintf(events.m_fFile, "Firing Event\n");
      events.FireEvent(a, 17,"SetLikesMe");
    fprintf(events.m_fFile, "Event Fired\n");
      _asm { jmp SetLikesMeNextHook }
    fprintf(events.m_fFile, "After the SetLikesMeNextHook\n");
}


been trying all sorts of things.
But the general result seems to be, that events.FireEvent doesnt like 'a', but surely a is the type of data it gets for any other hook.
Am I missing a cast?

The other hooks can work with data types such as '7ffffffd', but not this one??
Back to top
View user's profile Send private message
MaxRock



Joined: 24 Jan 2008
Posts: 196

PostPosted: Thu Jul 29, 2010 22:10    Post subject: Reply with quote

I'm assuming you're using Terra's nwnx_events as a base?

I've copied the code you've posted - using 0x004D0450 as org_SetLikesMe and it sure enough crashes.
But: I have nwnx_event.nss set to print the name of OBJECT_SELF which it does just fine, and then it crashes:

"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."

Is that the same you're getting?
Back to top
View user's profile Send private message Send e-mail MSN Messenger
Zebranky



Joined: 04 Jun 2006
Posts: 415

PostPosted: Thu Jul 29, 2010 22:16    Post subject: Reply with quote

I suspect you're actually crashing right after the jmp, and it's just not flushing the second log message. What did I say about asm?!

Code:
void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
   fprintf(events.m_fFile, "Firing Event\n");
      events.FireEvent(a, 17,"SetLikesMe");
    fprintf(events.m_fFile, "Event Fired\n");
      SetLikesMeNextHook(pThis, NULL, a, b, c);
    fprintf(events.m_fFile, "After the SetLikesMeNextHook\n");
}


As long as you're hooking properly (at the start of SetPVPPlayerLikesMe), SetLikesMeNextHook should have the right value.
_________________
Win32 SVN builds: http://www.mercuric.net/nwn/nwnx/

<Fluffy-Kooshy> NWNx plugin is to this as nuclear warheads are to getting rid of fire ants.

<ThriWork> whenever I hear nwn extender, I think what does NWN need a penis extender for?
Back to top
View user's profile Send private message Visit poster's website
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 22:16    Post subject: Reply with quote

Actually, because Im running my server on a 32bit VM, I cannot get any debug info regarding the crashes, only the offset of where the crash occurs.

The interesting thing though, is that for me, its Crashing within the RunScript method call, if you look at the code I pasted above, you can see I have another debug message print after the RunScript and it never makes it that far.


This is all the data I am using

Code:

DWORD org_LikesMe         = 0x4D0450;

success3_0 = CreateHook( org_LikesMe, SetLikesMeHookProc,(PVOID*) &SetLikesMeNextHook, "SetLikesMe" );


PrintHook( org_LikesMe, success3_0, "SetLikesMe" );

void __fastcall SetLikesMeHookProc(void *pThis, void *, unsigned long a, int b, int c)
{
   fprintf(events.m_fFile, "Firing Event\n");
      events.FireEvent(*(dword *)a, 17,"SetLikesMe");
    fprintf(events.m_fFile, "Event Fired\n");
      _asm { jmp SetLikesMeNextHook }
    fprintf(events.m_fFile, "After the SetLikesMeNextHook\n");
}


void (*SetLikesMeNextHook)();




Not to sound dense or anything.... but should I change
success3_0 = CreateHook( org_LikesMe, SetLikesMeHookProc,(PVOID*) &SetLikesMeNextHook, "SetLikesMe" );

to match the fastcall method arguments?
I was tempted to, but when I examined the Devastating Critical method, and the others, they all just kept it as a PVOID*, so I decided to try that too.
Back to top
View user's profile Send private message
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 22:19    Post subject: Reply with quote

LMAO. Lol

I did try not using the ASM, but when I used the direct call to the function, it resulted in crashes inside the nwnx_events.dll instead of inside nwnserver.exe.

I will give it a try shortly, my 'test dummies' who I test the hostility on have gone for a nap. Lol
Back to top
View user's profile Send private message
MaxRock



Joined: 24 Jan 2008
Posts: 196

PostPosted: Thu Jul 29, 2010 22:21    Post subject: Reply with quote

Baaleos wrote:

The interesting thing though, is that for me, its Crashing within the RunScript method call, if you look at the code I pasted above, you can see I have another debug message print after the RunScript and it never makes it that far.


Well, the reason it never makes it to the last debug message is that you jump out of the function before that.
Back to top
View user's profile Send private message Send e-mail MSN Messenger
Baaleos



Joined: 02 Sep 2007
Posts: 830

PostPosted: Thu Jul 29, 2010 22:24    Post subject: Reply with quote

Quote:

Firing Event
o SetLikesMe (17): OBJECT_SELF: 7ffffffd.


Code:

   fprintf(events.m_fFile, "Firing Event\n");
      events.FireEvent(*(dword *)a, 17,"SetLikesMe");
    fprintf(events.m_fFile, "Event Fired\n"); <<******** This one never fires
      _asm { jmp SetLikesMeNextHook }
    fprintf(events.m_fFile, "After the SetLikesMeNextHook\n");



Yes, ok, it was jmp'ing out of the function, but there should have been another 1 log before the jmp.

But if as you say, it just didnt have a chance to flush, then that explains it.
I was just expecting it to be a linear progression, where it would have to have wrote the log files before jmping, if thats the order they came in.
Back to top
View user's profile Send private message
Display posts from previous:   
Post new topic   Reply to topic    nwnx.org Forum Index -> Windows development All times are GMT + 2 Hours
Goto page 1, 2, 3  Next
Page 1 of 3

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2005 phpBB Group