|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
|
|
4
|
|
|
namespace eXpansion\Framework\Core\Services\Application; |
|
5
|
|
|
|
|
6
|
|
|
use eXpansion\Framework\Core\Services\Console; |
|
7
|
|
|
use Maniaplanet\DedicatedServer\Connection; |
|
8
|
|
|
use Propel\Runtime\Connection\Exception\ConnectionException; |
|
9
|
|
|
use Propel\Runtime\Propel; |
|
10
|
|
|
use Psr\Log\LoggerInterface; |
|
11
|
|
|
use Symfony\Component\Console\Output\OutputInterface; |
|
12
|
|
|
|
|
13
|
|
|
abstract class AbstractApplication implements RunInterface |
|
14
|
|
|
{ |
|
15
|
|
|
/** Base eXpansion callbacks. */ |
|
16
|
|
|
const EVENT_BEFORE_INIT = "expansion.before_init"; |
|
17
|
|
|
const EVENT_AFTER_INIT = "expansion.after_init"; |
|
18
|
|
|
const EVENT_READY = "expansion.ready"; |
|
19
|
|
|
const EVENT_STOP = "expansion.stop"; |
|
20
|
|
|
|
|
21
|
|
|
const EXPANSION_VERSION = "dev"; |
|
22
|
|
|
|
|
23
|
|
|
/** @var Connection */ |
|
24
|
|
|
protected $connection; |
|
25
|
|
|
|
|
26
|
|
|
/** @var DispatcherInterface */ |
|
27
|
|
|
protected $dispatcher; |
|
28
|
|
|
|
|
29
|
|
|
/** @var Console */ |
|
30
|
|
|
protected $console; |
|
31
|
|
|
|
|
32
|
|
|
/** @var bool */ |
|
33
|
|
|
protected $isRunning = true; |
|
34
|
|
|
/** |
|
35
|
|
|
* @var LoggerInterface |
|
36
|
|
|
*/ |
|
37
|
|
|
private $logger; |
|
38
|
|
|
|
|
39
|
|
|
/** |
|
40
|
|
|
* Application constructor. |
|
41
|
|
|
* |
|
42
|
|
|
* @param DispatcherInterface $dispatcher |
|
43
|
|
|
* @param Connection $connection |
|
44
|
|
|
* @param Console $output |
|
45
|
|
|
* @param LoggerInterface $logger |
|
46
|
|
|
*/ |
|
47
|
138 |
|
public function __construct( |
|
48
|
|
|
DispatcherInterface $dispatcher, |
|
49
|
|
|
Connection $connection, |
|
50
|
|
|
Console $output, |
|
51
|
|
|
LoggerInterface $logger |
|
52
|
|
|
) { |
|
53
|
138 |
|
$this->connection = $connection; |
|
54
|
138 |
|
$this->dispatcher = $dispatcher; |
|
55
|
138 |
|
$this->console = $output; |
|
56
|
138 |
|
$this->logger = $logger; |
|
57
|
138 |
|
} |
|
58
|
|
|
|
|
59
|
|
|
/** |
|
60
|
|
|
* Initialize eXpansion. |
|
61
|
|
|
* |
|
62
|
|
|
* @param OutputInterface $console |
|
63
|
|
|
* |
|
64
|
|
|
* @return $this |
|
65
|
|
|
*/ |
|
66
|
|
|
public function init(OutputInterface $console) |
|
67
|
|
|
{ |
|
68
|
|
|
$this->console->init($console, $this->dispatcher); |
|
69
|
|
|
$this->dispatcher->dispatch(self::EVENT_BEFORE_INIT, []); |
|
70
|
|
|
$this->dispatcher->init($this->connection); |
|
71
|
|
|
$this->dispatcher->dispatch(self::EVENT_AFTER_INIT, []); |
|
72
|
|
|
|
|
73
|
|
|
return $this; |
|
74
|
|
|
} |
|
75
|
|
|
|
|
76
|
|
|
/** |
|
77
|
|
|
* Run eXpansion |
|
78
|
|
|
* |
|
79
|
|
|
* @inheritdoc |
|
80
|
|
|
*/ |
|
81
|
|
|
public function run() |
|
82
|
|
|
{ |
|
83
|
|
|
// Time each cycle needs to take in microseconds. Wrunning 60 cycles per seconds to have optimal response time. |
|
84
|
|
|
$cycleTime = (1 / 60) * 1000000; |
|
85
|
|
|
|
|
86
|
|
|
// Running GC collect every 5 minutes should be sufficient.; |
|
87
|
|
|
$gcCycleTime = 60 * 5; |
|
88
|
|
|
|
|
89
|
|
|
// Time when we will force gc cycles. |
|
90
|
|
|
$maxGcCycleTime = 60 * 20; |
|
91
|
|
|
|
|
92
|
|
|
// Last time garbage collector ran. Assume that at start it ran. |
|
93
|
|
|
$lastGcTime = time(); |
|
94
|
|
|
|
|
95
|
|
|
$this->console->writeln("Running preflight checks..."); |
|
96
|
|
|
$this->connection->enableCallbacks(true); |
|
97
|
|
|
|
|
98
|
|
|
// need to send this for scripts to start callback handling |
|
99
|
|
|
try { |
|
100
|
|
|
$this->connection->triggerModeScriptEvent("XmlRpc.EnableCallbacks", ["True"]); |
|
101
|
|
|
Propel::getConnection()->inTransaction(); |
|
102
|
|
|
} catch (ConnectionException $propelException) { |
|
103
|
|
|
|
|
104
|
|
|
$this->console->writeln("\nLooks like your database connection is down, see logs for more details."); |
|
105
|
|
|
$this->console->writeln("Please check-in later, when you database is up and running."); |
|
106
|
|
|
$this->logger->error("Unable to open connection for database server", ["exception" => $propelException]); |
|
107
|
|
|
exit(1); |
|
|
|
|
|
|
108
|
|
|
|
|
109
|
|
|
} catch (\Exception $exception) { |
|
110
|
|
|
$this->connection->saveMatchSettings('MatchSettings/eXpansion-mode-fail-' . date(DATE_ISO8601) . '.txt'); |
|
111
|
|
|
throw $exception; |
|
112
|
|
|
} |
|
113
|
|
|
|
|
114
|
|
|
$this->console->writeln("preflight checks OK."); |
|
115
|
|
|
|
|
116
|
|
|
$this->dispatcher->dispatch(self::EVENT_READY, []); |
|
117
|
|
|
|
|
118
|
|
|
$this->console->writeln("And takeoff"); |
|
119
|
|
|
|
|
120
|
|
|
do { |
|
121
|
|
|
$startTime = microtime(true); |
|
122
|
|
|
|
|
123
|
|
|
// Run the actuall application |
|
124
|
|
|
$this->executeRun(); |
|
125
|
|
|
|
|
126
|
|
|
$endTime = microtime(true); |
|
127
|
|
|
$delay = $cycleTime - (($endTime - $startTime) * 1000000); |
|
128
|
|
|
|
|
129
|
|
|
// If we got lot's of time and it's been a while since last GC collect let's run a garbage collector |
|
130
|
|
|
// cycle this iteration. |
|
131
|
|
|
if ($delay > $cycleTime / 2 && $lastGcTime < (time() - $gcCycleTime)) { |
|
132
|
|
|
// PHP does this automatically as well but in some mysterious ways it can sometimes keep in memory |
|
133
|
|
|
// hundred of mb's before running it. |
|
134
|
|
|
gc_collect_cycles(); |
|
135
|
|
|
$lastGcTime = time(); |
|
136
|
|
|
|
|
137
|
|
|
// Renew delay so that this iteration isn't much slower then the others |
|
138
|
|
|
$endTime = microtime(true); |
|
139
|
|
|
$delay = $cycleTime - (($endTime - $startTime) * 1000000); |
|
140
|
|
|
} |
|
141
|
|
|
|
|
142
|
|
|
if ($lastGcTime < (time() - $maxGcCycleTime)) { |
|
143
|
|
|
//It's been a while since last Garbage collection forcing it to go even through the application is |
|
144
|
|
|
// running slow. |
|
145
|
|
|
gc_collect_cycles(); |
|
146
|
|
|
$lastGcTime = time(); |
|
147
|
|
|
|
|
148
|
|
|
} elseif ($delay > 0) { |
|
149
|
|
|
usleep($delay); |
|
150
|
|
|
} |
|
151
|
|
|
} while ($this->isRunning); |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
|
|
/** |
|
155
|
|
|
* Stop eXpansion. |
|
156
|
|
|
*/ |
|
157
|
|
|
public function stopApplication() |
|
158
|
|
|
{ |
|
159
|
|
|
$this->dispatcher->dispatch(self::EVENT_STOP, []); |
|
160
|
|
|
$this->isRunning = false; |
|
161
|
|
|
} |
|
162
|
|
|
|
|
163
|
|
|
abstract protected function executeRun(); |
|
164
|
|
|
} |
|
165
|
|
|
|
An exit expression should only be used in rare cases. For example, if you write a short command line script.
In most cases however, using an
exitexpression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.