Creating a browser game bot with PHP cURL – Part 02 (BattleKnight bot)

March 10, 2013 by Asbra — Leave a comment

You should read How to create a BattleKnight bot – Part 01 before you start with this part of the tutorial.

Reading stats

Knowing how much health, silver, experience, etc. can be very useful, for example to determine which action to take (drink a potion, do a quest, work ..)

To make our bot read out your stats from the site, go to any BattleKnight page (after you are logged in), right-click anywhere on the page and choose “View Source”.
Then search for one of the values you have, for example my character has 277 silver, so I search (Ctrl+F in Chrome) for 277
Here is what I find:
BattleKnight bot reading your stats

Now, the easiest way for us to read the silver count into a PHP variable is to use a regular expression. If you do not have previous experience with regex (abbreviation for Regular Expressions) do not worry! I have a solution without using regex aswell.

With regex we can do like this:

preg_match( '/id="silverCount">([^<]+)<\/span>/s', $curl->data, $matches );
$silver = intval( trim( $matches[1] ) );

Without using regex we can do this:

$start = strpos( $curl->data, 'id="silverCount">' );
$end   = strpos( $curl->data, '</span>', $start + 1 );
$tmp   = substr( $curl->data, $start, ( $end - 1 ) - $start );

$silver = intval( trim( $tmp ) );

If you are going to do this without regex, you should write that into a function. Here is the function I use in these applications:

function strbetween( $string, $str1, $str2 )
{
  if( $str1 == '' )
  {
    $pos = 0;
  }
  else
  {
    $pos = strpos( $string, $str1 );

    if( $pos === false )
    {
      return false;
    }
  }

  $end = strpos( $string, $str2, ( $pos + 1 ) );

  return substr( $string, $pos + strlen( $str1 ), ( $end - $pos ) - strlen( $str1 ) );
}

To use this function with the example above, it would be:

$silver = intval( trim( strbetween( $curl->data, 'id="silverCount">', '</span>' ) ) );

Alright, so now we have got silver in a variable, let’s do the rest of the values we might need as well. If you look around where we found silver you will also see the value for health, experience, level and rubies. So let’s add those:

$health = intval( trim( strbetween( $curl->data, 'id="lifeCount">', '</span>' ) ) );
$silver = intval( trim( strbetween( $curl->data, 'id="silverCount">', '</span>' ) ) );
$exp = intval( trim( strbetween( $curl->data, 'id="levelCount">', '</span>' ) ) );
$level = intval( strip_tags( trim( strbetween( $curl->data, 'id="userLevel">', '</span>' ) ) ) );
$rubies = intval( trim( strbetween( $curl->data, 'id="rubyCount">', '</span>' ) ) );

You see that I added strip_tags function when fetching our character level? It is because the markup looks like this:

                    <div id="userLevel">
                        <span>
                        	5                        </span>
                        <!-- end #userLevel -->
                    </div>

What the strip_tags function does is to remove HTML tags from the string passed to it. With my strbetween function I get the content between <div id="userLevel"> and </span> which means that it would give us <span> 5 and we only want the 5 so we use strip_tags to remove the span tag.

trim function removes whitespace at the start/end of a string.
intval makes sure that the string is a number. If it is not a number it will return 0. You can read more about it here: php.net intval

BattleKnight bot – healing

So, our bot already knows how to perform missions, that is good. But what to do when we are low on health? We would not want to go on a quest then.. Instead, we should use a healing potion!
I will show you how to make the bot go through your inventory, how you can read out what is in your current inventory, and to use that information to find your health potions. Then we will tell the game to use one potion to heal our character.

(If you don’t have a healing potion, buy one)
Open up the Character screen. The URL of this page should be user/ at the end.
Right-click, “View page source”
Search for “PotionRed” (assuming it’s a red potion, if it’s yellow search “PotionYellow”)
BattleKnight bot use potions

This might look confusing to you, this is part of JSON data. Scroll up a bit and you can see that we are in an argument to the JavaScript function g_dragFunctions.storeAttributes. In this case we see that the first argument is 'inventory', if we look for other calls to g_dragFunctions.storeAttributes we find that the first argument differs. This function call is placed for each item in your inventory, if it is an equipped gear item, the first argument will be 'character'.
The last argument in the call is the one that contains our potion, here is the entire argument:

{"item_id":"3761265","quest_id":"0","item_level":"0","item_karma":"0","item_pic":"PotionRed50","item_own":"33xxx2","item_inventory":"1","item_info":{"posX":0,"posY":0},"item_width":"1","item_depth":"1","item_set":"0","item_name":"_db178","item_add":"0","item_magic_level":"white","item_value":"50","item_use":"0","item_slot":"0","item_rune1":"0","item_rune2":"0","item_rune3":"0","item_str":"0","item_dex":"0","item_end":"0","item_luck":"0","item_weapon":"0","item_shield":"0","item_ride":"0","item_critical":"0","item_speed":"0","item_off":"0","item_def":"0","item_damage":"0","item_damage2":"0","item_armour":"0","item_fire":"0","item_fire_res":"0","item_ice":"0","item_ice_res":"0","item_shock":"0","item_shock_res":"0","item_poison":"0","item_poison_res":"0","item_special_ability":[],"item_timestamp":"2013-03-09 12:03:50","clue_item":null,"clue_level":null,"clue_status":null,"clue_data":null,"item_manor_seconds":null,"item_fullName":"50 HP Healing Potion","item_ruby":0}

This is a JSON structure with item-data. What we want from this is probably the item_id.

Let’s write some code to grab all those function calls and filter so we only have the ‘inventory’ ones, and to parse out the last argument into a JSON object.

$item_id = 0; // This variable will hold the item_id of the red potion we find (if any)

$data = explode( 'g_dragFunctions.storeAttributes(', $curl->data );

foreach( $data as $temp )
{
  if( substr( $temp, 0, 11 ) != "'inventory'" ) // Check if first argument is 'inventory'
  {
    continue; // If it is Not, then we skip it
  }

  // Use the strbetween function from before to get the last argument of the call (the JSON item-data)
  $json_data = '{' . strbetween( $tmp, 'false,{', ');' );

  // json_decode function converts pure JSON data into a PHP object
  $json = json_decode( $json_data );

  if( substr( $json->item_pic, 0, 9 ) == 'PotionRed' ) // Check that it is a red potion
  {
    $item_id = intval( $json->item_id ); // Get the item ID

    break; // We have found a red potion, we do not need to continue parsing the inventory
  }
}

Now we want to use the potion.

Open the Network tab in the Developer Console of Chrome/Opera (or Tamper Data if you use Firefox)
Drag a potion to your character so that it is used
Copy the URL that the GET request was sent to, it should be similar to this:
http://s7.battleknight.co.uk/ajax/ajax/usePotion/?noCache=1360006512229&id=37xxx52&merchant=false&table=user

Now we can craft our own request and send to the server to use the potion. After the code above, add the following:

$url = $baseUrl . 'ajax/ajax/usePotion/?noCache=1&id=' . $item_id . '&merchant=false&table=user';

$curl->get( $url, $baseUrl . 'user/' );

Now our bot can use a potion! Put this into a function to keep everything nice & tidy.

function heal()
{
  $curl->get( $baseUrl . 'user/', $baseUrl ); // Open the character page

  $item_id = 0; // This variable will hold the item_id of the red potion we find (if any)

  $data = explode( 'g_dragFunctions.storeAttributes(', $curl->data );

  foreach( $data as $temp )
  {
    if( substr( $temp, 0, 11 ) != "'inventory'" ) // Check if first argument is 'inventory'
    {
      continue; // If it is Not, then we skip it
    }

    // Use the strbetween function from before to get the last argument of the call (the JSON item-data)
    $json_data = '{' . strbetween( $tmp, 'false,{', ');' );

    // json_decode function converts pure JSON data into a PHP object
    $json = json_decode( $json_data );

    if( substr( $json->item_pic, 0, 9 ) == 'PotionRed' ) // Check that it is a red potion
    {
      $item_id = intval( $json->item_id ); // Get the item ID

      break; // We have found a red potion, we do not need to continue parsing the inventory
    }
  }

  $url = $baseUrl . 'ajax/ajax/usePotion/?noCache=1&id=' . $item_id . '&merchant=false&table=user';

  $curl->get( $url, $baseUrl . 'user/' );
}

Now we can add in the cron.php of our BattleKnight bot, right before we call mission(); a check to see if we are in good health. For example:

if( $health < 100 )
{
  heal();
}

Pretty simple, right?

Here is the complete code right now:

cron.php

<?php
/*
 * @author  Johan / Asbra <johan@asbra.net>
 * @version 1.0.130309
 * @date    2013-03-05
 */
require 'curl.class.php';
$curl = new cURL();

require 'functions.php';

$username = 'Your_BattleKnight_Username';
$password = md5( 'Your_BattleKnight_Password' );
$server   = 15;

$baseUrl = 'http://s' . $server . '.battleknight.de/';

if( !login( $username, $password ) )
{
  echo 'Failed to log in!';
  echo $curl->data;
  exit;
}

if( $health < 100 )
{
  heal();
}

mission();

functions.php

<?php
/*
 * @author  Johan / Asbra
 * @version 1.0.130309
 * @date    2013-03-05
 */
function login( $username, $password )
{
  global $curl, $baseUrl;

  $url = $baseUrl . "/main/login/$username/$password?kid=&servername=null&serverlanguage=null";

  $data = $curl->get( $url, $baseUrl );

  if( strstr( $data, '<form id="loginForm"' ) ) // If the login form is on the page, we know that we failed to log in
  {
    return false;
  }

  return true;
}

function mission()
{
  global $curl, $baseUrl;

  $url = $baseUrl . 'world/location';

  $curl->get( $url );

  if( strstr( $curl->data, 'class="cooldownFight"' ) )
  {
    echo "Cooldown!";
    return false;
  }

  $data = array( 'chooseMission' => 'BanditLair', // 'BanditLair', 'Cave', 'StoneCircle' or 'Coast'
                 'missionArt'    => 'small', // 'small', 'medium' or 'large'
                 'missionKarma'  => 'Good', // 'Good' or 'Evil'
                 'buyRubies'     => '0', // I don't want to spend Rubies
  );

  $curl->post( $url, $data, $url );
  return true;
}

function heal()
{
  $curl->get( $baseUrl . 'user/', $baseUrl ); // Open the character page

  $item_id = 0; // This variable will hold the item_id of the red potion we find (if any)

  $data = explode( 'g_dragFunctions.storeAttributes(', $curl->data );

  foreach( $data as $temp )
  {
    if( substr( $temp, 0, 11 ) != "'inventory'" ) // Check if first argument is 'inventory'
    {
      continue; // If it is Not, then we skip it
    }

    // Use the strbetween function from before to get the last argument of the call (the JSON item-data)
    $json_data = '{' . strbetween( $tmp, 'false,{', ');' );

    // json_decode function converts pure JSON data into a PHP object
    $json = json_decode( $json_data );

    if( substr( $json->item_pic, 0, 9 ) == 'PotionRed' ) // Check that it is a red potion
    {
      $item_id = intval( $json->item_id ); // Get the item ID

      break; // We have found a red potion, we do not need to continue parsing the inventory
    }
  }

  $url = $baseUrl . 'ajax/ajax/usePotion/?noCache=1&id=' . $item_id . '&merchant=false&table=user';

  $curl->get( $url, $baseUrl . 'user/' );
}

I think this is enough for this time, stay tuned for the next part 🙂

Asbra

Posts Facebook

Blogging out of many years of experience with gamehacking, programming, reverse-engineering and general tomfoolery.

No Comments

Be the first to start the conversation.

Leave a Reply