1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
namespace eXpansion\Framework\Core\Services\Application; |
5
|
|
|
|
6
|
|
|
use eXpansion\Framework\Core\Services\Console; |
7
|
|
|
use eXpansion\Framework\Core\Services\DedicatedConnection\Factory; |
8
|
|
|
use Maniaplanet\DedicatedServer\Connection; |
9
|
|
|
use Propel\Runtime\Connection\Exception\ConnectionException; |
10
|
|
|
use Propel\Runtime\Propel; |
11
|
|
|
use Psr\Log\LoggerInterface; |
12
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
13
|
|
|
|
14
|
|
|
abstract class AbstractApplication implements RunInterface |
15
|
|
|
{ |
16
|
|
|
/** Base eXpansion callbacks. */ |
17
|
|
|
const EVENT_BEFORE_INIT = "expansion.before_init"; |
18
|
|
|
const EVENT_AFTER_INIT = "expansion.after_init"; |
19
|
|
|
const EVENT_READY = "expansion.ready"; |
20
|
|
|
const EVENT_STOP = "expansion.stop"; |
21
|
|
|
|
22
|
|
|
const EXPANSION_VERSION = "dev"; |
23
|
|
|
|
24
|
|
|
/** @var Factory */ |
25
|
|
|
protected $factory; |
26
|
|
|
|
27
|
|
|
/** @var DispatcherInterface */ |
28
|
|
|
protected $dispatcher; |
29
|
|
|
|
30
|
|
|
/** @var Console */ |
31
|
|
|
protected $console; |
32
|
|
|
|
33
|
|
|
/** @var bool */ |
34
|
|
|
protected $isRunning = true; |
35
|
|
|
/** |
36
|
|
|
* @var LoggerInterface |
37
|
|
|
*/ |
38
|
|
|
private $logger; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* AbstractApplication constructor. |
42
|
|
|
* |
43
|
|
|
* @param DispatcherInterface $dispatcher |
44
|
|
|
* @param Factory $factory |
45
|
|
|
* @param Console $output |
46
|
|
|
* @param LoggerInterface $logger |
47
|
|
|
*/ |
48
|
151 |
|
public function __construct( |
49
|
|
|
DispatcherInterface $dispatcher, |
50
|
|
|
Factory $factory, |
51
|
|
|
Console $output, |
52
|
|
|
LoggerInterface $logger |
53
|
|
|
) { |
54
|
151 |
|
$this->factory = $factory; |
55
|
151 |
|
$this->dispatcher = $dispatcher; |
56
|
151 |
|
$this->console = $output; |
57
|
151 |
|
$this->logger = $logger; |
58
|
151 |
|
} |
59
|
|
|
|
60
|
|
|
/** |
61
|
|
|
* Initialize eXpansion. |
62
|
|
|
* |
63
|
|
|
* @param OutputInterface $console |
64
|
|
|
* |
65
|
|
|
* @return $this|mixed |
66
|
|
|
* @throws \Maniaplanet\DedicatedServer\Xmlrpc\TransportException |
67
|
|
|
*/ |
68
|
1 |
|
public function init(OutputInterface $console) |
69
|
|
|
{ |
70
|
1 |
|
$this->checkPhpExtensions($console); |
71
|
|
|
|
72
|
1 |
|
$this->console->init($console, $this->dispatcher); |
73
|
1 |
|
$this->dispatcher->dispatch(self::EVENT_BEFORE_INIT, []); |
74
|
|
|
|
75
|
1 |
|
$this->factory->createConnection(); |
76
|
1 |
|
$this->dispatcher->init($this->factory->getConnection()); |
77
|
|
|
|
78
|
1 |
|
$this->dispatcher->dispatch(self::EVENT_AFTER_INIT, []); |
79
|
1 |
|
return $this; |
80
|
|
|
} |
81
|
|
|
|
82
|
1 |
|
protected function checkPhpExtensions(OutputInterface $console) |
83
|
|
|
{ |
84
|
|
|
$extensions = array( |
85
|
1 |
|
'openssl' => 'extension=php_openssl.dll', |
86
|
|
|
'curl' => 'extension=curl.dll', |
87
|
|
|
); |
88
|
1 |
|
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { |
89
|
|
|
$extensions['com_dotnet'] = 'extension=php_com_dotnet.dll'; |
90
|
|
|
} |
91
|
|
|
|
92
|
1 |
|
$status = true; |
93
|
1 |
|
$showIni = false; |
94
|
1 |
View Code Duplication |
foreach ($extensions as $extension => $description) { |
|
|
|
|
95
|
1 |
|
if (!extension_loaded($extension)) { |
96
|
|
|
$console->writeln( |
97
|
|
|
"<error>eXpansion needs PHP extension $extension to run. Enable it to run eXpansion => " . $description . "</error>" |
98
|
|
|
); |
99
|
|
|
$status = false; |
100
|
1 |
|
$showIni = true; |
101
|
|
|
} |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
$recommend = array( |
105
|
1 |
|
'xmlrpc' => "It will have better performances !", |
106
|
|
|
); |
107
|
1 |
View Code Duplication |
foreach ($recommend as $extension => $reason) { |
|
|
|
|
108
|
1 |
|
if (!extension_loaded($extension)) { |
109
|
|
|
$console->writeln( |
110
|
|
|
"<error>eXpansion works better with PHP extension</error> <info>$extension</info>: " . $reason . "" |
111
|
|
|
); |
112
|
1 |
|
$showIni = true; |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
1 |
|
if ($showIni) { |
117
|
|
|
$console->writeln('<info>[PHP] PHP is using fallowing ini file :</info> "'. php_ini_loaded_file() .'"'); |
118
|
|
|
sleep(5); |
119
|
|
|
} |
120
|
1 |
|
return $status; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
/** |
124
|
|
|
* Run eXpansion |
125
|
|
|
* |
126
|
|
|
* @inheritdoc |
127
|
|
|
*/ |
128
|
2 |
|
public function run() |
129
|
|
|
{ |
130
|
|
|
// Time each cycle needs to take in microseconds. Wrunning 60 cycles per seconds to have optimal response time. |
131
|
2 |
|
$cycleTime = (1 / 60) * 1000000; |
132
|
|
|
|
133
|
|
|
// Running GC collect every 5 minutes should be sufficient.; |
134
|
2 |
|
$gcCycleTime = 60 * 5; |
135
|
|
|
|
136
|
|
|
// Time when we will force gc cycles. |
137
|
2 |
|
$maxGcCycleTime = 60 * 20; |
138
|
|
|
|
139
|
|
|
// Last time garbage collector ran. Assume that at start it ran. |
140
|
2 |
|
$lastGcTime = time(); |
141
|
|
|
|
142
|
2 |
|
$this->console->writeln("Running preflight checks..."); |
143
|
2 |
|
$this->factory->getConnection()->enableCallbacks(true); |
144
|
|
|
|
145
|
|
|
// need to send this for scripts to start callback handling |
146
|
|
|
try { |
147
|
2 |
|
$this->factory->getConnection()->triggerModeScriptEvent("XmlRpc.EnableCallbacks", ["True"]); |
148
|
|
|
} catch (\Exception $exception) { |
149
|
|
|
$this->factory->getConnection()->saveMatchSettings('MatchSettings/eXpansion-mode-fail-'.date(DATE_ISO8601).'.txt'); |
150
|
|
|
throw $exception; |
151
|
|
|
} |
152
|
|
|
|
153
|
2 |
|
$this->console->writeln("preflight checks OK."); |
154
|
|
|
|
155
|
2 |
|
$this->dispatcher->dispatch(self::EVENT_READY, []); |
156
|
|
|
|
157
|
2 |
|
$this->console->writeln("And takeoff"); |
158
|
|
|
|
159
|
|
|
do { |
160
|
2 |
|
$startTime = microtime(true); |
161
|
|
|
|
162
|
|
|
// Run the actuall application |
163
|
2 |
|
$this->executeRun(); |
164
|
|
|
|
165
|
2 |
|
$endTime = microtime(true); |
166
|
2 |
|
$delay = $cycleTime - (($endTime - $startTime) * 1000000); |
167
|
|
|
|
168
|
|
|
// If we got lot's of time and it's been a while since last GC collect let's run a garbage collector |
169
|
|
|
// cycle this iteration. |
170
|
2 |
|
if ($delay > $cycleTime / 2 && $lastGcTime < (time() - $gcCycleTime)) { |
171
|
|
|
// PHP does this automatically as well but in some mysterious ways it can sometimes keep in memory |
172
|
|
|
// hundred of mb's before running it. |
173
|
|
|
gc_collect_cycles(); |
174
|
|
|
$lastGcTime = time(); |
175
|
|
|
|
176
|
|
|
// Renew delay so that this iteration isn't much slower then the others |
177
|
|
|
$endTime = microtime(true); |
178
|
|
|
$delay = $cycleTime - (($endTime - $startTime) * 1000000); |
179
|
|
|
} |
180
|
|
|
|
181
|
2 |
|
if ($lastGcTime < (time() - $maxGcCycleTime)) { |
182
|
|
|
//It's been a while since last Garbage collection forcing it to go even through the application is |
183
|
|
|
// running slow. |
184
|
|
|
gc_collect_cycles(); |
185
|
|
|
$lastGcTime = time(); |
186
|
|
|
|
187
|
2 |
|
} elseif ($delay > 0) { |
188
|
2 |
|
usleep($delay); |
189
|
|
|
} |
190
|
2 |
|
} while ($this->isRunning); |
191
|
|
|
|
192
|
2 |
|
$this->factory->getConnection()->sendHideManialinkPage(null); |
193
|
2 |
|
$this->factory->getConnection()->triggerModeScriptEvent("Shootmania.UI.ResetProperties", []); |
194
|
2 |
|
$this->factory->getConnection()->triggerModeScriptEvent("Trackmania.UI.ResetProperties", []); |
195
|
2 |
|
} |
196
|
|
|
|
197
|
|
|
/** |
198
|
|
|
* Stop eXpansion. |
199
|
|
|
*/ |
200
|
2 |
|
public function stopApplication() |
201
|
|
|
{ |
202
|
2 |
|
$this->dispatcher->dispatch(self::EVENT_STOP, []); |
203
|
2 |
|
$this->isRunning = false; |
204
|
2 |
|
} |
205
|
|
|
|
206
|
|
|
abstract protected function executeRun(); |
207
|
|
|
} |
208
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.