M2mp   A
last analyzed

Complexity

Total Complexity 7

Size/Duplication

Total Lines 177
Duplicated Lines 0 %

Test Coverage

Coverage 96.88%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 7
eloc 45
c 1
b 0
f 0
dl 0
loc 177
ccs 31
cts 32
cp 0.9688
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
A processStatus() 0 14 1
A processServerInfo() 0 19 1
A processResponse() 0 15 2
A processPlayers() 0 22 3
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
 * Mafia 2 Multiplayer Protocol Class
29
 *
30
 * Loosely based on SAMP protocol
31
 *
32
 * Query port = server port + 1
33
 *
34
 * Handles processing Mafia 2 Multiplayer servers
35
 *
36
 * @package GameQ\Protocols
37
 * @author Wilson Jesus <>
38
 */
39
class M2mp extends Protocol
40
{
41
    /**
42
     * Array of packets we want to look up.
43
     * Each key should correspond to a defined method in this or a parent class
44
     *
45
     * @var array
46
     */
47
    protected $packets = [
48
        self::PACKET_ALL => "M2MP",
49
    ];
50
51
    /**
52
     * Use the response flag to figure out what method to run
53
     *
54
     * @var array
55
     */
56
    protected $responses = [
57
        "M2MP" => 'processStatus',
58
    ];
59
60
    /**
61
     * The query protocol used to make the call
62
     *
63
     * @var string
64
     */
65
    protected $protocol = 'm2mp';
66
67
    /**
68
     * String name of this protocol class
69
     *
70
     * @var string
71
     */
72
    protected $name = 'm2mp';
73
74
    /**
75
     * Longer string name of this protocol class
76
     *
77
     * @var string
78
     */
79
    protected $name_long = "Mafia 2 Multiplayer";
80
81
    /**
82
     * The difference between the client port and query port
83
     *
84
     * @var int
85
     */
86
    protected $port_diff = 1;
87
88
    /**
89
     * Normalize settings for this protocol
90
     *
91
     * @var array
92
     */
93
    protected $normalize = [
94
        // General
95
        'general' => [
96
            // target       => source
97
            'hostname'   => 'servername',
98
            'gametype'   => 'gamemode',
99
            'maxplayers' => 'max_players',
100
            'numplayers' => 'num_players',
101
            'password'   => 'password',
102
        ],
103
        // Individual
104
        'player'  => [
105
            'name'  => 'name',
106
        ],
107
    ];
108
109
    /**
110
     * Handle response from the server
111
     *
112
     * @return mixed
113
     * @throws Exception
114
     * @throws \GameQ\Exception\Protocol
115
     */
116 6
    public function processResponse()
117
    {
118
        // Make a buffer
119 6
        $buffer = new Buffer(implode('', $this->packets_response));
120
121
        // Grab the header
122 6
        $header = $buffer->read(4);
123
124
        // Header
125
        // Figure out which packet response this is
126 6
        if ($header != "M2MP") {
127
            throw new Exception(__METHOD__ . " response type '" . bin2hex($header) . "' is not valid");
128
        }
129
130 6
        return call_user_func_array([$this, $this->responses[$header]], [$buffer]);
131
    }
132
133
    /**
134
     * Process the status response
135
     *
136
     * @param Buffer $buffer
137
     *
138
     * @return array
139
     * @throws \GameQ\Exception\Protocol
140
     */
141 6
    protected function processStatus(Buffer $buffer)
142
    {
143
        // We need to split the data and offload
144 6
        $results = $this->processServerInfo($buffer);
145
146 6
        $results = array_merge_recursive(
147 6
            $results,
148 6
            $this->processPlayers($buffer)
149 6
        );
150
151 6
        unset($buffer);
152
153
        // Return results
154 6
        return $results;
155
    }
156
157
    /**
158
     * Handle processing the server information
159
     *
160
     * @param Buffer $buffer
161
     *
162
     * @return array
163
     * @throws \GameQ\Exception\Protocol
164
     */
165 6
    protected function processServerInfo(Buffer $buffer)
166
    {
167
        // Set the result to a new result instance
168 6
        $result = new Result();
169
170
        // Always dedicated
171 6
        $result->add('dedicated', 1);
172
173
        // Pull out the server information
174
        // Note the length information is incorrect, we correct using offset options in pascal method
175 6
        $result->add('servername', $buffer->readPascalString(1, true));
176 6
        $result->add('num_players', $buffer->readPascalString(1, true));
177 6
        $result->add('max_players', $buffer->readPascalString(1, true));
178 6
        $result->add('gamemode', $buffer->readPascalString(1, true));
179 6
        $result->add('password', (bool) $buffer->readInt8());
180
181 6
        unset($buffer);
182
183 6
        return $result->fetch();
184
    }
185
186
    /**
187
     * Handle processing of player data
188
     *
189
     * @param Buffer $buffer
190
     *
191
     * @return array
192
     * @throws \GameQ\Exception\Protocol
193
     */
194 6
    protected function processPlayers(Buffer $buffer)
195
    {
196
        // Set the result to a new result instance
197 6
        $result = new Result();
198
199
        // Parse players
200
        // Read the player info, it's in the same query response for some odd reason.
201 6
        while ($buffer->getLength()) {
202
            // Check to see if we ran out of info, length bug from response
203 6
            if ($buffer->getLength() <= 1) {
204 6
                break;
205
            }
206
207
            // Only player name information is available
208
            // Add player name, encoded
209 6
            $result->addPlayer('name', Str::isoToUtf8(trim($buffer->readPascalString(1, true))));
210
        }
211
212
        // Clear
213 6
        unset($buffer);
214
215 6
        return $result->fetch();
216
    }
217
}
218