View previous topic :: View next topic |
Author |
Message |
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 18:08 Post subject: Hooking Player Attitude Changes for pvp |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 18:45 Post subject: added some debug statements |
|
|
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 |
|
|
Zebranky
Joined: 04 Jun 2006 Posts: 415
|
Posted: Thu Jul 29, 2010 19:33 Post subject: |
|
|
Dagnabbit, stop using asm hooks
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 19:41 Post subject: asm |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 19:50 Post subject: Wow |
|
|
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 |
|
|
Zebranky
Joined: 04 Jun 2006 Posts: 415
|
Posted: Thu Jul 29, 2010 20:43 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 20:49 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 21:06 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 21:17 Post subject: definitely the runscript |
|
|
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 |
|
|
MaxRock
Joined: 24 Jan 2008 Posts: 196
|
Posted: Thu Jul 29, 2010 22:10 Post subject: |
|
|
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 |
|
|
Zebranky
Joined: 04 Jun 2006 Posts: 415
|
Posted: Thu Jul 29, 2010 22:16 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 22:16 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 22:19 Post subject: |
|
|
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 |
|
|
MaxRock
Joined: 24 Jan 2008 Posts: 196
|
Posted: Thu Jul 29, 2010 22:21 Post subject: |
|
|
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 |
|
|
Baaleos
Joined: 02 Sep 2007 Posts: 830
|
Posted: Thu Jul 29, 2010 22:24 Post subject: |
|
|
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 |
|
|
|
|
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
|