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:
19: namespace GameQ\Protocols;
20:
21: use GameQ\Buffer;
22: use GameQ\Exception\Protocol as Exception;
23: use GameQ\Protocol;
24: use GameQ\Result;
25: use GameQ\Server;
26: use GameQ\Protocols\Http;
27:
28: /**
29: * GTA Five M Protocol Class
30: *
31: * Server base can be found at https://fivem.net/
32: *
33: * Based on code found at https://github.com/LiquidObsidian/fivereborn-query
34: *
35: * @author Austin Bischoff <austin@codebeard.com>
36: *
37: * Adding FiveM Player List by
38: * @author Jesse Lukas <eranio@g-one.org>
39: */
40: class Cfx extends Protocol
41: {
42:
43: /**
44: * Array of packets we want to look up.
45: * Each key should correspond to a defined method in this or a parent class
46: *
47: * @type array
48: */
49: protected $packets = [
50: self::PACKET_STATUS => "\xFF\xFF\xFF\xFFgetinfo xxx",
51: ];
52:
53: /**
54: * Use the response flag to figure out what method to run
55: *
56: * @type array
57: */
58: protected $responses = [
59: "\xFF\xFF\xFF\xFFinfoResponse" => "processStatus",
60: ];
61:
62: /**
63: * The query protocol used to make the call
64: *
65: * @type string
66: */
67: protected $protocol = 'cfx';
68:
69: /**
70: * String name of this protocol class
71: *
72: * @type string
73: */
74: protected $name = 'cfx';
75:
76: /**
77: * Longer string name of this protocol class
78: *
79: * @type string
80: */
81: protected $name_long = "CitizenFX";
82:
83: /**
84: * Holds the Player list so we can overwrite it back
85: *
86: * @var string
87: */
88: protected $PlayerList = [];
89:
90: /**
91: * Normalize settings for this protocol
92: *
93: * @type array
94: */
95: protected $normalize = [
96: // General
97: 'general' => [
98: // target => source
99: 'gametype' => 'gametype',
100: 'hostname' => 'hostname',
101: 'mapname' => 'mapname',
102: 'maxplayers' => 'sv_maxclients',
103: 'mod' => 'gamename',
104: 'numplayers' => 'clients',
105: 'password' => 'privateClients',
106: ],
107: ];
108:
109: /**
110: * Get FiveM players list using a sub query
111: */
112: public function beforeSend(Server $server)
113: {
114: $GameQ = new \GameQ\GameQ();
115: $GameQ->addServer([
116: 'type' => 'cfxplayers',
117: 'host' => "$server->ip:$server->port_query",
118: ]);
119: $results = $GameQ->process();
120: $this->PlayerList = isset($results[0]) && isset($results[0][0]) ? $results[0][0] : [];
121: }
122:
123: /**
124: * Process the response
125: *
126: * @return array
127: * @throws \GameQ\Exception\Protocol
128: */
129: public function processResponse()
130: {
131: // In case it comes back as multiple packets (it shouldn't)
132: $buffer = new Buffer(implode('', $this->packets_response));
133:
134: // Figure out what packet response this is for
135: $response_type = $buffer->readString(PHP_EOL);
136:
137: // Figure out which packet response this is
138: if (empty($response_type) || !array_key_exists($response_type, $this->responses)) {
139: throw new Exception(__METHOD__ . " response type '{$response_type}' is not valid");
140: }
141:
142: // Offload the call
143: $results = call_user_func_array([$this, $this->responses[$response_type]], [$buffer]);
144:
145: return $results;
146: }
147:
148: /*
149: * Internal methods
150: */
151:
152: /**
153: * Handle processing the status response
154: *
155: * @param Buffer $buffer
156: *
157: * @return array
158: */
159: protected function processStatus(Buffer $buffer)
160: {
161: // Set the result to a new result instance
162: $result = new Result();
163:
164: // Lets peek and see if the data starts with a \
165: if ($buffer->lookAhead(1) == '\\') {
166: // Burn the first one
167: $buffer->skip(1);
168: }
169:
170: // Explode the data
171: $data = explode('\\', $buffer->getBuffer());
172:
173: // No longer needed
174: unset($buffer);
175:
176: $itemCount = count($data);
177:
178: // Now lets loop the array
179: for ($x = 0; $x < $itemCount; $x += 2) {
180: // Set some local vars
181: $key = $data[$x];
182: $val = $data[$x + 1];
183:
184: if (in_array($key, ['challenge'])) {
185: continue; // skip
186: }
187:
188: // Regular variable so just add the value.
189: $result->add($key, $val);
190: }
191:
192: // Add result of sub http-protocol if available
193: if ($this->PlayerList) {
194: $result->add('players', $this->PlayerList);
195: }
196:
197: return $result->fetch();
198: }
199: }
200: