Fixing MOHAA Model Collision

Information about the engine technology is revealed and discussed here
Post Reply
m0d hippY
Warrant Officer
Posts: 132
Joined: Tue Apr 27, 2004 3:58 am

Fixing MOHAA Model Collision

Post by m0d hippY »

Over the past few weeks\months I have been somewhat busy, but just recently got another idea for fixing something in MOHAA. I think this is a bit more "doable" than alot of the other projects, but I would like to get feedback from others and hopefully some help with the project.

The fix will be implemented through Wombat's gamex wrapper.

Basically one of MOHAA's biggest issues is the collision detection with the player model & brush.

After testing several hours, here is what I have come up with.

1. Player model collision is broken in MOHAA, because the player can only walk so far into a solid object, however what bypasses this is when the client pushes against the wall & leans at the same time. This causes some unstable clipping to occur and causes certain parts to clip through walls.

2. After testing MOHAA's leaning, I know that the angle the player leans at is a float value and ranges between -16 & 16. What I also noticed is that the player doesn't lean as far as the game should want him to, instead it makes him lean a constant -16,16.

In other words regard;ess of how close to "open" space the player model is the game forces the client to lean the full amount every time.
-16 when leaning right
16 when leaning left
The value is a float value that increments from 0.00 to 16\-16.00, but the incrementation is almost instant, so the model goes from 0 to the max lean in a second if not less.

Anywho as far as fixing the leaning in MOHAA, I had to figure out where all these variables are controlled\stored.

Using a memory search program I found the following information.

Code: Select all

    int leanLeft = *(int*)0x12F7150;
    int leanRight = *(int*)0x12F7168;

    float value1 = *(float*)0xE5C5578;
    float value2 = *(float*)0xDA301B0;
    float value3 = *(float*)0xE5C5598;
The first 2 variables are either 0 or 1. If it's 1 that means the player is leaning in that direction, if it's 0 then he's not leaning, if both are set to 1, that also stops the player from the ability to lean, you can just as easily set both values to 0.

The other 3 variables labeled value1-value3, are the offsets where the angle is stored.
So if the client leans left then those 3 values will all be set to -16 and 16 when they lean right. If those 3 values are 0, then they aren't leaning.

You need to set all those offsets for it to work properly.

What does this mean for us?

Well my hypothesis for fixing the model collision would work like this.
-If the player is up against a wall (we can test it using the trace_t function and checking if the fraction == 1.0), we need to calculate the distance the player is from the wall.
Once we have the distance calculated we store it in aa temporary place.
Next we need to detect whether or not the player is leaning. We can do so by having 3 checks something similar to this

Code: Select all

if((gi.SendConsoleCommand("+leanleft") || (gi.SendConsoleCommand("+leanright")))
                if(leanLeft != 0 || leanRight != 0)// if value is 1 we're leaning, if it's 0 we're not
                {
                    if(value1 != 0 && value2 != 0 && value3 != 0) //when we lean it is either -16 or 16 if it's 0, we're not leaning
1. We check if they are leaning by seeing if they are passing the +leanleft or +leanright command through console or keybind.
2. We're checking if either of the 2 offsets are = to anything other than 0.
3. We're checking to see if the 3 values that hold the lean angles are not equal to 0.

If all these 3 pass, we know the client is leaning, so at that point what we want to do is take the distance we calculated above to see how far we're from the wall and check it to see if we have 16 units of space to lean.

If the distance is less than 16, we can do 1 of 2 things.
1. make the player lean only as much as he can (so if the distance is 9 units away from the wall force it only 9)
2. Don't let the player lean at all, instead print a message on the players screen telling them that they don't have space to lean.

This will fix the mode collision issue in the game as well as fix the clipping issue that is used as a glitch for STWH.

The issue I'm running into is figuring out where to do this checking. What function? We tried ClientThink(), ClientCommand(), ClientConnect(), all of which crash the game after a few minutes.

Here is the code I'm working with:

Code: Select all

{
    int clientNum;

    int leanLeft = *(int*)0x12F7150;
    int leanRight = *(int*)0x12F7168;

    float value1 = *(float*)0xE5C5578;
    float value2 = *(float*)0xDA301B0;
    float value3 = *(float*)0xE5C5598;

    for(int i=0; i<32 && i!=clientNum;i++)
    {
        if((gi.SendConsoleCommand("+leanleft") || (gi.SendConsoleCommand("+leanright")))
                if(leanLeft != 0 || leanRight != 0)// if value is 1 we're leaning, if it's 0 we're not
                {
                    if(value1 != 0 && value2 != 0 && value3 != 0) //when we lean it is either -16 or 16 if it's 0, we're not leaning
                    {
                        gi.Printf("We are definitly leaning");
                    }
            }
        }
    }
}
Here is an export of the functions used in MOHAA:

Code: Select all

typedef struct
{
 public:
  int apiversion;
  void (*Init) (/* unknown */);
  void (*Shutdown) (/* unknown */);
  void (*Cleanup) (/* unknown */);
  void (*Precache) (/* unknown */);
  void (*SetMap) (/* unknown */);
  void (*Restart) (/* unknown */);
  void (*SetTime) (/* unknown */);
  void (*SpawnEntities) (/* unknown */);
  char *(*ClientConnect) (/* unknown */);
  void (*ClientBegin) (/* unknown */);
  void (*ClientUserinfoChanged) (/* unknown */);
  void (*ClientDisconnect) (/* unknown */);
  void (*ClientCommand) (/* unknown */);
  void (*ClientThink) (/* unknown */);
  void (*BotBegin) (/* unknown */);
  void (*BotThink) (/* unknown */);
  void (*PrepFrame) (/* unknown */);
  void (*RunFrame) (/* unknown */);
  void (*ServerSpawned) (/* unknown */);
  void (*RegisterSounds) (/* unknown */);
  qboolean (*AllowPaused) (/* unknown */);
  qboolean (*ConsoleCommand) (/* unknown */);
  void (*ArchivePersistant) (/* unknown */);
  void (*WriteLevel) (/* unknown */);
  qboolean (*ReadLevel) (/* unknown */);
  qboolean (*LevelArchiveValid) (/* unknown */);
  void (*ArchiveInteger) (/* unknown */);
  void (*ArchiveFloat) (/* unknown */);
  void (*ArchiveString) (/* unknown */);
  void (*ArchiveSvsTime) (/* unknown */);
  orientation_t (*TIKI_Orientation) (/* unknown */);
  void (*DebugCircle) (/* unknown */);
  void (*SetFrameNumber) (/* unknown */);
  void (*SoundCallback) (/* unknown */);
  class prof_game_s *prof_struct;
  struct gentity_s *gentities;
  int gentitySize;
  int num_entities;
  int max_entities;
  char *error_message;
} game_export_t;
Any help is appreciated.
Image
Image
Rookie One.pl
Site Admin
Posts: 2752
Joined: Fri Jan 31, 2003 7:49 pm
Location: Nowa Wies Tworoska, Poland
Contact:

Post by Rookie One.pl »

You can't just check if the client is using a console command. Server has no "read" access to client console.

The core of the STWH problem is that instead of signalling that the lean button is being pressed and letting the server handle the rest (most bullet-proof way; this is how all other input is handled), the client itself performs the lean and sends the modified eye position and rotation over the net. To make things more complicated, it does that using a separate type of a network packet, just for the eye position (why wouldn't they use usercmd_t like for everything else, I do not know). This eye position/rotation info is then used as the weapon muzzle point. I guess they wanted to smooth out lag, but IMHO achieved an epic fail instead. The introduced hacking possibilities prove the stupidity of the solution.

As for the visuals (i.e. player models viewed from third person apparently getting inside walls), it's a tad more complicated issue. From what I can tell, this just does a boolean check on whether the player is pressing a lean button or not. The solution (just so that first and third person animations match) would be to set the bone quaternions using the angles from the eye pos/rot packet sent over the net instead.

And in order to fix all the clipping problems, a head trace identical to the one done on the client must be performed on the server-side.

I can't see it being done. EA's not releasing the source, and writing codecaves for all of this stuff requires a brainiac with loads of spare time.
Admin
Image
Image
Honour guide me.

here's my stuff - inequation.org | here's where I work - thefarm51.com
m0d hippY
Warrant Officer
Posts: 132
Joined: Tue Apr 27, 2004 3:58 am

Post by m0d hippY »

Damn, that's not what I wanted to hear, but thankyou for that detailed insight. Seriously rookie you sure you don't "secretly" work with EA haha. You have far more experience it seems in this game than anyone I have ever met. I don't even think hackers come close to as much knowledge in the engine as you do.

Either way, you say there is no way to check or read from console. Isn't there some kind of event or something that tells the game a person is leaning. With the help of Elgan I tested a serverside script that calculates the angle of leaning which is what I used to search the values in memory. If that command is serverside then maybe we can use that. This is the script I used:

Code: Select all

main:
level waittill prespawn
level waittill spawn

while(1)
{
  for (local.i = 1; local.i <= $player.size; local.i++)
  {
    local.player = $player[local.i]
    local.lean = (local.player getcontrollerangles 0)[2]
    iprintln ("lean: " + local.lean)
  }
  waitframe
}
end
if "getcontrollerangles" is a serverside command, maybe instead of checking or trying to read from the console or memory values I can simply read from that command, and if it returns 0 then the client is leaning?

I'm trying to go about this a different way.
the client itself performs the lean and sends the modified eye position and rotation over the net. This eye position/rotation info is then used as the weapon muzzle point.
I know heiko originally fixed STWH through the gamex wrapper by checking something in the muzzle as well.

As far as the "eyepos" issue is concerned, if I do remember correctly, has been fixed by jv_map. Well sort of. It was one of the fixes that were being worked on for the "still unreleased" MOHAA 1.12 patch aka MOH Reloaded. However even with the eyepos\muzzle fix STWH was still vulnerable because I tested it.

As far as 3rd person is concerned, I'm not too worried about that when it concerns STWH.
The solution (just so that first and third person animations match) would be to set the bone quaternions using the angles from the eye pos/rot packet sent over the net instead.
I needed a dictionary to figure out what you were talking about haha, pathetic I know, but seriously I wouldn't even know where to begin with something like that, and as far as packets are concerned, well that's a completely different story in itself.
And in order to fix all the clipping problems, a head trace identical to the one done on the client must be performed on the server-side.

I can't see it being done. EA's not releasing the source, and writing codecaves for all of this stuff requires a brainiac with loads of spare time.
Granted I'm no guru at this stuff clearly as you can see, but surely you can do this without having to deal with codecaves. I mean after all if one were to get all the reversed structs, classes needed to perform something like this wouldn't that discard the use to need codecaves?

And as far as a head trace is concerned, does it have to be identical, or can it just be close enough? After all we can use trace_t to check what's infront and so on right?

This is similar to the antiwallhack fix using tracers and such only we don't care so much whether we're infront\behind a brush, we're more concerned with how close we are to the brush infront or to the side of us.

I feel like I'm babbling, but surely if STWH was as easily implemented there should be a similar way to stop it I would imagine.
Image
Image
Rookie One.pl
Site Admin
Posts: 2752
Joined: Fri Jan 31, 2003 7:49 pm
Location: Nowa Wies Tworoska, Poland
Contact:

Post by Rookie One.pl »

I think hackers have the same knowledge, only I have it as a part of a bigger picture, having worked with the Q3 engine before.

Well, what I meant is that you can't just read a player's console. You can detect if they're leaning extremely easily, though - just bitwise-OR the "buttons" element of the player's last usercmd_t against the leaning bits (cmd->buttons & LEAN_LEFT/RIGHT).

What do you mean, eyepos issue? I only recall that Jv fixed the so-called "self-spectator" in MoH Reloaded (i.e. leaning further in 1st person than the 3rd person model showed, allowing to effectively see beyond corners without being seen).

Quaternions are a way of describing spatial rotation. It's a mathematical object built of complex numbers. And what I meant by using the packet is to retrieve the lean angle from what the client sends to the server instead of calculating the angle on the server. It's all a bit inconsistent - they made it so that first person leaning is handled by client, third person by server. But then again, there are many such inconsistencies in MoHAA.

I'm afraid some code would need to be injected, this is stuff that touches not only the game library but also the engine.

Head traces must be identical or you'll experience view jittering when not in full lean (e.g. blocked by a wall). Tried and tested while modding Enemy Territory.

Well, from my experience (though I have little of that, granted I'm only 19 ;) ), simple flaws in complex mechanisms are the most difficult to fix.
Admin
Image
Image
Honour guide me.

here's my stuff - inequation.org | here's where I work - thefarm51.com
Post Reply