Passed
Pull Request — v3 (#731)
by
unknown
34:55
created

Eos::queryServers()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 23
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 23
rs 9.8333
cc 2
nc 2
nop 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\Exception\Protocol as Exception;
22
use GameQ\Server;
23
24
/**
25
 * Epic Online Services Protocol Class
26
 *
27
 * Serves as a base class for EOS-powered games.
28
 *
29
 * @package GameQ\Protocols
30
 * @author  H.Rouatbi
31
 */
32
class Eos extends Http
33
{
34
    /**
35
     * The protocol being used
36
     *
37
     * @var string
38
     */
39
    protected $protocol = 'eos';
40
41
    /**
42
     * Longer string name of this protocol class
43
     *
44
     * @var string
45
     */
46
    protected $name_long = 'Epic Online Services';
47
48
    /**
49
     * String name of this protocol class
50
     *
51
     * @var string
52
     */
53
    protected $name = 'eos';
54
55
    /**
56
     * Grant type used for authentication
57
     * 
58
     * @var string
59
     */
60
    protected $grant_type = 'client_credentials';
61
62
    /**
63
     * Deployment ID for the game or application
64
     *
65
     * @var string
66
     */
67
    protected $deployment_id = null;
68
69
    /**
70
     * User ID for authentication
71
     *
72
     * @var string
73
     */
74
    protected $user_id = null;
75
76
    /**
77
     * User secret key for authentication
78
     *
79
     * @var string
80
     */
81
    protected $user_secret = null;
82
83
    /**
84
     * Holds the server ip so we can overwrite it back
85
     *
86
     * @var string
87
     */
88
    protected $serverIp = null;
89
90
    /**
91
     * Holds the server port query so we can overwrite it back
92
     *
93
     * @var string
94
     */
95
    protected $serverPortQuery = null;
96
97
    /**
98
     * Normalize some items
99
     *
100
     * @var array
101
     */
102
    protected $normalize = [
103
        // General
104
        'general' => [
105
            // target       => source
106
            'hostname'   => 'hostname',
107
            'mapname'    => 'mapname',
108
            'maxplayers' => 'maxplayers',
109
            'numplayers' => 'numplayers',
110
            'password'   => 'password',
111
        ]
112
    ];
113
114
    /**
115
     * Process the response from the EOS API
116
     *
117
     * @return array
118
     * @throws Exception
119
     */
120
    public function processResponse()
121
    {
122
        // Authenticate and get the access token
123
        $auth_token = $this->authenticate();
124
125
        // If authentication failed, throw an exception
126
        if (!$auth_token) {
127
            throw new Exception('Failed to authenticate with EOS API.');
128
        }
129
130
        // Query for server data
131
        $server_data = $this->queryServers($auth_token);
132
133
        // If no server data, throw an exception
134
        if (empty($server_data)) {
135
            throw new Exception('No server data found. Server might be offline.');
136
        }
137
138
        return $server_data;
139
    }
140
141
    /**
142
     * Called before sending the request
143
     * 
144
     * @param Server $server
145
     */
146
    public function beforeSend($server)
147
    {
148
        $this->serverIp = $server->ip();
149
        $this->serverPortQuery = $server->portQuery();
150
    }
151
152
    /**
153
     * Authenticate to get the access token
154
     *
155
     * @return string|null
156
     */
157
    protected function authenticate()
158
    {
159
        $auth_url = "https://api.epicgames.dev/auth/v1/oauth/token";
160
        $auth_headers = [
161
            'Authorization: Basic ' . base64_encode("{$this->user_id}:{$this->user_secret}"),
162
            'Accept-Encoding: deflate, gzip',
163
            'Content-Type: application/x-www-form-urlencoded',
164
        ];
165
166
        $auth_postfields = "grant_type={$this->grant_type}&deployment_id={$this->deployment_id}";
167
168
        if ($this->grant_type === 'external_auth') {
169
            // Perform device authentication if necessary
170
            $device_auth = $this->deviceAuthentication();
171
            if (!$device_auth) {
172
                return null;
173
            }
174
            $auth_postfields .= "&external_auth_type=deviceid_access_token&external_auth_token={$device_auth['access_token']}&nonce=ABCHFA3qgUCJ1XTPAoGDEF&display_name=User";
175
        }
176
177
        // Make the request to get the access token
178
        $response = $this->httpRequest($auth_url, $auth_headers, $auth_postfields);
179
180
        return isset($response['access_token']) ? $response['access_token'] : null;
181
    }
182
183
    /**
184
     * Query the EOS server for matchmaking data
185
     *
186
     * @param string $auth_token
187
     * @return array|null
188
     */
189
    protected function queryServers($auth_token)
190
    {
191
        $server_query_url = "https://api.epicgames.dev/matchmaking/v1/{$this->deployment_id}/filter";
192
        $query_headers = [
193
            "Authorization: Bearer {$auth_token}",
194
            'Accept: application/json',
195
            'Content-Type: application/json',
196
        ];
197
198
        $query_body = json_encode([
199
            'criteria' => [
200
                [
201
                    'key' => 'attributes.ADDRESS_s',
202
                    'op' => 'EQUAL',
203
                    'value' => $this->serverIp,
204
                ],
205
            ],
206
            'maxResults' => 200,
207
        ]);
208
209
        $response = $this->httpRequest($server_query_url, $query_headers, $query_body);
210
211
        return isset($response['sessions']) ? $response['sessions'] : null;
212
    }
213
214
    /**
215
     * Handle device authentication for external auth type
216
     *
217
     * @return array|null
218
     */
219
    protected function deviceAuthentication()
220
    {
221
        $device_auth_url = "https://api.epicgames.dev/auth/v1/accounts/deviceid";
222
        $device_auth_headers = [
223
            'Authorization: Basic ' . base64_encode("{$this->user_id}:{$this->user_secret}"),
224
            'Accept-Encoding: deflate, gzip',
225
            'Content-Type: application/x-www-form-urlencoded',
226
        ];
227
228
        $device_auth_postfields = "deviceModel=PC";
229
230
        return $this->httpRequest($device_auth_url, $device_auth_headers, $device_auth_postfields);
231
    }
232
233
    /**
234
     * Execute an HTTP request
235
     *
236
     * @param string $url
237
     * @param array $headers
238
     * @param string $postfields
239
     * @return array|null
240
     */
241
    protected function httpRequest($url, $headers, $postfields)
242
    {
243
        $ch = curl_init();
244
245
        curl_setopt($ch, CURLOPT_URL, $url);
246
        curl_setopt($ch, CURLOPT_POST, 1);
247
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
248
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
249
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields);
250
251
        $response = curl_exec($ch);
252
253
        if (!$response) {
254
            return null;
255
        }
256
257
        return json_decode($response, true);
0 ignored issues
show
Bug introduced by
It seems like $response can also be of type true; however, parameter $json of json_decode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

257
        return json_decode(/** @scrutinizer ignore-type */ $response, true);
Loading history...
258
    }
259
}
260