1: | <?php |
2: | |
3: | |
4: | |
5: | |
6: | |
7: | |
8: | |
9: | |
10: | |
11: | |
12: | |
13: | |
14: | |
15: | |
16: | |
17: | |
18: | |
19: | namespace GameQ; |
20: | |
21: | use GameQ\Exception\Server as Exception; |
22: | |
23: | |
24: | |
25: | |
26: | |
27: | |
28: | class Server |
29: | { |
30: | |
31: | const SERVER_TYPE = 'type'; |
32: | |
33: | const SERVER_HOST = 'host'; |
34: | |
35: | const SERVER_ID = 'id'; |
36: | |
37: | const SERVER_OPTIONS = 'options'; |
38: | |
39: | |
40: | |
41: | |
42: | const SERVER_OPTIONS_QUERY_PORT = 'query_port'; |
43: | |
44: | |
45: | |
46: | |
47: | |
48: | |
49: | protected $protocol = null; |
50: | |
51: | |
52: | |
53: | |
54: | |
55: | |
56: | public $id = null; |
57: | |
58: | |
59: | |
60: | |
61: | |
62: | |
63: | public $ip = null; |
64: | |
65: | |
66: | |
67: | |
68: | |
69: | |
70: | public $port_client = null; |
71: | |
72: | |
73: | |
74: | |
75: | |
76: | |
77: | public $port_query = null; |
78: | |
79: | |
80: | |
81: | |
82: | |
83: | |
84: | protected $options = []; |
85: | |
86: | |
87: | |
88: | |
89: | |
90: | |
91: | protected $sockets = []; |
92: | |
93: | |
94: | |
95: | |
96: | |
97: | |
98: | |
99: | |
100: | public function __construct(array $server_info = []) |
101: | { |
102: | |
103: | if (!array_key_exists(self::SERVER_TYPE, $server_info) || empty($server_info[self::SERVER_TYPE])) { |
104: | throw new Exception("Missing server info key '" . self::SERVER_TYPE . "'!"); |
105: | } |
106: | |
107: | |
108: | if (!array_key_exists(self::SERVER_HOST, $server_info) || empty($server_info[self::SERVER_HOST])) { |
109: | throw new Exception("Missing server info key '" . self::SERVER_HOST . "'!"); |
110: | } |
111: | |
112: | |
113: | $this->checkAndSetIpPort($server_info[self::SERVER_HOST]); |
114: | |
115: | |
116: | if (array_key_exists(self::SERVER_ID, $server_info) && !empty($server_info[self::SERVER_ID])) { |
117: | |
118: | $this->id = $server_info[self::SERVER_ID]; |
119: | } else { |
120: | |
121: | $this->id = sprintf('%s:%d', $this->ip, $this->port_client); |
122: | } |
123: | |
124: | |
125: | if (array_key_exists(self::SERVER_OPTIONS, $server_info)) { |
126: | |
127: | $this->options = $server_info[self::SERVER_OPTIONS]; |
128: | } |
129: | |
130: | try { |
131: | |
132: | $class = new \ReflectionClass( |
133: | sprintf('GameQ\\Protocols\\%s', ucfirst(strtolower($server_info[self::SERVER_TYPE]))) |
134: | ); |
135: | |
136: | $this->protocol = $class->newInstanceArgs([$this->options]); |
137: | } catch (\ReflectionException $e) { |
138: | throw new Exception("Unable to locate Protocols class for '{$server_info[self::SERVER_TYPE]}'!"); |
139: | } |
140: | |
141: | |
142: | $this->checkAndSetServerOptions(); |
143: | |
144: | unset($server_info, $class); |
145: | } |
146: | |
147: | |
148: | |
149: | |
150: | |
151: | |
152: | |
153: | |
154: | protected function checkAndSetIpPort($ip_address) |
155: | { |
156: | |
157: | if (substr_count($ip_address, ':') > 1) { |
158: | |
159: | if (strstr($ip_address, ']:')) { |
160: | |
161: | $server_addr = explode(':', $ip_address); |
162: | |
163: | |
164: | $this->port_client = (int)array_pop($server_addr); |
165: | |
166: | |
167: | $this->ip = implode(':', $server_addr); |
168: | |
169: | unset($server_addr); |
170: | } else { |
171: | |
172: | throw new Exception( |
173: | "The host address '{$ip_address}' is missing the port. All " |
174: | . "servers must have a port defined!" |
175: | ); |
176: | } |
177: | |
178: | |
179: | if (!filter_var(trim($this->ip, '[]'), FILTER_VALIDATE_IP, ['flags' => FILTER_FLAG_IPV6,])) { |
180: | throw new Exception("The IPv6 address '{$this->ip}' is invalid."); |
181: | } |
182: | } else { |
183: | |
184: | if (strstr($ip_address, ':')) { |
185: | list($this->ip, $this->port_client) = explode(':', $ip_address); |
186: | |
187: | |
188: | $this->port_client = (int)$this->port_client; |
189: | } else { |
190: | |
191: | throw new Exception( |
192: | "The host address '{$ip_address}' is missing the port. All " |
193: | . "servers must have a port defined!" |
194: | ); |
195: | } |
196: | |
197: | |
198: | if (! filter_var($this->ip, FILTER_VALIDATE_IP, ['flags' => FILTER_FLAG_IPV4,])) { |
199: | |
200: | $resolved = gethostbyname($this->ip); |
201: | |
202: | |
203: | if ($this->ip === $resolved) { |
204: | |
205: | throw new Exception("Unable to resolve the host '{$this->ip}' to an IP address."); |
206: | } else { |
207: | $this->ip = $resolved; |
208: | } |
209: | } |
210: | } |
211: | } |
212: | |
213: | |
214: | |
215: | |
216: | protected function checkAndSetServerOptions() |
217: | { |
218: | |
219: | if (array_key_exists(self::SERVER_OPTIONS_QUERY_PORT, $this->options)) { |
220: | $this->port_query = (int)$this->options[self::SERVER_OPTIONS_QUERY_PORT]; |
221: | } else { |
222: | |
223: | $this->port_query = $this->protocol->findQueryPort($this->port_client); |
224: | } |
225: | } |
226: | |
227: | |
228: | |
229: | |
230: | |
231: | |
232: | |
233: | |
234: | |
235: | public function setOption($key, $value) |
236: | { |
237: | $this->options[$key] = $value; |
238: | |
239: | return $this; |
240: | } |
241: | |
242: | |
243: | |
244: | |
245: | |
246: | |
247: | |
248: | |
249: | public function getOption($key) |
250: | { |
251: | return (array_key_exists($key, $this->options)) ? $this->options[$key] : null; |
252: | } |
253: | |
254: | public function getOptions() |
255: | { |
256: | return $this->options; |
257: | } |
258: | |
259: | |
260: | |
261: | |
262: | |
263: | |
264: | public function id() |
265: | { |
266: | return $this->id; |
267: | } |
268: | |
269: | |
270: | |
271: | |
272: | |
273: | |
274: | public function ip() |
275: | { |
276: | return $this->ip; |
277: | } |
278: | |
279: | |
280: | |
281: | |
282: | |
283: | |
284: | public function portClient() |
285: | { |
286: | return $this->port_client; |
287: | } |
288: | |
289: | |
290: | |
291: | |
292: | |
293: | |
294: | public function portQuery() |
295: | { |
296: | return $this->port_query; |
297: | } |
298: | |
299: | |
300: | |
301: | |
302: | |
303: | |
304: | public function protocol() |
305: | { |
306: | return $this->protocol; |
307: | } |
308: | |
309: | |
310: | |
311: | |
312: | |
313: | |
314: | public function getJoinLink() |
315: | { |
316: | |
317: | $joinLink = $this->protocol->joinLink(); |
318: | |
319: | |
320: | if (is_null($joinLink)) { |
321: | return null; |
322: | } |
323: | |
324: | |
325: | return sprintf($joinLink, $this->ip, $this->portClient()); |
326: | } |
327: | |
328: | |
329: | |
330: | |
331: | |
332: | |
333: | |
334: | |
335: | |
336: | |
337: | public function socketAdd(Query\Core $socket) |
338: | { |
339: | $this->sockets[] = $socket; |
340: | } |
341: | |
342: | |
343: | |
344: | |
345: | |
346: | |
347: | |
348: | |
349: | public function socketGet() |
350: | { |
351: | $socket = null; |
352: | |
353: | if (count($this->sockets) > 0) { |
354: | $socket = array_pop($this->sockets); |
355: | } |
356: | |
357: | return $socket; |
358: | } |
359: | |
360: | |
361: | |
362: | |
363: | |
364: | |
365: | public function socketCleanse() |
366: | { |
367: | |
368: | foreach ($this->sockets as $socket) { |
369: | |
370: | $socket->close(); |
371: | } |
372: | |
373: | |
374: | $this->sockets = []; |
375: | } |
376: | } |
377: | |