Passed
Push — v3 ( 01700e...6888fb )
by
unknown
07:39 queued 05:23
created

Quake3   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 195
Duplicated Lines 0 %

Test Coverage

Coverage 97.78%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 11
eloc 58
c 2
b 0
f 0
dl 0
loc 195
ccs 44
cts 45
cp 0.9778
rs 10

4 Methods

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