Completed
Push — v3 ( ad1253...a48cff )
by Austin
04:28
created

Unreal2::processDetails()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 1

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 21
ccs 14
cts 14
cp 1
rs 9.3143
cc 1
eloc 14
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\Protocol;
22
use GameQ\Buffer;
23
use GameQ\Result;
24
use GameQ\Exception\Protocol as Exception;
25
26
/**
27
 * Unreal 2 Protocol class
28
 *
29
 * @author Austin Bischoff <[email protected]>
30
 */
31
abstract class Unreal2 extends Protocol
32
{
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
     * @type 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
     * @type 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
     * @type string
61
     */
62
    protected $protocol = 'unreal2';
63
64
    /**
65
     * String name of this protocol class
66
     *
67
     * @type string
68
     */
69
    protected $name = 'unreal2';
70
71
    /**
72
     * Longer string name of this protocol class
73
     *
74
     * @type string
75
     */
76
    protected $name_long = "Unreal 2";
77
78
    /**
79
     * Normalize settings for this protocol
80
     *
81
     * @type 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
     * Parse the challenge response and apply it to all the packet types
104
     *
105
     * @param \GameQ\Buffer $challenge_buffer
106
     *
107
     * @return bool
108
     * @throws \GameQ\Exception\Protocol
109
     */
110
    public function challengeParseAndApply(Buffer $challenge_buffer)
111
    {
112
113
        // Skip the header
114
        $challenge_buffer->skip(5);
115
116
        // Apply the challenge and return
117
        return $this->challengeApply($challenge_buffer->read(4));
118
    }
119
120
    /**
121
     * Process the response
122
     *
123
     * @return array
124
     * @throws \GameQ\Exception\Protocol
125
     */
126 4
    public function processResponse()
127
    {
128
129
        // Will hold the packets after sorting
130 4
        $packets = [ ];
131
132
        // We need to pre-sort these for split packets so we can do extra work where needed
133 4
        foreach ($this->packets_response as $response) {
134 4
            $buffer = new Buffer($response);
135
136
            // Pull out the header
137 4
            $header = $buffer->read(5);
138
139
            // Add the packet to the proper section, we will combine later
140 4
            $packets[$header][] = $buffer->getBuffer();
141 4
        }
142
143 4
        unset($buffer);
144
145 4
        $results = [ ];
146
147
        // Now let's iterate and process
148 4
        foreach ($packets as $header => $packetGroup) {
149
            // Figure out which packet response this is
150 4
            if (!array_key_exists($header, $this->responses)) {
151
                throw new Exception(__METHOD__ . " response type '{$header}' is not valid");
152
            }
153
154
            // Now we need to call the proper method
155 4
            $results = array_merge(
156 4
                $results,
157 4
                call_user_func_array([ $this, $this->responses[$header] ], [ new Buffer(implode($packetGroup)) ])
158 4
            );
159 4
        }
160
161 4
        unset($packets);
162
163 4
        return $results;
164
    }
165
166
    /*
167
     * Internal methods
168
     */
169
170
    /**
171
     * Handles processing the details data into a usable format
172
     *
173
     * @param \GameQ\Buffer $buffer
174
     *
175
     * @return mixed
176
     * @throws \GameQ\Exception\Protocol
177
     */
178 2
    protected function processDetails(Buffer $buffer)
179
    {
180
181
        // Set the result to a new result instance
182 2
        $result = new Result();
183
184 2
        $result->add('serverid', $buffer->readInt32()); // 0
185 2
        $result->add('serverip', $buffer->readPascalString(1)); // empty
186 2
        $result->add('gameport', $buffer->readInt32());
187 2
        $result->add('queryport', $buffer->readInt32()); // 0
188 2
        $result->add('servername', utf8_encode($buffer->readPascalString(1)));
189 2
        $result->add('mapname', utf8_encode($buffer->readPascalString(1)));
190 2
        $result->add('gametype', $buffer->readPascalString(1));
191 2
        $result->add('numplayers', $buffer->readInt32());
192 2
        $result->add('maxplayers', $buffer->readInt32());
193 2
        $result->add('ping', $buffer->readInt32()); // 0
194
195 2
        unset($buffer);
196
197 2
        return $result->fetch();
198
    }
199
200
    /**
201
     * Handles processing the player data into a usable format
202
     *
203
     * @param \GameQ\Buffer $buffer
204
     *
205
     * @return mixed
206
     */
207 4
    protected function processPlayers(Buffer $buffer)
208
    {
209
210
        // Set the result to a new result instance
211 4
        $result = new Result();
212
213
        // Parse players
214 4
        while ($buffer->getLength()) {
215
            // Player id
216 4
            if (($id = $buffer->readInt32()) === 0) {
217
                break;
218
            }
219
220 4
            $result->addPlayer('id', $id);
221 4
            $result->addPlayer('name', utf8_encode($buffer->readPascalString(1)));
222 4
            $result->addPlayer('ping', $buffer->readInt32());
223 4
            $result->addPlayer('score', $buffer->readInt32());
224
225
            // Skip the next 4, unsure what they are for
226 4
            $buffer->skip(4);
227 4
        }
228
229 4
        unset($buffer, $id);
230
231 4
        return $result->fetch();
232
    }
233
234
    /**
235
     * Handles processing the rules data into a usable format
236
     *
237
     * @param \GameQ\Buffer $buffer
238
     *
239
     * @return mixed
240
     */
241 4
    protected function processRules(Buffer $buffer)
242
    {
243
244
        // Set the result to a new result instance
245 4
        $result = new Result();
246
247
        // Named values
248 4
        $inc = -1;
249 4
        while ($buffer->getLength()) {
250
            // Grab the key
251 4
            $key = $buffer->readPascalString(1);
252
253
            // Make sure mutators don't overwrite each other
254 4
            if ($key === 'Mutator') {
255 4
                $key .= ++$inc;
256 4
            }
257
258 4
            $result->add(strtolower($key), utf8_encode($buffer->readPascalString(1)));
259 4
        }
260
261 4
        unset($buffer);
262
263 4
        return $result->fetch();
264
    }
265
}
266