Quake2   A
last analyzed

Complexity

Total Complexity 8

Size/Duplication

Total Lines 194
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 8
eloc 57
c 1
b 0
f 0
dl 0
loc 194
ccs 42
cts 42
cp 1
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A processStatus() 0 14 1
A processResponse() 0 14 3
A processPlayers() 0 42 2
A processServerInfo() 0 23 2
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
 * Quake2 Protocol Class
14
 *
15
 * Handles processing Quake 3 servers
16
 *
17
 * @package GameQ\Protocols
18
 */
19
class Quake2 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\xFFstatus\x00",
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\xFF\x70\x72\x69\x6e\x74" => 'processStatus',
38
    ];
39
40
    /**
41
     * The query protocol used to make the call
42
     *
43
     * @var string
44
     */
45
    protected $protocol = 'quake2';
46
47
    /**
48
     * String name of this protocol class
49
     *
50
     * @var string
51
     */
52
    protected $name = 'quake2';
53
54
    /**
55
     * Longer string name of this protocol class
56
     *
57
     * @var string
58
     */
59
    protected $name_long = "Quake 2 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'   => 'hostname',
72
            'mapname'    => 'mapname',
73
            'maxplayers' => 'maxclients',
74
            'mod'        => 'g_gametype',
75
            'numplayers' => 'clients',
76
            'password'   => 'password',
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 42
    public function processResponse()
94
    {
95
        // Make a buffer
96 42
        $buffer = new Buffer(implode('', $this->packets_response));
97
98
        // Grab the header
99 42
        $header = $buffer->readString("\x0A");
100
101
        // Figure out which packet response this is
102 42
        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 30
        return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
107
    }
108
109
    /**
110
     * Process the status response
111
     *
112
     * @param Buffer $buffer
113
     * @return array
114
     * @throws \GameQ\Exception\Protocol
115
     */
116 30
    protected function processStatus(Buffer $buffer)
117
    {
118
        // We need to split the data and offload
119 30
        $results = $this->processServerInfo(new Buffer($buffer->readString("\x0A")));
120
121 30
        $results = array_merge_recursive(
122 30
            $results,
123 30
            $this->processPlayers(new Buffer($buffer->getBuffer()))
124 30
        );
125
126 30
        unset($buffer);
127
128
        // Return results
129 30
        return $results;
130
    }
131
132
    /**
133
     * Handle processing the server information
134
     *
135
     * @param Buffer $buffer
136
     * @return array
137
     * @throws \GameQ\Exception\Protocol
138
     */
139 30
    protected function processServerInfo(Buffer $buffer)
140
    {
141
        // Set the result to a new result instance
142 30
        $result = new Result();
143
144
        // Burn leading \ if one exists
145 30
        $buffer->readString('\\');
146
147
        // Key / value pairs
148 30
        while ($buffer->getLength()) {
149
            // Add result
150 30
            $result->add(
151 30
                trim($buffer->readString('\\')),
152 30
                Str::isoToUtf8(trim($buffer->readStringMulti(['\\', "\x0a"])))
153 30
            );
154
        }
155
156 30
        $result->add('password', 0);
157 30
        $result->add('mod', 0);
158
159 30
        unset($buffer);
160
161 30
        return $result->fetch();
162
    }
163
164
    /**
165
     * Handle processing of player data
166
     *
167
     * @param Buffer $buffer
168
     * @return array
169
     * @throws \GameQ\Exception\Protocol
170
     */
171 30
    protected function processPlayers(Buffer $buffer)
172
    {
173
        // Some games do not have a number of current players
174 30
        $playerCount = 0;
175
176
        // Set the result to a new result instance
177 30
        $result = new Result();
178
179
        // Loop until we are out of data
180 30
        while ($buffer->getLength()) {
181
            // Make a new buffer with this block
182 18
            $playerInfo = new Buffer($buffer->readString("\x0A"));
183
184
            // Add player info
185 18
            $result->addPlayer('frags', $playerInfo->readString("\x20"));
186 18
            $result->addPlayer('ping', $playerInfo->readString("\x20"));
187
188
            // Skip first "
189 18
            $playerInfo->skip(1);
190
191
            // Add player name, encoded
192 18
            $result->addPlayer('name', Str::isoToUtf8(trim(($playerInfo->readString('"')))));
193
194
            // Skip first "
195 18
            $playerInfo->skip(2);
196
197
            // Add address
198 18
            $result->addPlayer('address', trim($playerInfo->readString('"')));
199
200
            // Increment
201 18
            $playerCount++;
202
203
            // Clear
204 18
            unset($playerInfo);
205
        }
206
207 30
        $result->add('clients', $playerCount);
208
209
        // Clear
210 30
        unset($buffer, $playerCount);
211
212 30
        return $result->fetch();
213
    }
214
}
215