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\Protocol;
23: use GameQ\Result;
24:
25: /**
26: * All-Seeing Eye Protocol class
27: *
28: * @author Marcel Bößendörfer <m.boessendoerfer@marbis.net>
29: * @author Austin Bischoff <austin@codebeard.com>
30: */
31: class Ase extends Protocol
32: {
33: /**
34: * Array of packets we want to look up.
35: * Each key should correspond to a defined method in this or a parent class
36: *
37: * @var array
38: */
39: protected $packets = [
40: self::PACKET_ALL => "s",
41: ];
42:
43: /**
44: * The query protocol used to make the call
45: *
46: * @var string
47: */
48: protected $protocol = 'ase';
49:
50: /**
51: * String name of this protocol class
52: *
53: * @var string
54: */
55: protected $name = 'ase';
56:
57: /**
58: * Longer string name of this protocol class
59: *
60: * @var string
61: */
62: protected $name_long = "All-Seeing Eye";
63:
64: /**
65: * Normalize settings for this protocol
66: *
67: * @var array
68: */
69: protected $normalize = [
70: // General
71: 'general' => [
72: // target => source
73: 'dedicated' => 'dedicated',
74: 'gametype' => 'gametype',
75: 'hostname' => 'servername',
76: 'mapname' => 'map',
77: 'maxplayers' => 'max_players',
78: 'mod' => 'game_dir',
79: 'numplayers' => 'num_players',
80: 'password' => 'password',
81: ],
82: // Individual
83: 'player' => [
84: 'name' => 'name',
85: 'score' => 'score',
86: 'team' => 'team',
87: 'ping' => 'ping',
88: 'time' => 'time',
89: ],
90: ];
91:
92: /**
93: * Process the response
94: *
95: * @return array
96: * @throws \GameQ\Exception\Protocol
97: */
98: public function processResponse()
99: {
100: // Create a new buffer
101: $buffer = new Buffer(implode('', $this->packets_response));
102:
103: // Check for valid response
104: if ($buffer->getLength() < 4) {
105: throw new \GameQ\Exception\Protocol(sprintf('%s The response from the server was empty.', __METHOD__));
106: }
107:
108: // Read the header
109: $header = $buffer->read(4);
110:
111: // Verify header
112: if ($header !== 'EYE1') {
113: throw new \GameQ\Exception\Protocol(sprintf('%s The response header "%s" does not match expected "EYE1"', __METHOD__, $header));
114: }
115:
116: // Create a new result
117: $result = new Result();
118:
119: // Variables
120: $result->add('gamename', $buffer->readPascalString(1, true));
121: $result->add('port', $buffer->readPascalString(1, true));
122: $result->add('servername', $buffer->readPascalString(1, true));
123: $result->add('gametype', $buffer->readPascalString(1, true));
124: $result->add('map', $buffer->readPascalString(1, true));
125: $result->add('version', $buffer->readPascalString(1, true));
126: $result->add('password', $buffer->readPascalString(1, true));
127: $result->add('num_players', $buffer->readPascalString(1, true));
128: $result->add('max_players', $buffer->readPascalString(1, true));
129: $result->add('dedicated', 1);
130:
131: // Offload the key/value pair processing
132: $this->processKeyValuePairs($buffer, $result);
133:
134: // Offload processing player and team info
135: $this->processPlayersAndTeams($buffer, $result);
136:
137: unset($buffer);
138:
139: return $result->fetch();
140: }
141:
142: // Internal methods
143:
144: /**
145: * Handles processing the extra key/value pairs for server settings
146: *
147: * @param \GameQ\Buffer $buffer
148: * @param \GameQ\Result $result
149: * @throws \GameQ\Exception\Protocol
150: */
151: protected function processKeyValuePairs(Buffer &$buffer, Result &$result)
152: {
153: // Key / value pairs
154: while ($buffer->getLength()) {
155: $key = $buffer->readPascalString(1, true);
156:
157: // If we have an empty key, we've reached the end
158: if (empty($key)) {
159: break;
160: }
161:
162: // Otherwise, add the pair
163: $result->add(
164: $key,
165: $buffer->readPascalString(1, true)
166: );
167: }
168:
169: unset($key);
170: }
171:
172: /**
173: * Handles processing the player and team data into a usable format
174: *
175: * @param \GameQ\Buffer $buffer
176: * @param \GameQ\Result $result
177: * @throws \GameQ\Exception\Protocol
178: */
179: protected function processPlayersAndTeams(Buffer &$buffer, Result &$result)
180: {
181: // Players and team info
182: while ($buffer->getLength()) {
183: // Get the flags
184: $flags = $buffer->readInt8();
185:
186: // Get data according to the flags
187: if ($flags & 1) {
188: $result->addPlayer('name', $buffer->readPascalString(1, true));
189: }
190: if ($flags & 2) {
191: $result->addPlayer('team', $buffer->readPascalString(1, true));
192: }
193: if ($flags & 4) {
194: $result->addPlayer('skin', $buffer->readPascalString(1, true));
195: }
196: if ($flags & 8) {
197: $result->addPlayer('score', $buffer->readPascalString(1, true));
198: }
199: if ($flags & 16) {
200: $result->addPlayer('ping', $buffer->readPascalString(1, true));
201: }
202: if ($flags & 32) {
203: $result->addPlayer('time', $buffer->readPascalString(1, true));
204: }
205: }
206: }
207: }
208: