Unreal2::processDetails()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 19
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 1
Metric Value
eloc 13
c 2
b 0
f 1
dl 0
loc 19
ccs 14
cts 14
cp 1
rs 9.8333
cc 1
nc 1
nop 1
crap 1
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\Helpers\Str;
24
use GameQ\Protocol;
25
use GameQ\Result;
26
27
/**
28
 * Unreal 2 Protocol class
29
 *
30
 * @author Austin Bischoff <[email protected]>
31
 */
32
class Unreal2 extends Protocol
33
{
34
    /**
35
     * Array of packets we want to look up.
36
     * Each key should correspond to a defined method in this or a parent class
37
     *
38
     * @var array
39
     */
40
    protected $packets = [
41
        self::PACKET_DETAILS => "\x79\x00\x00\x00\x00",
42
        self::PACKET_RULES   => "\x79\x00\x00\x00\x01",
43
        self::PACKET_PLAYERS => "\x79\x00\x00\x00\x02",
44
    ];
45
46
    /**
47
     * Use the response flag to figure out what method to run
48
     *
49
     * @var array
50
     */
51
    protected $responses = [
52
        "\x80\x00\x00\x00\x00" => "processDetails", // 0
53
        "\x80\x00\x00\x00\x01" => "processRules", // 1
54
        "\x80\x00\x00\x00\x02" => "processPlayers", // 2
55
    ];
56
57
    /**
58
     * The query protocol used to make the call
59
     *
60
     * @var string
61
     */
62
    protected $protocol = 'unreal2';
63
64
    /**
65
     * String name of this protocol class
66
     *
67
     * @var string
68
     */
69
    protected $name = 'unreal2';
70
71
    /**
72
     * Longer string name of this protocol class
73
     *
74
     * @var string
75
     */
76
    protected $name_long = "Unreal 2";
77
78
    /**
79
     * Normalize settings for this protocol
80
     *
81
     * @var array
82
     */
83
    protected $normalize = [
84
        // General
85
        'general' => [
86
            // target       => source
87
            'dedicated'  => 'ServerMode',
88
            'gametype'   => 'gametype',
89
            'hostname'   => 'servername',
90
            'mapname'    => 'mapname',
91
            'maxplayers' => 'maxplayers',
92
            'numplayers' => 'numplayers',
93
            'password'   => 'password',
94
        ],
95
        // Individual
96
        'player'  => [
97
            'name'  => 'name',
98
            'score' => 'score',
99
        ],
100
    ];
101
102
    /**
103
     * Process the response
104
     *
105
     * @return array
106
     * @throws \GameQ\Exception\Protocol
107
     */
108 36
    public function processResponse()
109
    {
110
        // Will hold the packets after sorting
111 36
        $packets = [];
112
113
        // We need to pre-sort these for split packets so we can do extra work where needed
114 36
        foreach ($this->packets_response as $response) {
115 36
            $buffer = new Buffer($response);
116
117
            // Pull out the header
118 36
            $header = $buffer->read(5);
119
120
            // Add the packet to the proper section, we will combine later
121 36
            $packets[$header][] = $buffer->getBuffer();
122
        }
123
124 36
        unset($buffer);
125
126 36
        $results = [];
127
128
        // Now let's iterate and process
129 36
        foreach ($packets as $header => $packetGroup) {
130
            // Figure out which packet response this is
131 36
            if (!array_key_exists($header, $this->responses)) {
132 12
                throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
133
            }
134
135
            // Now we need to call the proper method
136 24
            $results = array_merge(
137 24
                $results,
138 24
                call_user_func_array([$this, $this->responses[$header]], [new Buffer(implode($packetGroup))])
139 24
            );
140
        }
141
142 24
        unset($packets);
143
144 24
        return $results;
145
    }
146
147
    // Internal methods
148
149
    /**
150
     * Handles processing the details data into a usable format
151
     *
152
     * @param \GameQ\Buffer $buffer
153
     *
154
     * @return mixed
155
     * @throws \GameQ\Exception\Protocol
156
     */
157 12
    protected function processDetails(Buffer $buffer)
158
    {
159
        // Set the result to a new result instance
160 12
        $result = new Result();
161
162 12
        $result->add('serverid', $buffer->readInt32()); // 0
163 12
        $result->add('serverip', $buffer->readPascalString(1)); // empty
164 12
        $result->add('gameport', $buffer->readInt32());
165 12
        $result->add('queryport', $buffer->readInt32()); // 0
166 12
        $result->add('servername', Str::isoToUtf8($buffer->readPascalString(1)));
167 12
        $result->add('mapname', Str::isoToUtf8($buffer->readPascalString(1)));
168 12
        $result->add('gametype', $buffer->readPascalString(1));
169 12
        $result->add('numplayers', $buffer->readInt32());
170 12
        $result->add('maxplayers', $buffer->readInt32());
171 12
        $result->add('ping', $buffer->readInt32()); // 0
172
173 12
        unset($buffer);
174
175 12
        return $result->fetch();
176
    }
177
178
    /**
179
     * Handles processing the player data into a usable format
180
     *
181
     * @param \GameQ\Buffer $buffer
182
     * @return mixed
183
     * @throws \GameQ\Exception\Protocol
184
     */
185 24
    protected function processPlayers(Buffer $buffer)
186
    {
187
        // Set the result to a new result instance
188 24
        $result = new Result();
189
190
        // Parse players
191 24
        while ($buffer->getLength()) {
192
            // Player id
193 24
            if (($id = $buffer->readInt32()) !== 0) {
194
                // Add the results
195 24
                $result->addPlayer('id', $id);
196 24
                $result->addPlayer('name', Str::isoToUtf8($buffer->readPascalString(1)));
197 24
                $result->addPlayer('ping', $buffer->readInt32());
198 24
                $result->addPlayer('score', $buffer->readInt32());
199
200
                // Skip the next 4, unsure what they are for
201 24
                $buffer->skip(4);
202
            }
203
        }
204
205 24
        unset($buffer, $id);
206
207 24
        return $result->fetch();
208
    }
209
210
    /**
211
     * Handles processing the rules data into a usable format
212
     *
213
     * @param \GameQ\Buffer $buffer
214
     * @return mixed
215
     * @throws \GameQ\Exception\Protocol
216
     */
217 24
    protected function processRules(Buffer $buffer)
218
    {
219
        // Set the result to a new result instance
220 24
        $result = new Result();
221
222
        // Named values
223 24
        $inc = -1;
224 24
        while ($buffer->getLength()) {
225
            // Grab the key
226 24
            $key = $buffer->readPascalString(1);
227
228
            // Make sure mutators don't overwrite each other
229 24
            if ($key === 'Mutator') {
230 24
                $key .= ++$inc;
231
            }
232
233 24
            $result->add(strtolower($key), Str::isoToUtf8($buffer->readPascalString(1)));
234
        }
235
236 24
        unset($buffer);
237
238 24
        return $result->fetch();
239
    }
240
}
241