1
0

Arma3.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. <?php
  2. /**
  3. * This file is part of GameQ.
  4. *
  5. * GameQ is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU Lesser General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * GameQ is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Lesser General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Lesser General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. namespace GameQ\Protocols;
  19. use GameQ\Buffer;
  20. use GameQ\Result;
  21. /**
  22. * Class Armed Assault 3
  23. *
  24. * Rules protocol reference: https://community.bistudio.com/wiki/Arma_3_ServerBrowserProtocol2
  25. *
  26. * @package GameQ\Protocols
  27. * @author Austin Bischoff <[email protected]>
  28. * @author Memphis017 <https://github.com/Memphis017>
  29. */
  30. class Arma3 extends Source
  31. {
  32. // Base DLC names
  33. const BASE_DLC_KART = 'Karts';
  34. const BASE_DLC_MARKSMEN = 'Marksmen';
  35. const BASE_DLC_HELI = 'Helicopters';
  36. const BASE_DLC_CURATOR = 'Curator';
  37. const BASE_DLC_EXPANSION = 'Expansion';
  38. const BASE_DLC_JETS = 'Jets';
  39. const BASE_DLC_ORANGE = 'Laws of War';
  40. const BASE_DLC_ARGO = 'Malden';
  41. const BASE_DLC_TACOPS = 'Tac-Ops';
  42. const BASE_DLC_TANKS = 'Tanks';
  43. const BASE_DLC_CONTACT = 'Contact';
  44. const BASE_DLC_ENOCH = 'Contact (Platform)';
  45. // Special
  46. const BASE_DLC_AOW = 'Art of War';
  47. // Creator DLC names
  48. const CREATOR_DLC_GM = 'Global Mobilization';
  49. const CREATOR_DLC_VN = 'S.O.G. Prairie Fire';
  50. const CREATOR_DLC_CSLA = 'ČSLA - Iron Curtain';
  51. const CREATOR_DLC_WS = 'Western Sahara';
  52. /**
  53. * DLC Flags/Bits as defined in the documentation.
  54. *
  55. * @see https://community.bistudio.com/wiki/Arma_3:_ServerBrowserProtocol3
  56. *
  57. * @var array
  58. */
  59. protected $dlcFlags = [
  60. 0b0000000000000001 => self::BASE_DLC_KART,
  61. 0b0000000000000010 => self::BASE_DLC_MARKSMEN,
  62. 0b0000000000000100 => self::BASE_DLC_HELI,
  63. 0b0000000000001000 => self::BASE_DLC_CURATOR,
  64. 0b0000000000010000 => self::BASE_DLC_EXPANSION,
  65. 0b0000000000100000 => self::BASE_DLC_JETS,
  66. 0b0000000001000000 => self::BASE_DLC_ORANGE,
  67. 0b0000000010000000 => self::BASE_DLC_ARGO,
  68. 0b0000000100000000 => self::BASE_DLC_TACOPS,
  69. 0b0000001000000000 => self::BASE_DLC_TANKS,
  70. 0b0000010000000000 => self::BASE_DLC_CONTACT,
  71. 0b0000100000000000 => self::BASE_DLC_ENOCH,
  72. 0b0001000000000000 => self::BASE_DLC_AOW,
  73. 0b0010000000000000 => 'Unknown',
  74. 0b0100000000000000 => 'Unknown',
  75. 0b1000000000000000 => 'Unknown',
  76. ];
  77. /**
  78. * String name of this protocol class
  79. *
  80. * @type string
  81. */
  82. protected $name = 'arma3';
  83. /**
  84. * Longer string name of this protocol class
  85. *
  86. * @type string
  87. */
  88. protected $name_long = "Arma3";
  89. /**
  90. * Query port = client_port + 1
  91. *
  92. * @type int
  93. */
  94. protected $port_diff = 1;
  95. /**
  96. * Process the rules since Arma3 changed their response for rules
  97. *
  98. * @param Buffer $buffer
  99. *
  100. * @return array
  101. * @throws \GameQ\Exception\Protocol
  102. */
  103. protected function processRules(Buffer $buffer)
  104. {
  105. // Total number of packets, burn it
  106. $buffer->readInt16();
  107. // Will hold the data string
  108. $data = '';
  109. // Loop until we run out of strings
  110. while ($buffer->getLength()) {
  111. // Burn the delimiters (i.e. \x01\x04\x00)
  112. $buffer->readString();
  113. // Add the data to the string, we are reassembling it
  114. $data .= $buffer->readString();
  115. }
  116. // Restore escaped sequences
  117. $data = str_replace(["\x01\x01", "\x01\x02", "\x01\x03"], ["\x01", "\x00", "\xFF"], $data);
  118. // Make a new buffer with the reassembled data
  119. $responseBuffer = new Buffer($data);
  120. // Kill the old buffer, should be empty
  121. unset($buffer, $data);
  122. // Set the result to a new result instance
  123. $result = new Result();
  124. // Get results
  125. $result->add('rules_protocol_version', $responseBuffer->readInt8()); // read protocol version
  126. $result->add('overflow', $responseBuffer->readInt8()); // Read overflow flags
  127. $dlcByte = $responseBuffer->readInt8(); // Grab DLC byte 1 and use it later
  128. $dlcByte2 = $responseBuffer->readInt8(); // Grab DLC byte 2 and use it later
  129. $dlcBits = ($dlcByte2 << 8) | $dlcByte; // concatenate DLC bits to 16 Bit int
  130. // Grab difficulty so we can man handle it...
  131. $difficulty = $responseBuffer->readInt8();
  132. // Process difficulty
  133. $result->add('3rd_person', $difficulty >> 7);
  134. $result->add('advanced_flight_mode', ($difficulty >> 6) & 1);
  135. $result->add('difficulty_ai', ($difficulty >> 3) & 3);
  136. $result->add('difficulty_level', $difficulty & 3);
  137. unset($difficulty);
  138. // Crosshair
  139. $result->add('crosshair', $responseBuffer->readInt8());
  140. // Loop over the base DLC bits so we can pull in the info for the DLC (if enabled)
  141. foreach ($this->dlcFlags as $dlcFlag => $dlcName) {
  142. // Check that the DLC bit is enabled
  143. if (($dlcBits & $dlcFlag) === $dlcFlag) {
  144. // Add the DLC to the list
  145. $result->addSub('dlcs', 'name', $dlcName);
  146. $result->addSub('dlcs', 'hash', dechex($responseBuffer->readInt32()));
  147. }
  148. }
  149. // Read the mount of mods, these include DLC as well as Creator DLC and custom modifications
  150. $modCount = $responseBuffer->readInt8();
  151. // Add mod count
  152. $result->add('mod_count', $modCount);
  153. // Loop over the mods
  154. while ($modCount) {
  155. // Read the mods hash
  156. $result->addSub('mods', 'hash', dechex($responseBuffer->readInt32()));
  157. // Get the information byte containing DLC flag and steamId length
  158. $infoByte = $responseBuffer->readInt8();
  159. // Determine isDLC by flag, first bit in upper nibble
  160. $result->addSub('mods', 'dlc', ($infoByte & 0b00010000) === 0b00010000);
  161. // Read the steam id of the mod/CDLC (might be less than 4 bytes)
  162. $result->addSub('mods', 'steam_id', $responseBuffer->readInt32($infoByte & 0x0F));
  163. // Read the name of the mod
  164. $result->addSub('mods', 'name', $responseBuffer->readPascalString(0, true) ?: 'Unknown');
  165. --$modCount;
  166. }
  167. // No longer needed
  168. unset($dlcByte, $dlcByte2, $dlcBits);
  169. // Get the signatures count
  170. $signatureCount = $responseBuffer->readInt8();
  171. $result->add('signature_count', $signatureCount);
  172. // Make signatures array
  173. $signatures = [];
  174. // Loop until we run out of signatures
  175. for ($x = 0; $x < $signatureCount; $x++) {
  176. $signatures[] = $responseBuffer->readPascalString(0, true);
  177. }
  178. // Add as a simple array
  179. $result->add('signatures', $signatures);
  180. unset($responseBuffer, $signatureCount, $signatures, $x);
  181. return $result->fetch();
  182. }
  183. }