Example #1
0
 function parseblocks()
 {
     $data_left = strlen($this->data);
     while ($data_left > $this->max_datablock) {
         //$prev = $block_id;
         $block_id = ord($this->data[0]);
         switch ($block_id) {
             // TimeSlot block
             case 0x1e:
             case 0x1f:
                 $temp = unpack('x1/vlength/vtime_inc', $this->data);
                 if (!$this->pause) {
                     $this->time += $temp['time_inc'];
                 }
                 if ($temp['length'] > 2 && $this->parse_actions) {
                     $this->parseactions(substr($this->data, 5, $temp['length'] - 2), $temp['length'] - 2);
                 }
                 $this->data = substr($this->data, $temp['length'] + 3);
                 $data_left -= $temp['length'] + 3;
                 break;
                 // Player chat message (patch version >= 1.07)
             // Player chat message (patch version >= 1.07)
             case 0x20:
                 // before 1.03 0x20 was used instead 0x22
                 if ($this->header['major_v'] > 2) {
                     $temp = unpack('x1/Cplayer_id/vlength/Cflags/vmode', $this->data);
                     if ($temp['flags'] == 0x20) {
                         $temp['mode'] = convert_chat_mode($temp['mode']);
                         $temp['text'] = substr($this->data, 9, $temp['length'] - 6);
                     } elseif ($temp['flags'] == 0x10) {
                         // those are strange messages, they aren't visible when
                         // watching the replay but they are present; they have no mode
                         $temp['text'] = substr($this->data, 7, $temp['length'] - 3);
                         unset($temp['mode']);
                     }
                     $this->data = substr($this->data, $temp['length'] + 4);
                     $data_left -= $temp['length'] + 4;
                     $temp['time'] = $this->time;
                     $temp['player_name'] = $this->players[$temp['player_id']]['name'];
                     $this->chat[] = $temp;
                     break;
                 }
                 // unknown (Random number/seed for next frame)
             // unknown (Random number/seed for next frame)
             case 0x22:
                 $temp = ord($this->data[1]);
                 $this->data = substr($this->data, $temp + 2);
                 $data_left -= $temp + 2;
                 break;
                 // unknown (startblocks)
             // unknown (startblocks)
             case 0x1a:
             case 0x1b:
             case 0x1c:
                 $this->data = substr($this->data, 5);
                 $data_left -= 5;
                 break;
                 // unknown (very rare, appears in front of a 'LeaveGame' action)
             // unknown (very rare, appears in front of a 'LeaveGame' action)
             case 0x23:
                 $this->data = substr($this->data, 11);
                 $data_left -= 11;
                 break;
                 // Forced game end countdown (map is revealed)
             // Forced game end countdown (map is revealed)
             case 0x2f:
                 $this->data = substr($this->data, 9);
                 $data_left -= 9;
                 break;
                 // LeaveGame
             // LeaveGame
             case 0x17:
                 $this->leaves++;
                 $temp = unpack('x1/Vreason/Cplayer_id/Vresult/Vunknown', $this->data);
                 $this->players[$temp['player_id']]['time'] = $this->time;
                 $this->players[$temp['player_id']]['leave_reason'] = $temp['reason'];
                 $this->players[$temp['player_id']]['leave_result'] = $temp['result'];
                 $this->data = substr($this->data, 14);
                 $data_left -= 14;
                 if ($this->leave_unknown) {
                     $this->leave_unknown = $temp['unknown'] - $this->leave_unknown;
                 }
                 $this->game['saver_id'] = $temp['player_id'];
                 if ($this->leaves == $this->game['player_count']) {
                     $this->game['saver_id'] = $temp['player_id'];
                     $this->game['saver_name'] = $this->players[$temp['player_id']]['name'];
                 }
                 if ($temp['reason'] == 0x1) {
                     switch ($temp['result']) {
                         case 0x8:
                             $this->game['loser_team'] = $this->players[$temp['player_id']]['team'];
                             break;
                         case 0x9:
                             $this->game['winner_team'] = $this->players[$temp['player_id']]['team'];
                             break;
                         case 0xa:
                             $this->game['loser_team'] = 'tie';
                             $this->game['winner_team'] = 'tie';
                             break;
                     }
                 } elseif ($temp['reason'] == 0xc && $this->game['saver_id']) {
                     switch ($temp['result']) {
                         case 0x7:
                             if ($this->leave_unknown > 0 && $this->continue_game) {
                                 $this->game['winner_team'] = $this->players[$this->game['saver_id']]['team'];
                             } else {
                                 $this->game['loser_team'] = $this->players[$this->game['saver_id']]['team'];
                             }
                             break;
                         case 0x8:
                             $this->game['loser_team'] = $this->players[$this->game['saver_id']]['team'];
                             break;
                         case 0x9:
                             $this->game['winner_team'] = $this->players[$this->game['saver_id']]['team'];
                             break;
                         case 0xb:
                             // this isn't correct according to w3g_format but generally works...
                             if ($this->leave_unknown > 0) {
                                 $this->game['winner_team'] = $this->players[$this->game['saver_id']]['team'];
                             }
                             break;
                     }
                 } elseif ($temp['reason'] == 0xc) {
                     switch ($temp['result']) {
                         case 0x7:
                             $this->game['loser_team'] = 99;
                             break;
                             // saver
                         // saver
                         case 0x8:
                             $this->game['winner_team'] = $this->players[$temp['player_id']]['team'];
                             break;
                         case 0x9:
                             $this->game['winner_team'] = 99;
                             break;
                             // saver
                         // saver
                         case 0xa:
                             $this->game['loser_team'] = 'tie';
                             $this->game['winner_team'] = 'tie';
                             break;
                     }
                 }
                 $this->leave_unknown = $temp['unknown'];
                 break;
             case 0:
                 $data_left = 0;
                 break;
             default:
                 $this->data = substr($this->data, 1);
                 $data_left -= 1;
                 break;
                 //exit('Unhandled replay command block: 0x'.sprintf('%02X', $block_id).' (prev: 0x'.sprintf('%02X', $prev).', time: '.$this->time.') in '.$this->filename);
         }
     }
 }
Example #2
0
 function parseactions($actionblock, $data_length)
 {
     $block_length = 0;
     $action = 0;
     while ($data_length) {
         if ($block_length) {
             $actionblock = substr($actionblock, $block_length);
         }
         $temp = unpack('Cplayer_id/vlength', $actionblock);
         $player_id = $temp['player_id'];
         $block_length = $temp['length'] + 3;
         $data_length -= $block_length;
         $was_deselect = false;
         $was_subupdate = false;
         $was_subgroup = false;
         $n = 3;
         while ($n < $block_length) {
             $prev = $action;
             $action = ord($actionblock[$n]);
             switch ($action) {
                 // Unit/building ability (no additional parameters)
                 // here we detect the races, heroes, units, items, buildings,
                 // upgrades
                 case 0x10:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag is one byte longer
                     }
                     $itemid = strrev(substr($actionblock, $n + 2, 4));
                     $value = convert_itemid($itemid);
                     if (!$value) {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                         // handling Destroyers
                         if (ord($actionblock[$n + 2]) == 0x33 && ord($actionblock[$n + 3]) == 0x2) {
                             $name = substr(convert_itemid('ubsp'), 2);
                             $this->players[$player_id]['units']['order'][$this->time] = $this->players[$player_id]['units_multiplier'] . ' ' . $name;
                             $this->players[$player_id]['units'][$name]++;
                             $name = substr(convert_itemid('uobs'), 2);
                             $this->players[$player_id]['units'][$name]--;
                         }
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('buildtrain')]++;
                         if (!$this->players[$player_id]['race_detected']) {
                             if ($race_detected = convert_race($itemid)) {
                                 $this->players[$player_id]['race_detected'] = $race_detected;
                             }
                         }
                         $name = substr($value, 2);
                         switch ($value[0]) {
                             case 'u':
                                 // preventing duplicated units
                                 if ($this->time - $this->players[$player_id]['units_time'] > ACTION_DELAY || $itemid != $this->players[$player_id]['last_itemid'] || ($itemid == 'hpea' || $itemid == 'ewsp' || $itemid == 'opeo' || $itemid == 'uaco') && $this->time - $this->players[$player_id]['units_time'] > 0) {
                                     $this->players[$player_id]['units_time'] = $this->time;
                                     $this->players[$player_id]['units']['order'][$this->time] = $this->players[$player_id]['units_multiplier'] . ' ' . $name;
                                     $this->players[$player_id]['units'][$name] += $this->players[$player_id]['units_multiplier'];
                                 }
                                 break;
                             case 'b':
                                 $this->players[$player_id]['buildings']['order'][$this->time] = $name;
                                 $this->players[$player_id]['buildings'][$name]++;
                                 break;
                             case 'h':
                                 $this->players[$player_id]['heroes']['order'][$this->time] = $name;
                                 $this->players[$player_id]['heroes'][$name]['revivals']++;
                                 break;
                             case 'a':
                                 list($hero, $ability) = explode(':', $name);
                                 $retraining_time = $this->players[$player_id]['retraining_time'];
                                 if (!$this->players[$player_id]['heroes'][$hero]['retraining_time']) {
                                     $this->players[$player_id]['heroes'][$hero]['retraining_time'] = 0;
                                 }
                                 // preventing too high levels (avoiding duplicated actions)
                                 // the second condition is mainly for games with random heroes
                                 // the third is for handling Tome of Retraining usage
                                 if (($this->time - $this->players[$player_id]['heroes'][$hero]['ability_time'] > ACTION_DELAY || !$this->players[$player_id]['heroes'][$hero]['ability_time'] || $this->time - $retraining_time < RETRAINING_TIME) && $this->players[$player_id]['heroes'][$hero]['abilities'][$retraining_time][$ability] < 3) {
                                     if ($this->time - $retraining_time > RETRAINING_TIME) {
                                         $this->players[$player_id]['heroes'][$hero]['ability_time'] = $this->time;
                                         $this->players[$player_id]['heroes'][$hero]['level']++;
                                         $this->players[$player_id]['heroes'][$hero]['abilities'][$this->players[$player_id]['heroes'][$hero]['retraining_time']][$ability]++;
                                     } else {
                                         $this->players[$player_id]['heroes'][$hero]['retraining_time'] = $retraining_time;
                                         $this->players[$player_id]['heroes'][$hero]['abilities']['order'][$retraining_time] = 'Retraining';
                                         $this->players[$player_id]['heroes'][$hero]['abilities'][$retraining_time][$ability]++;
                                     }
                                     $this->players[$player_id]['heroes'][$hero]['abilities']['order'][$this->time] = $ability;
                                 }
                                 break;
                             case 'i':
                                 $this->players[$player_id]['items']['order'][$this->time] = $name;
                                 $this->players[$player_id]['items'][$name]++;
                                 if ($itemid == 'tret') {
                                     $this->players[$player_id]['retraining_time'] = $this->time;
                                 }
                                 break;
                             case 'p':
                                 // preventing duplicated upgrades
                                 if ($this->time - $this->players[$player_id]['upgrades_time'] > ACTION_DELAY || $itemid != $this->players[$player_id]['last_itemid']) {
                                     $this->players[$player_id]['upgrades_time'] = $this->time;
                                     $this->players[$player_id]['upgrades']['order'][$this->time] = $name;
                                     $this->players[$player_id]['upgrades'][$name]++;
                                 }
                                 break;
                             default:
                                 $this->errors[$this->time] = 'Unknown ItemID at ' . convert_time($this->time) . ': ' . $value;
                                 break;
                         }
                         $this->players[$player_id]['last_itemid'] = $itemid;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 14;
                     } else {
                         $n += 6;
                     }
                     break;
                     // Unit/building ability (with target position)
                 // Unit/building ability (with target position)
                 case 0x11:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     $value = strrev(substr($actionblock, $n + 2, 4));
                     if ($value = convert_buildingid($value)) {
                         $this->players[$player_id]['buildings']['order'][$this->time] = $value;
                         $this->players[$player_id]['buildings'][$value]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 22;
                     } else {
                         $n += 14;
                     }
                     break;
                     // Unit/building ability (with target position and target object ID)
                 // Unit/building ability (with target position and target object ID)
                 case 0x12:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) == 0x3 && ord($actionblock[$n + 3]) == 0x0) {
                         // rightclick
                         $this->players[$player_id]['actions_details'][convert_action('rightclick')]++;
                     } elseif (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 30;
                     } else {
                         $n += 22;
                     }
                     break;
                     // Give item to Unit / Drop item on ground
                 // Give item to Unit / Drop item on ground
                 case 0x13:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     $this->players[$player_id]['actions_details'][convert_action('item')]++;
                     if ($this->header['major_v'] >= 7) {
                         $n += 38;
                     } else {
                         $n += 30;
                     }
                     break;
                     // Unit/building ability (with two target positions and two item IDs)
                 // Unit/building ability (with two target positions and two item IDs)
                 case 0x14:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 13) {
                         $n++;
                         // ability flag
                     }
                     if (ord($actionblock[$n + 2]) == 0x3 && ord($actionblock[$n + 3]) == 0x0) {
                         // rightclick
                         $this->players[$player_id]['actions_details'][convert_action('rightclick')]++;
                     } elseif (ord($actionblock[$n + 2]) <= 0x19 && ord($actionblock[$n + 3]) == 0x0) {
                         // basic commands
                         $this->players[$player_id]['actions_details'][convert_action('basic')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('ability')]++;
                     }
                     if ($this->header['major_v'] >= 7) {
                         $n += 43;
                     } else {
                         $n += 35;
                     }
                     break;
                     // Change Selection (Unit, Building, Area)
                 // Change Selection (Unit, Building, Area)
                 case 0x16:
                     $temp = unpack('Cmode/vnum', substr($actionblock, $n + 1, 3));
                     if ($temp['mode'] == 0x2 || !$was_deselect) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('select')]++;
                     }
                     $was_deselect = $temp['mode'] == 0x2;
                     $this->players[$player_id]['units_multiplier'] = $temp['num'];
                     $n += 4 + $temp['num'] * 8;
                     break;
                     // Assign Group Hotkey
                 // Assign Group Hotkey
                 case 0x17:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('assignhotkey')]++;
                     $temp = unpack('Cgroup/vnum', substr($actionblock, $n + 1, 3));
                     $this->players[$player_id]['hotkeys'][$temp['group']]['assigned']++;
                     $this->players[$player_id]['hotkeys'][$temp['group']]['last_totalitems'] = $temp['num'];
                     $n += 4 + $temp['num'] * 8;
                     break;
                     // Select Group Hotkey
                 // Select Group Hotkey
                 case 0x18:
                     $this->players[$player_id]['actions'][] = $this->time;
                     // Won't show up in APM stats, if we comment this line
                     $this->players[$player_id]['actions_details'][convert_action('selecthotkey')]++;
                     $this->players[$player_id]['hotkeys'][ord($actionblock[$n + 1])]['used']++;
                     $this->players[$player_id]['units_multiplier'] = $this->players[$player_id]['hotkeys'][ord($actionblock[$n + 1])]['last_totalitems'];
                     $n += 3;
                     break;
                     // Select Subgroup
                 // Select Subgroup
                 case 0x19:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         if ($was_subgroup) {
                             // can't think of anything better (check action 0x1A)
                             $this->players[$player_id]['actions'][] = $this->time;
                             $this->players[$player_id]['actions_details'][convert_action('subgroup')]++;
                             // I don't have any better idea what to do when somebody binds buildings
                             // of more than one type to a single key and uses them to train units
                             // TODO: this is rarely executed, maybe it should go after if ($was_subgroup) {}?
                             $this->players[$player_id]['units_multiplier'] = 1;
                         }
                         $n += 13;
                     } else {
                         if (ord($actionblock[$n + 1]) != 0 && ord($actionblock[$n + 1]) != 0xff && !$was_subupdate) {
                             $this->players[$player_id]['actions'][] = $this->time;
                             $this->players[$player_id]['actions_details'][convert_action('subgroup')]++;
                         }
                         $was_subupdate = ord($actionblock[$n + 1]) == 0xff;
                         $n += 2;
                     }
                     break;
                     // some subaction holder?
                     // version < 14b: Only in scenarios, maybe a trigger-related command
                 // some subaction holder?
                 // version < 14b: Only in scenarios, maybe a trigger-related command
                 case 0x1a:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $n += 1;
                         $was_subgroup = $prev == 0x19 || $prev == 0;
                         //0 is for new blocks which start with 0x19
                     } else {
                         $n += 10;
                     }
                     break;
                     // Only in scenarios, maybe a trigger-related command
                     // version < 14b: Select Ground Item
                 // Only in scenarios, maybe a trigger-related command
                 // version < 14b: Select Ground Item
                 case 0x1b:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $n += 10;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 10;
                     }
                     break;
                     // Select Ground Item
                     // version < 14b: Cancel hero revival (new in 1.13)
                 // Select Ground Item
                 // version < 14b: Cancel hero revival (new in 1.13)
                 case 0x1c:
                     // OR is for torunament reps which don't have build_v
                     if ($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 10;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 9;
                     }
                     break;
                     // Cancel hero revival
                     // Remove unit from building queue
                 // Cancel hero revival
                 // Remove unit from building queue
                 case 0x1d:
                 case 0x1e:
                     // OR is for torunament reps which don't have build_v
                     if (($this->header['build_v'] >= 6040 || $this->header['major_v'] > 14) && $action != 0x1e) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $n += 9;
                     } else {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('removeunit')]++;
                         $value = convert_itemid(strrev(substr($actionblock, $n + 2, 4)));
                         $name = substr($value, 2);
                         switch ($value[0]) {
                             case 'u':
                                 // preventing duplicated units cancellations
                                 if ($this->time - $this->players[$player_id]['runits_time'] > ACTION_DELAY || $value != $this->players[$player_id]['runits_value']) {
                                     $this->players[$player_id]['runits_time'] = $this->time;
                                     $this->players[$player_id]['runits_value'] = $value;
                                     $this->players[$player_id]['units']['order'][$this->time] = '-1 ' . $name;
                                     $this->players[$player_id]['units'][$name]--;
                                 }
                                 break;
                             case 'b':
                                 $this->players[$player_id]['buildings'][$name]--;
                                 break;
                             case 'h':
                                 $this->players[$player_id]['heroes'][$name]['revivals']--;
                                 break;
                             case 'p':
                                 // preventing duplicated upgrades cancellations
                                 if ($this->time - $this->players[$player_id]['rupgrades_time'] > ACTION_DELAY || $value != $this->players[$player_id]['rupgrades_value']) {
                                     $this->players[$player_id]['rupgrades_time'] = $this->time;
                                     $this->players[$player_id]['rupgrades_value'] = $value;
                                     $this->players[$player_id]['upgrades'][$name]--;
                                 }
                                 break;
                         }
                         $n += 6;
                     }
                     break;
                     // Found in replays with patch version 1.04 and 1.05.
                 // Found in replays with patch version 1.04 and 1.05.
                 case 0x21:
                     $n += 9;
                     break;
                     // Change ally options
                 // Change ally options
                 case 0x50:
                     $n += 6;
                     break;
                     // Transfer resources
                 // Transfer resources
                 case 0x51:
                     $n += 10;
                     break;
                     // Map trigger chat command (?)
                 // Map trigger chat command (?)
                 case 0x60:
                     $n += 9;
                     while ($actionblock[$n] != "") {
                         $n++;
                     }
                     ++$n;
                     break;
                     // ESC pressed
                 // ESC pressed
                 case 0x61:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('esc')]++;
                     ++$n;
                     break;
                     // Scenario Trigger
                 // Scenario Trigger
                 case 0x62:
                     if ($this->header['major_v'] >= 7) {
                         $n += 13;
                     } else {
                         $n += 9;
                     }
                     break;
                     // Enter select hero skill submenu for WarCraft III patch version <= 1.06
                 // Enter select hero skill submenu for WarCraft III patch version <= 1.06
                 case 0x65:
                     $this->players[$player_id]['actions'][] = $this->time;
                     $this->players[$player_id]['actions_details'][convert_action('heromenu')]++;
                     ++$n;
                     break;
                     // Enter select hero skill submenu
                     // Enter select building submenu for WarCraft III patch version <= 1.06
                 // Enter select hero skill submenu
                 // Enter select building submenu for WarCraft III patch version <= 1.06
                 case 0x66:
                     $this->players[$player_id]['actions'][] = $this->time;
                     if ($this->header['major_v'] >= 7) {
                         $this->players[$player_id]['actions_details'][convert_action('heromenu')]++;
                     } else {
                         $this->players[$player_id]['actions_details'][convert_action('buildmenu')]++;
                     }
                     $n += 1;
                     break;
                     // Enter select building submenu
                     // Minimap signal (ping) for WarCraft III patch version <= 1.06
                 // Enter select building submenu
                 // Minimap signal (ping) for WarCraft III patch version <= 1.06
                 case 0x67:
                     if ($this->header['major_v'] >= 7) {
                         $this->players[$player_id]['actions'][] = $this->time;
                         $this->players[$player_id]['actions_details'][convert_action('buildmenu')]++;
                         $n += 1;
                     } else {
                         $n += 13;
                     }
                     break;
                     // Minimap signal (ping)
                     // Continue Game (BlockB) for WarCraft III patch version <= 1.06
                 // Minimap signal (ping)
                 // Continue Game (BlockB) for WarCraft III patch version <= 1.06
                 case 0x68:
                     if ($this->header['major_v'] >= 7) {
                         $n += 13;
                     } else {
                         $n += 17;
                     }
                     break;
                     // Continue Game (BlockB)
                     // Continue Game (BlockA) for WarCraft III patch version <= 1.06
                 // Continue Game (BlockB)
                 // Continue Game (BlockA) for WarCraft III patch version <= 1.06
                 case 0x69:
                     // Continue Game (BlockA)
                 // Continue Game (BlockA)
                 case 0x6a:
                     $this->continue_game = 1;
                     $n += 17;
                     break;
                     // Pause game
                 // Pause game
                 case 0x1:
                     $this->pause = true;
                     $temp = '';
                     $temp['time'] = $this->time;
                     $temp['text'] = convert_chat_mode(0xfe, $this->players[$player_id]['name']);
                     $this->chat[] = $temp;
                     $n += 1;
                     break;
                     // Resume game
                 // Resume game
                 case 0x2:
                     $temp = '';
                     $this->pause = false;
                     $temp['time'] = $this->time;
                     $temp['text'] = convert_chat_mode(0xff, $this->players[$player_id]['name']);
                     $this->chat[] = $temp;
                     $n += 1;
                     break;
                     // Increase game speed in single player game (Num+)
                 // Increase game speed in single player game (Num+)
                 case 0x4:
                     // Decrease game speed in single player game (Num-)
                 // Decrease game speed in single player game (Num-)
                 case 0x5:
                     $n += 1;
                     break;
                     // Set game speed in single player game (options menu)
                 // Set game speed in single player game (options menu)
                 case 0x3:
                     $n += 2;
                     break;
                     // Save game
                 // Save game
                 case 0x6:
                     $i = 1;
                     while ($actionblock[$n] != "") {
                         $n++;
                     }
                     $n += 1;
                     break;
                     // Save game finished
                 // Save game finished
                 case 0x7:
                     $n += 5;
                     break;
                     // Only in scenarios, maybe a trigger-related command
                 // Only in scenarios, maybe a trigger-related command
                 case 0x75:
                     $n += 2;
                     break;
                 default:
                     $temp = '';
                     for ($i = 3; $i < $n; $i++) {
                         // first 3 bytes are player ID and length
                         $temp .= sprintf('%02X', ord($actionblock[$i])) . ' ';
                     }
                     $temp .= '[' . sprintf('%02X', ord($actionblock[$n])) . '] ';
                     for ($i = 1; $n + $i < strlen($actionblock); $i++) {
                         $temp .= sprintf('%02X', ord($actionblock[$n + $i])) . ' ';
                     }
                     $this->errors[] = 'Unknown action at ' . convert_time($this->time) . ': 0x' . sprintf('%02X', $action) . ', prev: 0x' . sprintf('%02X', $prev) . ', dump: ' . $temp;
                     // skip to the next CommandBlock
                     // continue 3, not 2 because of http://php.net/manual/en/control-structures.continue.php#68193
                     // ('Current functionality treats switch structures as looping in regards to continue.')
                     continue 3;
             }
         }
         $was_deselect = $action == 0x16;
         $was_subupdate = $action == 0x19;
     }
 }