Passed
Push — v3 ( 836575...105154 )
by
unknown
02:40
created

Quake3::processPlayers()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 46
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 5.0042

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 17
c 2
b 0
f 0
dl 0
loc 46
ccs 17
cts 18
cp 0.9444
rs 9.3888
cc 5
nc 5
nop 1
crap 5.0042
1
<?php
2
3
4
namespace GameQ\Protocols;
5
6
use GameQ\Buffer;
7
use GameQ\Exception\Protocol as Exception;
8
use GameQ\Helpers\Str;
9
use GameQ\Protocol;
10
use GameQ\Result;
11
12
/**
13
 * Quake3 Protocol Class
14
 *
15
 * Handles processing Quake 3 servers
16
 *
17
 * @package GameQ\Protocols
18
 */
19
class Quake3 extends Protocol
20
{
21
    /**
22
     * Array of packets we want to look up.
23
     * Each key should correspond to a defined method in this or a parent class
24
     *
25
     * @var array
26
     */
27
    protected $packets = [
28
        self::PACKET_STATUS => "\xFF\xFF\xFF\xFF\x67\x65\x74\x73\x74\x61\x74\x75\x73\x0A",
29
    ];
30
31
    /**
32
     * Use the response flag to figure out what method to run
33
     *
34
     * @var array
35
     */
36
    protected $responses = [
37
        "\xFF\xFF\xFF\xFFstatusResponse" => 'processStatus',
38
    ];
39
40
    /**
41
     * The query protocol used to make the call
42
     *
43
     * @var string
44
     */
45
    protected $protocol = 'quake3';
46
47
    /**
48
     * String name of this protocol class
49
     *
50
     * @var string
51
     */
52
    protected $name = 'quake3';
53
54
    /**
55
     * Longer string name of this protocol class
56
     *
57
     * @var string
58
     */
59
    protected $name_long = "Quake 3 Server";
60
61
    /**
62
     * Normalize settings for this protocol
63
     *
64
     * @var array
65
     */
66
    protected $normalize = [
67
        // General
68
        'general' => [
69
            // target       => source
70
            'gametype'   => 'gamename',
71
            'hostname'   => 'sv_hostname',
72
            'mapname'    => 'mapname',
73
            'maxplayers' => 'sv_maxclients',
74
            'mod'        => 'g_gametype',
75
            'numplayers' => 'clients',
76
            'password'   => ['g_needpass', 'pswrd'],
77
        ],
78
        // Individual
79
        'player'  => [
80
            'name'  => 'name',
81
            'ping'  => 'ping',
82
            'score' => 'frags',
83
        ],
84
    ];
85
86
    /**
87
     * Handle response from the server
88
     *
89
     * @return mixed
90
     * @throws Exception
91
     * @throws \GameQ\Exception\Protocol
92
     */
93 186
    public function processResponse()
94
    {
95
        // Make a buffer
96 186
        $buffer = new Buffer(implode('', $this->packets_response));
97
98
        // Grab the header
99 186
        $header = $buffer->readString("\x0A");
100
101
        // Figure out which packet response this is
102 186
        if (empty($header) || !array_key_exists($header, $this->responses)) {
103 12
            throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
104
        }
105
106 174
        return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
107
    }
108
109
    /**
110
     * @param Buffer $buffer
111
     * @return array
112
     * @throws Exception
113
     * @throws \GameQ\Exception\Protocol
114
     */
115 174
    protected function processStatus(Buffer $buffer)
116
    {
117
        // We need to split the data and offload
118 174
        $results = $this->processServerInfo(new Buffer($buffer->readString("\x0A")));
119
120 174
        $results = array_merge_recursive(
121 174
            $results,
122 174
            $this->processPlayers(new Buffer($buffer->getBuffer()))
123 174
        );
124
125 174
        unset($buffer);
126
127
        // Return results
128 174
        return $results;
129
    }
130
131
    /**
132
     * Handle processing the server information
133
     *
134
     * @param Buffer $buffer
135
     * @return array
136
     * @throws \GameQ\Exception\Protocol
137
     */
138 174
    protected function processServerInfo(Buffer $buffer)
139
    {
140
        // Set the result to a new result instance
141 174
        $result = new Result();
142
143
        // Burn leading \ if one exists
144 174
        $buffer->readString('\\');
145
146
        // Key / value pairs
147 174
        while ($buffer->getLength()) {
148
            // Add result
149 174
            $result->add(
150 174
                trim($buffer->readString('\\')),
151 174
                Str::isoToUtf8(trim($buffer->readStringMulti(['\\', "\x0a"])))
152 174
            );
153
        }
154
155 174
        unset($buffer);
156
157 174
        return $result->fetch();
158
    }
159
160
    /**
161
     * Handle processing of player data
162
     *
163
     * @param Buffer $buffer
164
     * @return array
165
     * @throws Exception
166
     * @throws \GameQ\Exception\Protocol
167
     */
168 144
    protected function processPlayers(Buffer $buffer)
169
    {
170
        // Some games do not have a number of current players
171 144
        $playerCount = 0;
172
173
        // Set the result to a new result instance
174 144
        $result = new Result();
175
176
        // Loop until we are out of data
177 144
        while ($buffer->getLength()) {
178
            // Add player info
179 126
            $result->addPlayer('frags', $buffer->readString("\x20"));
180 126
            $result->addPlayer('ping', $buffer->readString("\x20"));
181
182
            // Look ahead to see if we have a name or team
183 126
            $checkTeam = $buffer->lookAhead(1);
184
185
            // We have team info
186 126
            if ($checkTeam != '' and $checkTeam != '"') {
187 6
                $result->addPlayer('team', $buffer->readString("\x20"));
188
            }
189
190
            // Check to make sure we have player name
191 126
            $checkPlayerName = $buffer->read();
192
193
            // Bad response
194 126
            if ($checkPlayerName !== '"') {
195
                throw new Exception('Expected " but got ' . $checkPlayerName . ' for beginning of player name string!');
196
            }
197
198
            // Add player name, encoded
199 126
            $result->addPlayer('name', Str::isoToUtf8(trim($buffer->readString('"'))));
200
201
            // Burn ending delimiter
202 126
            $buffer->read();
203
204
            // Increment
205 126
            $playerCount++;
206
        }
207
208 144
        $result->add('clients', $playerCount);
209
210
        // Clear
211 144
        unset($buffer, $playerCount);
212
213 144
        return $result->fetch();
214
    }
215
}
216