Server Help

General Questions - Reversing subgame2 ran into a problem

fatrolls - Wed Apr 16, 2014 11:24 pm
Post subject: Reversing subgame2 ran into a problem
I'm trying to port subgame2 into Linux but I ran into a problem.

I know the maximum allowed players in subgame2 is either 1024 or 1025 or maybe that's maximum in just one arena I don't know for sure.

But when I was re-factoring some structures I ran into a interesting problem for some reason the structure starts to overlap what I called ArenaPlayerCount which is at 0xFF0C of the struct_ARENA I called it.

Now the playerPointersForSomething starts at 0xFB20

FF0C - FB20 = 3EC of space which is 1004 bytes of space.

Lets say the struct_PLAYER is a total of 0x35D (861 bytes).

So yeah about only 1 player can fit in there, so.. I figured it had to be a array of addresses which point to that struct_PLAYER which makes sense.

But since the addresses take 4 bytes each that means 1004/4=251

Which means only 251 players can be stored in the arena?

I also thought maybe ArenaPlayerCount is like a base address.

And every player pointer is only 1 byte long which would append to the base address but seems like that's impossible since the addresses are always in randomized or just unpredictable locations.

Thought maybe the playerPointersForSomething itself isn't a array but just a pointer to another location which holds all the players but I couldn't find it. Then again it can't be since the playerPointersForSomething points to the first player.

Also the struct_ARENA can't be larger I found the structure limit here
newArena = operator new(0x1D40Eu);
So each arena structure is exactly 119,822 bytes.


I remember when I used to play Trench Wars during World War 3 there was over 380 players in one arena so I don't believe the limit is 251 players per arena, I'm just doing something wrong.

Here is a fully decompiled function to show the bug, I already posted on many other reversing forums thinking they'll show me some basics on how to do this but meh no replies so far thinking I may get some insight here.

Code: Show/Hide

int __thiscall GetNextFrequencyToJoin(struct_ARENA *arena)
{
  struct_ARENA *arenaa; // ebx@1
  signed int TotalPlaying; // ebp@1
  struct_PLAYER **eachPlayer; // edx@2
  int ArenaPlayerCount; // esi@2
  int Frequency; // eax@3
  signed int CurrentFrequency; // esi@10
  int *NumPlayersPerFreqArrayy; // edi@10
  int NumPlayersThisFreq; // ecx@12
  signed int TotalPlayingg; // [sp+10h] [bp-324h]@1
  int NumPlayersPerFreqArray[200]; // [sp+14h] [bp-320h]@1

  arenaa = arena;
  memset(NumPlayersPerFreqArray, 0, sizeof(NumPlayersPerFreqArray));
  TotalPlaying = 0;
  TotalPlayingg = 0;
  if ( arena->ArenaPlayerCount > 0 )
  {
    eachPlayer = arena->playerPointersForSomething;// Obviously Player Pointers but why only 251? If possible 1025 players
    ArenaPlayerCount = arena->ArenaPlayerCount;
    do
    {
      Frequency = (*eachPlayer)->Frequency;
      if ( Frequency >= 0 && (*eachPlayer)->Ship != 8 )
      {
        if ( Frequency < 200 )
          ++NumPlayersPerFreqArray[Frequency];
        ++TotalPlaying;
      }
      ++eachPlayer;
      --ArenaPlayerCount;
    }
    while ( ArenaPlayerCount );
    TotalPlayingg = TotalPlaying;
  }
  CurrentFrequency = 0;
  NumPlayersPerFreqArrayy = NumPlayersPerFreqArray;
  while ( 1 )
  {
    if ( CurrentFrequency >= arenaa->ArenaSettings.MaxFrequency )
      return rand() % arenaa->ArenaSettings.MaxFrequency;
    NumPlayersThisFreq = *NumPlayersPerFreqArrayy;
    if ( !*NumPlayersPerFreqArrayy )
      break;
    if ( NumPlayersThisFreq < arenaa->ArenaSettings.MaxPerTeam
      && NumPlayersThisFreq <= TotalPlayingg / arenaa->ServersideArenaSettings.TeamDesiredTeams )
      break;
    ++CurrentFrequency;
    ++NumPlayersPerFreqArrayy;
    if ( CurrentFrequency >= 200 )
      return rand() % arenaa->ArenaSettings.MaxFrequency;
  }
  return CurrentFrequency;
}


Here is a Naked version just so you can see the address offsets for the structure.
Code: Show/Hide

int __thiscall GetNextFrequencyToJoin(int arena)
{
  struct_ARENA *arenaa; // ebx@1
  signed int TotalPlaying; // ebp@1
  int eachPlayer; // edx@2
  int ArenaPlayerCount; // esi@2
  int Frequency; // eax@3
  signed int CurrentFrequency; // esi@10
  int *NumPlayersPerFreqArrayy; // edi@10
  int NumPlayersThisFreq; // ecx@12
  signed int TotalPlayingg; // [sp+10h] [bp-324h]@1
  int NumPlayersPerFreqArray[200]; // [sp+14h] [bp-320h]@1

  arenaa = arena;
  memset(NumPlayersPerFreqArray, 0, sizeof(NumPlayersPerFreqArray));
  TotalPlaying = 0;
  TotalPlayingg = 0;
  if ( *(arena + 65292) > 0 )
  {
    eachPlayer = arena + 64288;                 // Obviously Player Pointers but why only 251? If possible 1025 players
    ArenaPlayerCount = *(arena + 65292);
    do
    {
      Frequency = *(*eachPlayer + 279);
      if ( Frequency >= 0 && *(*eachPlayer + 275) != 8 )
      {
        if ( Frequency < 200 )
          ++NumPlayersPerFreqArray[Frequency];
        ++TotalPlaying;
      }
      eachPlayer += 4;
      --ArenaPlayerCount;
    }
    while ( ArenaPlayerCount );
    TotalPlayingg = TotalPlaying;
  }
  CurrentFrequency = 0;
  NumPlayersPerFreqArrayy = NumPlayersPerFreqArray;
  while ( 1 )
  {
    if ( CurrentFrequency >= *(arenaa + 109378) )
      return rand() % *(arenaa + 109378);
    NumPlayersThisFreq = *NumPlayersPerFreqArrayy;
    if ( !*NumPlayersPerFreqArrayy )
      break;
    if ( NumPlayersThisFreq < *(arenaa + 109567) && NumPlayersThisFreq <= TotalPlayingg / *(arenaa + 109706) )
      break;
    ++CurrentFrequency;
    ++NumPlayersPerFreqArrayy;
    if ( CurrentFrequency >= 200 )
      return rand() % *(arenaa + 109378);
  }
  return CurrentFrequency;
}


Super raw
Code: Show/Hide

function GetNextFrequencyToJoin {
    esp = esp - 0x324;
    ebx = ecx;
    ecx = 0xc8;
    asm{ rep stosd   };
    ebp = 0x0;
    if (*(ebx + 0xff0c) > 0x0) {
            edx = ebx + 0xfb20;
            esi = 0x0;
            do {
                    if ((*(*edx + 0x117) >= 0x0) && (*(ecx + 0x113) != 0x8)) {
                            if (eax < 0xc8) {
                                    *(esp + eax * 0x4 + 0x14) = *(esp + eax * 0x4 + 0x14) + 0x1;
                            }
                            ebp = &var_1;
                    }
                    edx = edx + 0x4;
                    esi = esi - 0x1;
            } while (esi != 0x0);
    }
    ebp = *(ebx + 0x1ab42);
    esi = 0x0;
    edi = esp + 0x14;

loc_4045a3:
    if (esi >= ebp) goto loc_4045d4;
    goto loc_4045a7;

loc_4045d4:
    eax = sub_41ceb0();
    asm{ cdq         };
    temp_2 = MAKEWORD(edx:eax);
    temp_3 = *(ebx + 0x1ab42);
    eax = temp_2 / temp_3;
    return temp_2 % temp_3;

loc_4045a7:
    if (*edi == 0x0) goto loc_4045ed;
    goto loc_4045ad;

loc_4045ed:
    eax = esi;
    return eax;

loc_4045ad:
    if (ecx >= *(int8_t *)(ebx + 0x1abff)) goto loc_4045c8;
    goto loc_4045b9;

loc_4045c8:
    esi = esi + 0x1;
    edi = edi + 0x4;
    if (esi < 0xc8) goto loc_4045a3;
    goto loc_4045d4;

loc_4045b9:
    asm{ cdq         };
    temp_0 = MAKEWORD(edx:*(esp + 0x10));
    temp_1 = *(ebx + 0x1ac8a);
    edx = temp_0 % temp_1;
    if (ecx <= temp_0 / temp_1) goto loc_4045ed;
    goto loc_4045c8;
}

L.C. - Sat May 28, 2016 1:59 am
Post subject:
Quote:
I remember when I used to play Trench Wars during World War 3 there was over 380 players in one arena so I don't believe the limit is 251 players per arena, I'm just doing something wrong.
When Continuum 0.36 or 0.37 was released, the only zone running the compatible Subgame had 700-1000 players in a single arena. I remember it very clearly, and it handled it well/okay too. I've never been able to replicate the results to that degree with bots though.
All times are -5 GMT
View topic
Powered by phpBB 2.0 .0.11 © 2001 phpBB Group