1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | |
20: | namespace GameQ\Protocols; |
21: | |
22: | use GameQ\Exception\Protocol as Exception; |
23: | use GameQ\Server; |
24: | |
25: | |
26: | |
27: | |
28: | |
29: | |
30: | |
31: | |
32: | |
33: | class Eos extends Http |
34: | { |
35: | |
36: | |
37: | |
38: | |
39: | |
40: | protected $protocol = 'eos'; |
41: | |
42: | |
43: | |
44: | |
45: | |
46: | |
47: | protected $name_long = 'Epic Online Services'; |
48: | |
49: | |
50: | |
51: | |
52: | |
53: | |
54: | protected $name = 'eos'; |
55: | |
56: | |
57: | |
58: | |
59: | |
60: | |
61: | protected $grant_type = 'client_credentials'; |
62: | |
63: | |
64: | |
65: | |
66: | |
67: | |
68: | protected $deployment_id = null; |
69: | |
70: | |
71: | |
72: | |
73: | |
74: | |
75: | protected $user_id = null; |
76: | |
77: | |
78: | |
79: | |
80: | |
81: | |
82: | protected $user_secret = null; |
83: | |
84: | |
85: | |
86: | |
87: | |
88: | |
89: | protected $serverIp = null; |
90: | |
91: | |
92: | |
93: | |
94: | |
95: | |
96: | protected $serverPortQuery = null; |
97: | |
98: | |
99: | |
100: | |
101: | |
102: | |
103: | protected $normalize = [ |
104: | |
105: | 'general' => [ |
106: | |
107: | 'hostname' => 'hostname', |
108: | 'mapname' => 'mapname', |
109: | 'maxplayers' => 'maxplayers', |
110: | 'numplayers' => 'numplayers', |
111: | 'password' => 'password', |
112: | ] |
113: | ]; |
114: | |
115: | |
116: | |
117: | |
118: | |
119: | |
120: | |
121: | public function processResponse() |
122: | { |
123: | $index = ($this->grant_type === 'external_auth') ? 2 : 1; |
124: | $server_data = isset($this->packets_response[$index]) ? json_decode($this->packets_response[$index], true) : null; |
125: | |
126: | $server_data = isset($server_data['sessions']) ? $server_data['sessions'] : null; |
127: | |
128: | |
129: | if (empty($server_data)) { |
130: | throw new Exception('No server data found. Server might be offline.'); |
131: | } |
132: | |
133: | return $server_data; |
134: | } |
135: | |
136: | |
137: | |
138: | |
139: | |
140: | |
141: | public function beforeSend(Server $server) |
142: | { |
143: | $this->serverIp = $server->ip(); |
144: | $this->serverPortQuery = $server->portQuery(); |
145: | |
146: | |
147: | $auth_token = $this->authenticate(); |
148: | |
149: | if (!$auth_token) { |
150: | return; |
151: | } |
152: | |
153: | |
154: | $this->queryServers($auth_token); |
155: | } |
156: | |
157: | |
158: | |
159: | |
160: | |
161: | |
162: | protected function authenticate() |
163: | { |
164: | $auth_url = "https://api.epicgames.dev/auth/v1/oauth/token"; |
165: | $auth_headers = [ |
166: | 'Authorization: Basic ' . base64_encode("{$this->user_id}:{$this->user_secret}"), |
167: | 'Accept-Encoding: deflate, gzip', |
168: | 'Content-Type: application/x-www-form-urlencoded', |
169: | ]; |
170: | |
171: | $auth_postfields = "grant_type={$this->grant_type}&deployment_id={$this->deployment_id}"; |
172: | |
173: | if ($this->grant_type === 'external_auth') { |
174: | |
175: | $device_auth = $this->deviceAuthentication(); |
176: | if (!$device_auth) { |
177: | return null; |
178: | } |
179: | $auth_postfields .= "&external_auth_type=deviceid_access_token" |
180: | . "&external_auth_token={$device_auth['access_token']}" |
181: | . "&nonce=ABCHFA3qgUCJ1XTPAoGDEF&display_name=User"; |
182: | } |
183: | |
184: | |
185: | $response = $this->httpRequest($auth_url, $auth_headers, $auth_postfields); |
186: | |
187: | return isset($response['access_token']) ? $response['access_token'] : null; |
188: | } |
189: | |
190: | |
191: | |
192: | |
193: | |
194: | |
195: | |
196: | protected function queryServers($auth_token) |
197: | { |
198: | $server_query_url = "https://api.epicgames.dev/matchmaking/v1/{$this->deployment_id}/filter"; |
199: | $query_headers = [ |
200: | "Authorization: Bearer {$auth_token}", |
201: | 'Accept: application/json', |
202: | 'Content-Type: application/json', |
203: | ]; |
204: | |
205: | $query_body = json_encode([ |
206: | 'criteria' => [ |
207: | [ |
208: | 'key' => 'attributes.ADDRESS_s', |
209: | 'op' => 'EQUAL', |
210: | 'value' => $this->serverIp, |
211: | ], |
212: | ], |
213: | 'maxResults' => 200, |
214: | ]); |
215: | |
216: | $response = $this->httpRequest($server_query_url, $query_headers, $query_body); |
217: | |
218: | return isset($response['sessions']) ? $response['sessions'] : null; |
219: | } |
220: | |
221: | |
222: | |
223: | |
224: | |
225: | |
226: | protected function deviceAuthentication() |
227: | { |
228: | $device_auth_url = "https://api.epicgames.dev/auth/v1/accounts/deviceid"; |
229: | $device_auth_headers = [ |
230: | 'Authorization: Basic ' . base64_encode("{$this->user_id}:{$this->user_secret}"), |
231: | 'Accept-Encoding: deflate, gzip', |
232: | 'Content-Type: application/x-www-form-urlencoded', |
233: | ]; |
234: | |
235: | $device_auth_postfields = "deviceModel=PC"; |
236: | |
237: | return $this->httpRequest($device_auth_url, $device_auth_headers, $device_auth_postfields); |
238: | } |
239: | |
240: | |
241: | |
242: | |
243: | |
244: | |
245: | |
246: | |
247: | |
248: | protected function httpRequest($url, $headers, $postfields) |
249: | { |
250: | $ch = curl_init(); |
251: | |
252: | curl_setopt($ch, CURLOPT_URL, $url); |
253: | curl_setopt($ch, CURLOPT_POST, 1); |
254: | curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); |
255: | curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); |
256: | curl_setopt($ch, CURLOPT_POSTFIELDS, $postfields); |
257: | |
258: | $response = curl_exec($ch); |
259: | |
260: | if (!$response) { |
261: | return null; |
262: | } |
263: | |
264: | $this->packets_response[] = $response; |
265: | |
266: | return json_decode($response, true); |
267: | } |
268: | |
269: | |
270: | |
271: | |
272: | |
273: | |
274: | |
275: | |
276: | |
277: | protected function getAttribute($attributes, $key, $default = null) |
278: | { |
279: | return isset($attributes[$key]) ? $attributes[$key] : $default; |
280: | } |
281: | } |
282: | |