Passed
Push — master ( 767dcd...21977d )
by Nico
22:25 queued 10:21
created

ColonyTick::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 12
dl 0
loc 15
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Stu\Module\Tick\Colony;
4
5
use Doctrine\Common\Collections\Collection;
6
use InvalidArgumentException;
7
use Override;
0 ignored issues
show
Bug introduced by
The type Override was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use RuntimeException;
9
use Stu\Component\Building\BuildingFunctionEnum;
10
use Stu\Component\Building\BuildingManagerInterface;
11
use Stu\Component\Colony\ColonyFunctionManagerInterface;
12
use Stu\Lib\Transfer\Storage\StorageManagerInterface;
13
use Stu\Lib\ColonyProduction\ColonyProduction;
14
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
15
use Stu\Module\Commodity\Lib\CommodityCacheInterface;
16
use Stu\Module\Logging\LoggerUtilFactoryInterface;
17
use Stu\Module\Logging\LoggerUtilInterface;
18
use Stu\Module\Message\Lib\PrivateMessageFolderTypeEnum;
19
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
20
use Stu\Module\PlayerSetting\Lib\UserEnum;
0 ignored issues
show
Bug introduced by
The type Stu\Module\PlayerSetting\Lib\UserEnum was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
21
use Stu\Module\Tick\Colony\Component\ColonyTickComponentInterface;
22
use Stu\Orm\Entity\BuildingCommodityInterface;
23
use Stu\Orm\Entity\ColonyInterface;
24
use Stu\Orm\Entity\CommodityInterface;
25
use Stu\Orm\Entity\PlanetFieldInterface;
26
use Stu\Orm\Repository\ColonyDepositMiningRepositoryInterface;
27
use Stu\Orm\Repository\ColonyRepositoryInterface;
28
use Stu\Orm\Repository\ModuleQueueRepositoryInterface;
29
use Stu\Orm\Repository\PlanetFieldRepositoryInterface;
30
31
final class ColonyTick implements ColonyTickInterface
32
{
33
    private LoggerUtilInterface $loggerUtil;
34
35
    /**
36
     * @var array<string>
37
     */
38
    private array $msg = [];
39
40
    /** @param array<ColonyTickComponentInterface> $components */
41 1
    public function __construct(
42
        private ModuleQueueRepositoryInterface $moduleQueueRepository,
43
        private PlanetFieldRepositoryInterface $planetFieldRepository,
44
        private PrivateMessageSenderInterface $privateMessageSender,
45
        private StorageManagerInterface $storageManager,
46
        private ColonyRepositoryInterface $colonyRepository,
47
        private BuildingManagerInterface $buildingManager,
48
        private ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository,
49
        private ColonyLibFactoryInterface $colonyLibFactory,
50
        private ColonyFunctionManagerInterface $colonyFunctionManager,
51
        private CommodityCacheInterface $commodityCache,
52
        LoggerUtilFactoryInterface $loggerUtilFactory,
53
        private array $components
54
    ) {
55 1
        $this->loggerUtil = $loggerUtilFactory->getLoggerUtil();
56
    }
57
58
    #[Override]
59
    public function work(ColonyInterface $colony): void
60
    {
61
        $doLog = $this->loggerUtil->doLog();
62
        if ($doLog) {
63
            $startTime = microtime(true);
64
        }
65
66
        $deactivatedFields = $this->mainLoop($colony);
67
68
        $this->colonyRepository->save($colony);
69
70
        $this->proceedModules($colony, $deactivatedFields);
71
        $this->sendMessages($colony);
72
73
        if ($doLog) {
74
            $endTime = microtime(true);
75
            $this->loggerUtil->log(sprintf("Colony-Id: %6d, seconds: %F", $colony->getId(), $endTime - $startTime));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $startTime does not seem to be defined for all execution paths leading up to this point.
Loading history...
76
        }
77
    }
78
79
    /**
80
     * @return array<int>
81
     */
82
    private function mainLoop(ColonyInterface $colony): array
83
    {
84
        $doLog = $this->loggerUtil->doLog();
85
86
        if ($doLog) {
87
            $startTime = microtime(true);
88
        }
89
90
        $i = 1;
91
        $production = $this->colonyLibFactory->createColonyCommodityProduction($colony)->getProduction();
92
93
        $deactivatedFields = [-1];
94
95
        while (true) {
96
97
            $rewind = $this->checkStorage($colony, $production, $deactivatedFields);
98
            $rewind |= $this->checkLivingSpace($colony, $production, $deactivatedFields);
99
            $rewind |= $this->checkEnergyProduction($colony, $production, $deactivatedFields);
100
101
            if ($rewind !== 0) {
102
                $i++;
103
                if ($i == 100) {
104
                    // SECURITY
105
                    //echo "HIT SECURITY BREAK\n";
106
                    break;
107
                }
108
                continue;
109
            }
110
            break;
111
        }
112
        $colony->setEps(
113
            min(
114
                $colony->getMaxEps(),
115
                $colony->getEps() + $this->planetFieldRepository->getEnergyProductionByHost($colony, $deactivatedFields)
116
            )
117
        );
118
119
        if ($doLog) {
120
            $endTime = microtime(true);
121
            $this->loggerUtil->log(sprintf("\tmainLoop, seconds: %F", $endTime - $startTime));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $startTime does not seem to be defined for all execution paths leading up to this point.
Loading history...
122
        }
123
124
        foreach ($this->components as $component) {
125
            $component->work($colony, $production);
126
        }
127
128
        $this->proceedStorage($colony, $production);
129
130
        return $deactivatedFields;
131
    }
132
133
    /**
134
     * @param array<int, ColonyProduction> $production
135
     * @param array<int> $deactivatedFields
136
     */
137
    private function checkStorage(
138
        ColonyInterface $colony,
139
        array &$production,
140
        array &$deactivatedFields
141
    ): bool {
142
143
        $result = false;
144
145
        foreach ($production as $pro) {
146
            if ($pro->getProduction() >= 0) {
147
                continue;
148
            }
149
150
            $commodityId = $pro->getCommodityId();
151
152
            $depositMining = $colony->getUserDepositMinings()[$commodityId] ?? null;
153
            if ($depositMining !== null && $depositMining->isEnoughLeft((int) abs($pro->getProduction()))) {
154
                continue;
155
            }
156
157
            $storage = $colony->getStorage();
158
            $storageItem = $storage[$commodityId] ?? null;
159
            if ($storageItem !== null && $storageItem->getAmount() + $pro->getProduction() >= 0) {
160
                continue;
161
            }
162
            //echo "coloId:" . $colony->getId() . ", production:" . $pro->getProduction() . ", commodityId:" . $commodityId . ", commodity:" . $this->commodityCache->get($commodityId)->getName() . "\n";
163
            $field = $this->getBuildingToDeactivateByCommodity($colony, $commodityId, $deactivatedFields);
164
            // echo $i." hit by commodity ".$field->getFieldId()." - produce ".$pro->getProduction()." MT ".microtime()."\n";
165
            $this->deactivateBuilding($field, $production, $this->commodityCache->get($commodityId));
166
            $deactivatedFields[] = $field->getFieldId();
167
168
            $result = true;
169
        }
170
171
        return $result;
172
    }
173
174
    /**
175
     * @param array<int, ColonyProduction> $production
176
     * @param array<int> $deactivatedFields
177
     */
178
    private function checkLivingSpace(
179
        ColonyInterface $colony,
180
        array &$production,
181
        array &$deactivatedFields
182
    ): bool {
183
        if ($colony->getWorkers() > $colony->getMaxBev()) {
184
            $field = $this->getBuildingToDeactivateByLivingSpace($colony, $deactivatedFields);
185
            if ($field !== null) {
186
                $this->deactivateBuilding($field, $production, 'Wohnraum');
187
                $deactivatedFields[] = $field->getFieldId();
188
189
                return true;
190
            }
191
        }
192
193
        return false;
194
    }
195
196
    /**
197
     * @param array<int, ColonyProduction> $production
198
     * @param array<int> $deactivatedFields
199
     */
200
    private function checkEnergyProduction(
201
        ColonyInterface $colony,
202
        array &$production,
203
        array &$deactivatedFields
204
    ): bool {
205
        $energyProduction = $this->planetFieldRepository->getEnergyProductionByHost($colony, $deactivatedFields);
206
207
        if ($energyProduction < 0 && $colony->getEps() + $energyProduction < 0) {
208
            $field = $this->getBuildingToDeactivateByEpsUsage($colony, $deactivatedFields);
209
            //echo $i . " hit by eps " . $field->getFieldId() . " - complete usage " . $colony->getEpsProduction() . " - usage " . $field->getBuilding()->getEpsProduction() . " MT " . microtime() . "\n";
210
            $this->deactivateBuilding($field, $production, 'Energie');
211
            $deactivatedFields[] = $field->getFieldId();
212
213
            return true;
214
        }
215
216
        return false;
217
    }
218
219
    /**
220
     * @param array<int, ColonyProduction> $production
221
     */
222
    private function deactivateBuilding(
223
        PlanetFieldInterface $field,
224
        array &$production,
225
        CommodityInterface|string $cause
226
    ): void {
227
        $ext = $cause instanceof CommodityInterface ? $cause->getName() : $cause;
228
        $building = $field->getBuilding();
229
230
        if ($building === null) {
231
            throw new InvalidArgumentException('can not deactivate field without building');
232
        }
233
234
        $this->buildingManager->deactivate($field);
235
236
        $this->mergeProduction($building->getCommodities(), $production);
237
238
        $this->msg[] = $building->getName() . " auf Feld " . $field->getFieldId() . " deaktiviert (Mangel an " . $ext . ")";
239
    }
240
241
    /**
242
     * @param array<int> $deactivatedFields
243
     */
244
    private function getBuildingToDeactivateByCommodity(
245
        ColonyInterface $colony,
246
        int $commodityId,
247
        array $deactivatedFields
248
    ): PlanetFieldInterface {
249
        $fields = $this->planetFieldRepository->getCommodityConsumingByHostAndCommodity(
250
            $colony,
251
            $commodityId,
252
            [1],
253
            1,
254
            $deactivatedFields
255
        );
256
257
        $result = current($fields);
258
        if (!$result) {
259
            throw new RuntimeException('no building found');
260
        }
261
262
        return $result;
263
    }
264
265
    /**
266
     * @param array<int> $deactivatedFields
267
     */
268
    private function getBuildingToDeactivateByEpsUsage(
269
        ColonyInterface $colony,
270
        array $deactivatedFields
271
    ): PlanetFieldInterface {
272
        $fields = $this->planetFieldRepository->getEnergyConsumingByHost(
273
            $colony,
274
            [1],
275
            1,
276
            $deactivatedFields
277
        );
278
279
        $result = current($fields);
280
        if (!$result) {
281
            throw new RuntimeException('no building found');
282
        }
283
284
        return $result;
285
    }
286
287
    /**
288
     * @param array<int> $deactivatedFields
289
     */
290
    private function getBuildingToDeactivateByLivingSpace(
291
        ColonyInterface $colony,
292
        array $deactivatedFields
293
    ): ?PlanetFieldInterface {
294
        $fields = $this->planetFieldRepository->getWorkerConsumingByColonyAndState(
295
            $colony->getId(),
296
            [1],
297
            1,
298
            $deactivatedFields
299
        );
300
301
        return $fields === [] ? null : current($fields);
302
    }
303
304
    /**
305
     * @param array<ColonyProduction> $production
306
     */
307
    private function proceedStorage(
308
        ColonyInterface $colony,
309
        array $production
310
    ): void {
311
        $doLog = $this->loggerUtil->doLog();
312
        if ($doLog) {
313
            $startTime = microtime(true);
314
        }
315
316
        $sum = $colony->getStorageSum();
317
318
        if ($doLog) {
319
            $startTime = microtime(true);
320
        }
321
322
        //DECREASE
323
        foreach ($production as $commodityId => $obj) {
324
            $amount = $obj->getProduction();
325
            $commodity = $this->commodityCache->get($commodityId);
326
327
            if ($amount < 0) {
328
                $amount = abs($amount);
329
330
                if ($commodity->isSaveable()) {
331
                    // STANDARD
332
                    $this->storageManager->lowerStorage(
333
                        $colony,
334
                        $this->commodityCache->get($commodityId),
335
                        $amount
0 ignored issues
show
Bug introduced by
It seems like $amount can also be of type double; however, parameter $amount of Stu\Lib\Transfer\Storage...terface::lowerStorage() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

335
                        /** @scrutinizer ignore-type */ $amount
Loading history...
336
                    );
337
                    $sum -= $amount;
338
                } else {
339
                    // EFFECTS
340
                    $depositMining = $colony->getUserDepositMinings()[$commodityId];
341
342
                    $depositMining->setAmountLeft($depositMining->getAmountLeft() - $amount);
0 ignored issues
show
Bug introduced by
$depositMining->getAmountLeft() - $amount of type double is incompatible with the type integer expected by parameter $amountLeft of Stu\Orm\Entity\ColonyDep...erface::setAmountLeft(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

342
                    $depositMining->setAmountLeft(/** @scrutinizer ignore-type */ $depositMining->getAmountLeft() - $amount);
Loading history...
343
                    $this->colonyDepositMiningRepository->save($depositMining);
344
                }
345
            }
346
        }
347
        if ($doLog) {
348
            $endTime = microtime(true);
349
            $this->loggerUtil->log(sprintf("\tforeach1, seconds: %F", $endTime - $startTime));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $startTime does not seem to be defined for all execution paths leading up to this point.
Loading history...
350
        }
351
352
        if ($doLog) {
353
            $startTime = microtime(true);
354
        }
355
        foreach ($production as $commodityId => $obj) {
356
            $startTimeC = microtime(true);
357
358
            $commodity = $this->commodityCache->get($commodityId);
359
            if ($obj->getProduction() <= 0 || !$commodity->isSaveable()) {
360
                continue;
361
            }
362
            if ($sum >= $colony->getMaxStorage()) {
363
                if ($colony->getUser()->isStorageNotification()) {
364
                    $this->msg[] = _('Das Lager der Kolonie ist voll');
365
                }
366
                break;
367
            }
368
            if ($sum + $obj->getProduction() > $colony->getMaxStorage()) {
369
                $this->storageManager->upperStorage(
370
                    $colony,
371
                    $commodity,
372
                    $colony->getMaxStorage() - $sum
373
                );
374
                if ($colony->getUser()->isStorageNotification()) {
375
                    $this->msg[] = _('Das Lager der Kolonie ist voll');
376
                }
377
                break;
378
            }
379
            $startTimeM = microtime(true);
380
            $this->storageManager->upperStorage(
381
                $colony,
382
                $commodity,
383
                $obj->getProduction()
384
            );
385
            if ($doLog) {
386
                $endTimeM = microtime(true);
387
                $this->loggerUtil->log(sprintf("\t\t\tupper, seconds: %F", $endTimeM - $startTimeM));
388
            }
389
            $sum += $obj->getProduction();
390
            if ($doLog) {
391
                $endTimeC = microtime(true);
392
                $this->loggerUtil->log(sprintf("\t\tcommodity: %s, seconds: %F", $commodity->getName(), $endTimeC - $startTimeC));
393
            }
394
        }
395
        if ($doLog) {
396
            $endTime = microtime(true);
397
            $this->loggerUtil->log(sprintf("\tforeach2, seconds: %F", $endTime - $startTime));
398
        }
399
400
        if ($doLog) {
401
            $startTime = microtime(true);
402
        }
403
404
        if ($doLog) {
405
            $endTime = microtime(true);
406
            $this->loggerUtil->log(sprintf("\tresearch, seconds: %F", $endTime - $startTime));
407
        }
408
409
        if ($colony->getPopulation() > $colony->getMaxBev()) {
410
            $this->proceedEmigration($colony);
411
            return;
412
        }
413
414
        if ($colony->getPopulationLimit() > 0 && $colony->getPopulation() > $colony->getPopulationLimit() && $colony->getWorkless()) {
415
            if (($free = ($colony->getPopulationLimit() - $colony->getWorkers())) > 0) {
416
                $this->msg[] = sprintf(
417
                    _('Es sind %d Arbeitslose ausgewandert'),
418
                    ($colony->getWorkless() - $free)
419
                );
420
                $colony->setWorkless($free);
421
            } else {
422
                $this->msg[] = _('Es sind alle Arbeitslosen ausgewandert');
423
                $colony->setWorkless(0);
424
            }
425
        }
426
        $this->proceedImmigration(
427
            $colony,
428
            $production
429
        );
430
431
        if ($doLog) {
432
            $endTime = microtime(true);
433
            $this->loggerUtil->log(sprintf("\tstorage, seconds: %F", $endTime - $startTime));
434
        }
435
    }
436
437
    /**
438
     * @param array<int> $deactivatedFields
439
     */
440
    private function proceedModules(ColonyInterface $colony, array $deactivatedFields): void
441
    {
442
        foreach ($this->moduleQueueRepository->getByColony($colony->getId()) as $queue) {
443
            $buildingFunction = $queue->getBuildingFunction();
444
445
            //spare parts and system components are generated by spacecraft tick manager, to avoid dead locks
446
            if (
447
                $buildingFunction === BuildingFunctionEnum::FABRICATION_HALL ||
448
                $buildingFunction === BuildingFunctionEnum::TECH_CENTER
449
            ) {
450
                continue;
451
            }
452
453
            if ($this->colonyFunctionManager->hasActiveFunction($colony, $buildingFunction, false, $deactivatedFields)) {
454
                $this->storageManager->upperStorage(
455
                    $colony,
456
                    $queue->getModule()->getCommodity(),
457
                    $queue->getAmount()
458
                );
459
460
                $this->msg[] = sprintf(
461
                    _('Es wurden %d %s hergestellt'),
462
                    $queue->getAmount(),
463
                    $queue->getModule()->getName()
464
                );
465
                $this->moduleQueueRepository->delete($queue);
466
            }
467
        }
468
    }
469
470
    /**
471
     * @param array<int, ColonyProduction> $production
472
     */
473
    private function proceedImmigration(
474
        ColonyInterface $colony,
475
        array $production
476
    ): void {
477
        // @todo
478
        $colony->setWorkless(
479
            $colony->getWorkless() +
480
                $this->colonyLibFactory->createColonyPopulationCalculator($colony, $production)->getGrowth()
481
        );
482
    }
483
484
    private function proceedEmigration(ColonyInterface $colony): void
485
    {
486
        if ($colony->getWorkless() !== 0) {
487
            $bev = random_int(1, $colony->getWorkless());
488
            $colony->setWorkless($colony->getWorkless() - $bev);
489
            $this->msg[] = $bev . " Einwohner sind ausgewandert";
490
        }
491
    }
492
493
    private function sendMessages(ColonyInterface $colony): void
494
    {
495
        if ($this->msg === []) {
496
            return;
497
        }
498
        $text = "Tickreport der Kolonie " . $colony->getName() . "\n";
499
        foreach ($this->msg as $msg) {
500
            $text .= $msg . "\n";
501
        }
502
503
        $this->privateMessageSender->send(
504
            UserEnum::USER_NOONE,
505
            $colony->getUserId(),
506
            $text,
507
            PrivateMessageFolderTypeEnum::SPECIAL_COLONY,
508
            $colony
509
        );
510
511
        $this->msg = [];
512
    }
513
514
    /**
515
     * @param Collection<int, BuildingCommodityInterface> $buildingProduction
516
     * @param array<int, ColonyProduction> $production
517
     */
518
    private function mergeProduction(
519
        Collection $buildingProduction,
520
        array &$production
521
    ): void {
522
        foreach ($buildingProduction as $obj) {
523
            $commodityId = $obj->getCommodityId();
524
            if (!array_key_exists($commodityId, $production)) {
525
                $data = $this->colonyLibFactory->createColonyProduction(
526
                    $obj->getCommodity(),
527
                    $obj->getAmount() * -1
528
                );
529
                $production[$commodityId] = $data;
530
            } elseif ($obj->getAmount() < 0) {
531
                $production[$commodityId]->upperProduction(abs($obj->getAmount()));
0 ignored issues
show
Bug introduced by
It seems like abs($obj->getAmount()) can also be of type double; however, parameter $value of Stu\Lib\ColonyProduction...tion::upperProduction() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

531
                $production[$commodityId]->upperProduction(/** @scrutinizer ignore-type */ abs($obj->getAmount()));
Loading history...
532
            } else {
533
                $production[$commodityId]->lowerProduction($obj->getAmount());
534
            }
535
        }
536
    }
537
}
538