Completed
Pull Request — master (#404)
by De Cramer
03:04
created

AbstractApplication::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13

Duplication

Lines 13
Ratio 100 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 13
loc 13
rs 9.8333
c 0
b 0
f 0
ccs 7
cts 7
cp 1
cc 1
nc 1
nop 5
crap 1
1
<?php
2
3
4
namespace eXpansion\Framework\Core\Services\Application;
5
6
use eXpansion\Framework\Core\Helpers\Version;
7
use eXpansion\Framework\Core\Services\Console;
8
use eXpansion\Framework\Core\Services\DedicatedConnection\Factory;
9
use Maniaplanet\DedicatedServer\Connection;
10
use Propel\Runtime\Connection\Exception\ConnectionException;
11
use Propel\Runtime\Propel;
12
use Psr\Log\LoggerInterface;
13
use Symfony\Component\Console\Output\OutputInterface;
14
15
abstract class AbstractApplication implements RunInterface
16
{
17
    /** Base eXpansion callbacks. */
18
    const EVENT_BEFORE_INIT = "expansion.before_init";
19
    const EVENT_SWITCH_TO_SCRIPT = "expansion.switched_to_script";
20
    const EVENT_AFTER_INIT = "expansion.after_init";
21
    const EVENT_READY = "expansion.ready";
22
    const EVENT_STOP = "expansion.stop";
23
24
    const EXPANSION_VERSION = "dev";
25
26
    /** @var Factory */
27
    protected $factory;
28
29
    /** @var DispatcherInterface */
30
    protected $dispatcher;
31
32
    /** @var Console */
33
    protected $console;
34
35
    /** @var LoggerInterface */
36
    protected $logger;
37
38
    /** @var Version */
39
    protected $version;
40
41
    /** @var bool */
42
    protected $isRunning = true;
43
44
45
    /**
46
     * AbstractApplication constructor.
47
     *
48
     * @param DispatcherInterface $dispatcher
49
     * @param Factory $factory
50
     * @param Console $output
51
     * @param LoggerInterface $logger
52
     * @param Version $version
53
     */
54 10 View Code Duplication
    public function __construct(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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.

Loading history...
55
        DispatcherInterface $dispatcher,
56
        Factory $factory,
57
        Console $output,
58
        LoggerInterface $logger,
59
        Version $version
60
    ) {
61 10
        $this->factory = $factory;
62 10
        $this->dispatcher = $dispatcher;
63 10
        $this->console = $output;
64 10
        $this->logger = $logger;
65 10
        $this->version = $version;
66 10
    }
67
68
    /**
69
     * Initialize eXpansion.
70
     *
71
     * @param OutputInterface $console
72
     *
73
     * @return $this|mixed
74
     * @throws \Maniaplanet\DedicatedServer\Xmlrpc\TransportException
75
     */
76 1
    public function init(OutputInterface $console)
77
    {
78 1
        $this->checkPhpExtensions($console);
79
80 1
        $this->console->init($console, $this->dispatcher);
81 1
        $this->dispatcher->dispatch(self::EVENT_BEFORE_INIT, []);
82
83 1
        $this->factory->createConnection();
84
85 1
        $this->console->writeln("Running preflight checks...");
86 1
        $this->factory->getConnection()->enableCallbacks(true);
87
88
        // need to send this for scripts to start callback handling
89
        try {
90 1
            $this->factory->getConnection()->triggerModeScriptEvent("XmlRpc.EnableCallbacks", ["True"]);
91 1
            $this->dispatcher->dispatch(self::EVENT_SWITCH_TO_SCRIPT, []);
92
        } catch (\Exception $exception) {
93
            $this->factory->getConnection()->saveMatchSettings('MatchSettings/eXpansion-mode-fail-'.date(DATE_ISO8601).'.txt');
94
95
            $console->writeln("");
96
            $msg = "<error>Failed to start eXpansion. This is probably due to the server not being in script mode.";
97
            $msg .= " Please switch <info>game_mode</info> to <info>0</info> and configure the proper <info>script_name</info> in your match setting file";
98
            $msg .= "</error>";
99
            $console->writeln("$msg");
100
            $console->writeln("");
101
102
            throw $exception;
103
        }
104
105 1
        $this->dispatcher->init($this->factory->getConnection());
106
107 1
        $this->dispatcher->dispatch(self::EVENT_AFTER_INIT, []);
108 1
        return $this;
109
    }
110
111 1
    protected function checkPhpExtensions(OutputInterface $console)
112
    {
113
        $extensions = array(
114 1
            'openssl' => 'extension=php_openssl.dll',
115
            'curl' => 'extension=curl.dll',
116
        );
117 1
        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
118
            $extensions['com_dotnet'] = 'extension=php_com_dotnet.dll';
119
        }
120
121 1
        $status = true;
122 1
        $showIni = false;
123 1 View Code Duplication
        foreach ($extensions as $extension => $description) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
124 1
            if (!extension_loaded($extension)) {
125
                $console->writeln(
126
                    "<error>eXpansion needs PHP extension $extension to run. Enable it to run eXpansion => " . $description . "</error>"
127
                );
128
                $status = false;
129
                $showIni = true;
130
            }
131
        }
132
133
        $recommend = array(
134 1
            'xmlrpc' => "It will have better performances !",
135
        );
136 1 View Code Duplication
        foreach ($recommend as $extension => $reason) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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.

Loading history...
137 1
            if (!extension_loaded($extension)) {
138
                $console->writeln(
139
                    "<error>eXpansion works better with PHP extension</error> <info>$extension</info>: " . $reason . ""
140
                );
141
                $showIni = true;
142
            }
143
        }
144
145 1
        if ($showIni) {
146
            $console->writeln('<info>[PHP] PHP is using fallowing ini file :</info> "'. php_ini_loaded_file() .'"');
147
            sleep(5);
148
        }
149 1
        return $status;
150
    }
151
152
    /**
153
     * Run eXpansion
154
     *
155
     * @inheritdoc
156
     */
157 2
    public function run()
158
    {
159
        // Time each cycle needs to take in microseconds. Wrunning 60 cycles per seconds to have optimal response time.
160 2
        $cycleTime = (1 / 60) * 1000000;
161
162
        // Running GC collect every 5 minutes should be sufficient.;
163 2
        $gcCycleTime = 60 * 5;
164
165
        // Time when we will force gc cycles.
166 2
        $maxGcCycleTime = 60 * 20;
167
168
        // Last time garbage collector ran. Assume that at start it ran.
169 2
        $lastGcTime = time();
170
171 2
        $this->console->writeln("preflight checks OK.");
172
173 2
        $this->dispatcher->dispatch(self::EVENT_READY, []);
174
175 2
        $this->console->writeln("And takeoff");
176
177
        do {
178 2
            $startTime = microtime(true);
179
180
            // Run the actuall application
181 2
            $this->executeRun();
182
183 2
            $endTime = microtime(true);
184 2
            $delay = $cycleTime - (($endTime - $startTime) * 1000000);
185
186
            // If we got lot's of time and it's been a while since last GC collect let's run a garbage collector
187
            // cycle this iteration.
188 2
            if ($delay > $cycleTime / 2 && $lastGcTime < (time() - $gcCycleTime)) {
189
                // PHP does this automatically as well but in some mysterious ways it can sometimes keep in memory
190
                // hundred of mb's before running it.
191
                gc_collect_cycles();
192
                $lastGcTime = time();
193
194
                // Renew delay so that this iteration isn't much slower then the others
195
                $endTime = microtime(true);
196
                $delay = $cycleTime - (($endTime - $startTime) * 1000000);
197
            }
198
199 2
            if ($lastGcTime < (time() - $maxGcCycleTime)) {
200
                //It's been a while since last Garbage collection forcing it to go even through the application is
201
                // running slow.
202
                gc_collect_cycles();
203
                $lastGcTime = time();
204
205 2
            } elseif ($delay > 0) {
206 2
                usleep($delay);
207
            }
208 2
        } while ($this->isRunning);
209
210 2
        $this->factory->getConnection()->sendHideManialinkPage(null);
211 2
        $this->factory->getConnection()->triggerModeScriptEvent("Shootmania.UI.ResetProperties", []);
212 2
        $this->factory->getConnection()->triggerModeScriptEvent("Trackmania.UI.ResetProperties", []);
213 2
    }
214
215
    /**
216
     * Stop eXpansion.
217
     */
218 2
    public function stopApplication()
219
    {
220 2
        $this->dispatcher->dispatch(self::EVENT_STOP, []);
221 2
        $this->isRunning = false;
222 2
    }
223
224
    abstract protected function executeRun();
225
}
226