Passed
Push — dev ( 766009...bcc49c )
by Janko
09:42
created

ColonyTick::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 14
nc 1
nop 14
dl 0
loc 30
ccs 0
cts 15
cp 0
crap 2
rs 9.7998
c 1
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 Doctrine\ORM\EntityManagerInterface;
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Stu\Component\Building\BuildingEnum;
10
use Stu\Component\Building\BuildingManagerInterface;
11
use Stu\Component\Colony\ColonyFunctionManagerInterface;
12
use Stu\Component\Colony\Storage\ColonyStorageManagerInterface;
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\PrivateMessageFolderSpecialEnum;
19
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
20
use Stu\Module\PlayerSetting\Lib\UserEnum;
21
use Stu\Module\Research\ResearchStateFactoryInterface;
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
use Stu\Orm\Repository\ResearchedRepositoryInterface;
31
32
final class ColonyTick implements ColonyTickInterface
33
{
34
    private ResearchedRepositoryInterface $researchedRepository;
35
36
    private ModuleQueueRepositoryInterface $moduleQueueRepository;
37
38
    private PlanetFieldRepositoryInterface $planetFieldRepository;
39
40
    private PrivateMessageSenderInterface $privateMessageSender;
41
42
    private ColonyStorageManagerInterface $colonyStorageManager;
43
44
    private ColonyRepositoryInterface $colonyRepository;
45
46
    private BuildingManagerInterface $buildingManager;
47
48
    private ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository;
49
50
    private EntityManagerInterface $entityManager;
51
52
    private ColonyLibFactoryInterface $colonyLibFactory;
53
54
    private ColonyFunctionManagerInterface $colonyFunctionManager;
55
56
    private ResearchStateFactoryInterface $researchStateFactory;
57
58
    private CommodityCacheInterface $commodityCache;
59
60
    private LoggerUtilInterface $loggerUtil;
61
62
    /**
63
     * @var array<string>
64
     */
65
    private array $msg = [];
66
67
    public function __construct(
68
        ResearchedRepositoryInterface $researchedRepository,
69
        ModuleQueueRepositoryInterface $moduleQueueRepository,
70
        PlanetFieldRepositoryInterface $planetFieldRepository,
71
        PrivateMessageSenderInterface $privateMessageSender,
72
        ColonyStorageManagerInterface $colonyStorageManager,
73
        ColonyRepositoryInterface $colonyRepository,
74
        BuildingManagerInterface $buildingManager,
75
        ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository,
76
        EntityManagerInterface $entityManager,
77
        ColonyLibFactoryInterface $colonyLibFactory,
78
        ColonyFunctionManagerInterface $colonyFunctionManager,
79
        ResearchStateFactoryInterface $researchStateFactory,
80
        CommodityCacheInterface $commodityCache,
81
        LoggerUtilFactoryInterface $loggerUtilFactory
82
    ) {
83
        $this->researchedRepository = $researchedRepository;
84
        $this->moduleQueueRepository = $moduleQueueRepository;
85
        $this->planetFieldRepository = $planetFieldRepository;
86
        $this->privateMessageSender = $privateMessageSender;
87
        $this->colonyStorageManager = $colonyStorageManager;
88
        $this->colonyRepository = $colonyRepository;
89
        $this->buildingManager = $buildingManager;
90
        $this->colonyDepositMiningRepository = $colonyDepositMiningRepository;
91
        $this->entityManager = $entityManager;
92
        $this->colonyLibFactory = $colonyLibFactory;
93
        $this->colonyFunctionManager = $colonyFunctionManager;
94
        $this->researchStateFactory = $researchStateFactory;
95
        $this->commodityCache = $commodityCache;
96
        $this->loggerUtil = $loggerUtilFactory->getLoggerUtil();
97
    }
98
99
    public function work(ColonyInterface $colony): void
100
    {
101
        $doLog = $this->loggerUtil->doLog();
102
        if ($doLog) {
103
            $startTime = microtime(true);
104
        }
105
106
        $this->mainLoop($colony);
107
108
        $this->colonyRepository->save($colony);
109
110
        $this->proceedModules($colony);
111
        $this->sendMessages($colony);
112
113
        if ($doLog) {
114
            $endTime = microtime(true);
115
            $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...
116
        }
117
    }
118
119
    private function mainLoop(ColonyInterface $colony): void
120
    {
121
        $doLog = $this->loggerUtil->doLog();
122
123
        if ($doLog) {
124
            $startTime = microtime(true);
125
        }
126
127
        $i = 1;
128
        $production = $this->colonyLibFactory->createColonyCommodityProduction($colony)->getProduction();
129
130
        while (true) {
131
132
            $rewind = $this->checkStorage($colony, $production);
133
            $rewind |= $this->checkLivingSpace($colony, $production);
134
            $rewind |= $this->checkEnergyProduction($colony, $production);
135
136
            if ($rewind) {
137
                $i++;
138
                if ($i == 100) {
139
                    // SECURITY
140
                    //echo "HIT SECURITY BREAK\n";
141
                    break;
142
                }
143
                continue;
144
            }
145
            break;
146
        }
147
        $colony->setEps(
148
            min(
149
                $colony->getMaxEps(),
150
                $colony->getEps() + $this->planetFieldRepository->getEnergyProductionByColony($colony->getId())
151
            )
152
        );
153
154
        if ($doLog) {
155
            $endTime = microtime(true);
156
            $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...
157
        }
158
159
        $this->proceedStorage($colony, $production);
160
    }
161
162
    /**
163
     * @param array<int, ColonyProduction> $production
164
     */
165
    private function checkStorage(
166
        ColonyInterface $colony,
167
        array &$production
168
    ): bool {
169
170
        $result = false;
171
172
        foreach ($production as $pro) {
173
            if ($pro->getProduction() >= 0) {
174
                continue;
175
            }
176
177
            $commodityId = $pro->getCommodityId();
178
179
            $depositMining = $colony->getUserDepositMinings()[$commodityId] ?? null;
180
            if ($depositMining !== null) {
181
                if ($depositMining->isEnoughLeft((int) abs($pro->getProduction()))) {
182
                    continue;
183
                }
184
            }
185
186
            $storage = $colony->getStorage();
187
            $storageItem = $storage[$commodityId] ?? null;
188
            if ($storageItem !== null && $storageItem->getAmount() + $pro->getProduction() >= 0) {
189
                continue;
190
            }
191
            //echo "coloId:" . $colony->getId() . ", production:" . $pro->getProduction() . ", commodityId:" . $commodityId . ", commodity:" . $this->commodityCache->get($commodityId)->getName() . "\n";
192
            $field = $this->getBuildingToDeactivateByCommodity($colony, $commodityId);
193
            // echo $i." hit by commodity ".$field->getFieldId()." - produce ".$pro->getProduction()." MT ".microtime()."\n";
194
            $this->deactivateBuilding($field, $production, $this->commodityCache->get($commodityId));
195
196
            $result = true;
197
        }
198
199
        return $result;
200
    }
201
202
    /**
203
     * @param array<int, ColonyProduction> $production
204
     */
205
    private function checkLivingSpace(ColonyInterface $colony, array &$production): bool
206
    {
207
        if ($colony->getWorkers() > $colony->getMaxBev()) {
208
            $field = $this->getBuildingToDeactivateByLivingSpace($colony);
209
            if ($field !== null) {
210
                $this->deactivateBuilding($field, $production, 'Wohnraum');
211
212
                return true;
213
            }
214
        }
215
216
        return false;
217
    }
218
219
    /**
220
     * @param array<int, ColonyProduction> $production
221
     */
222
    private function checkEnergyProduction(ColonyInterface $colony, array &$production): bool
223
    {
224
        $energyProduction = $this->planetFieldRepository->getEnergyProductionByColony($colony->getId());
225
226
        if ($energyProduction < 0 && $colony->getEps() + $energyProduction < 0) {
227
            $field = $this->getBuildingToDeactivateByEpsUsage($colony);
228
            //echo $i . " hit by eps " . $field->getFieldId() . " - complete usage " . $colony->getEpsProduction() . " - usage " . $field->getBuilding()->getEpsProduction() . " MT " . microtime() . "\n";
229
            $this->deactivateBuilding($field, $production, 'Energie');
230
231
            return true;
232
        }
233
234
        return false;
235
    }
236
237
    /**
238
     * @param array<ColonyProduction> $production
239
     */
240
    private function deactivateBuilding(
241
        PlanetFieldInterface $field,
242
        array &$production,
243
        CommodityInterface|string $cause
244
    ): void {
245
        if ($cause instanceof CommodityInterface) {
246
            $ext = $cause->getName();
247
        } else {
248
            $ext = $cause;
249
        }
250
        $building = $field->getBuilding();
251
252
        if ($building === null) {
253
            throw new InvalidArgumentException('can not deactivate field without building');
254
        }
255
256
        $this->buildingManager->deactivate($field);
257
        $this->entityManager->flush();
258
259
        $this->mergeProduction($building->getCommodities(), $production);
260
261
        $this->msg[] = $building->getName() . " auf Feld " . $field->getFieldId() . " deaktiviert (Mangel an " . $ext . ")";
262
    }
263
264
    private function getBuildingToDeactivateByCommodity(ColonyInterface $colony, int $commodityId): PlanetFieldInterface
265
    {
266
        $fields = $this->planetFieldRepository->getCommodityConsumingByColonyAndCommodity(
267
            $colony->getId(),
268
            $commodityId,
269
            [1]
270
        );
271
272
        $result = current($fields);
0 ignored issues
show
Bug introduced by
$fields of type iterable is incompatible with the type array|object expected by parameter $array of current(). ( Ignorable by Annotation )

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

272
        $result = current(/** @scrutinizer ignore-type */ $fields);
Loading history...
273
        if (!$result) {
274
            throw new RuntimeException('no building found');
275
        }
276
277
        return $result;
278
    }
279
280
    private function getBuildingToDeactivateByEpsUsage(ColonyInterface $colony): PlanetFieldInterface
281
    {
282
        $fields = $this->planetFieldRepository->getEnergyConsumingByColony($colony->getId(), [1], 1);
283
284
        $result = current($fields);
0 ignored issues
show
Bug introduced by
$fields of type iterable is incompatible with the type array|object expected by parameter $array of current(). ( Ignorable by Annotation )

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

284
        $result = current(/** @scrutinizer ignore-type */ $fields);
Loading history...
285
        if (!$result) {
286
            throw new RuntimeException('no building found');
287
        }
288
289
        return $result;
290
    }
291
292
    private function getBuildingToDeactivateByLivingSpace(ColonyInterface $colony): ?PlanetFieldInterface
293
    {
294
        $fields = $this->planetFieldRepository->getWorkerConsumingByColonyAndState($colony->getId(), [1], 1);
295
296
        return empty($fields) ? null : current($fields);
0 ignored issues
show
Bug introduced by
$fields of type iterable is incompatible with the type array|object expected by parameter $array of current(). ( Ignorable by Annotation )

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

296
        return empty($fields) ? null : current(/** @scrutinizer ignore-type */ $fields);
Loading history...
297
    }
298
299
    /**
300
     * @param array<ColonyProduction> $production
301
     */
302
    private function proceedStorage(
303
        ColonyInterface $colony,
304
        array $production
305
    ): void {
306
        $doLog = $this->loggerUtil->doLog();
307
        if ($doLog) {
308
            $startTime = microtime(true);
309
        }
310
311
        $sum = $colony->getStorageSum();
312
313
        if ($doLog) {
314
            $startTime = microtime(true);
315
        }
316
317
        //DECREASE
318
        foreach ($production as $commodityId => $obj) {
319
            $amount = $obj->getProduction();
320
            $commodity = $this->commodityCache->get($commodityId);
321
322
            if ($amount < 0) {
323
                $amount = (int) abs($amount);
324
325
                if ($commodity->isSaveable()) {
326
                    // STANDARD
327
                    $this->colonyStorageManager->lowerStorage(
328
                        $colony,
329
                        $this->commodityCache->get($commodityId),
330
                        $amount
331
                    );
332
                    $sum -= $amount;
333
                } else {
334
                    // EFFECTS
335
                    $depositMining = $colony->getUserDepositMinings()[$commodityId];
336
337
                    $depositMining->setAmountLeft($depositMining->getAmountLeft() - $amount);
338
                    $this->colonyDepositMiningRepository->save($depositMining);
339
                }
340
            }
341
        }
342
        if ($doLog) {
343
            $endTime = microtime(true);
344
            $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...
345
        }
346
347
        if ($doLog) {
348
            $startTime = microtime(true);
349
        }
350
        foreach ($production as $commodityId => $obj) {
351
            $startTimeC = microtime(true);
352
353
            $commodity = $this->commodityCache->get($commodityId);
354
            if ($obj->getProduction() <= 0 || !$commodity->isSaveable()) {
355
                continue;
356
            }
357
            if ($sum >= $colony->getMaxStorage()) {
358
                if ($colony->getUser()->isStorageNotification()) {
359
                    $this->msg[] = _('Das Lager der Kolonie ist voll');
360
                }
361
                break;
362
            }
363
            if ($sum + $obj->getProduction() > $colony->getMaxStorage()) {
364
                $this->colonyStorageManager->upperStorage(
365
                    $colony,
366
                    $commodity,
367
                    $colony->getMaxStorage() - $sum
368
                );
369
                if ($colony->getUser()->isStorageNotification()) {
370
                    $this->msg[] = _('Das Lager der Kolonie ist voll');
371
                }
372
                break;
373
            }
374
            $startTimeM = microtime(true);
375
            $this->colonyStorageManager->upperStorage(
376
                $colony,
377
                $commodity,
378
                $obj->getProduction()
379
            );
380
            if ($doLog) {
381
                $endTimeM = microtime(true);
382
                $this->loggerUtil->log(sprintf("\t\t\tupper, seconds: %F", $endTimeM - $startTimeM));
383
            }
384
            $sum += $obj->getProduction();
385
            if ($doLog) {
386
                $endTimeC = microtime(true);
387
                $this->loggerUtil->log(sprintf("\t\tcommodity: %s, seconds: %F", $commodity->getName(), $endTimeC - $startTimeC));
388
            }
389
        }
390
        if ($doLog) {
391
            $endTime = microtime(true);
392
            $this->loggerUtil->log(sprintf("\tforeach2, seconds: %F", $endTime - $startTime));
393
        }
394
395
        if ($doLog) {
396
            $startTime = microtime(true);
397
        }
398
        $current_research = $this->researchedRepository->getCurrentResearch($colony->getUser());
399
400
        if ($current_research && $current_research->getActive()) {
401
            if (isset($production[$current_research->getResearch()->getCommodityId()])) {
402
                $this->researchStateFactory->createResearchState()->advance(
403
                    $current_research,
404
                    $production[$current_research->getResearch()->getCommodityId()]->getProduction()
405
                );
406
            }
407
        }
408
        if ($doLog) {
409
            $endTime = microtime(true);
410
            $this->loggerUtil->log(sprintf("\tresearch, seconds: %F", $endTime - $startTime));
411
        }
412
413
        if ($colony->getPopulation() > $colony->getMaxBev()) {
414
            $this->proceedEmigration($colony);
415
            return;
416
        }
417
418
        if ($colony->getPopulationLimit() > 0 && $colony->getPopulation() > $colony->getPopulationLimit() && $colony->getWorkless()) {
419
            if (($free = ($colony->getPopulationLimit() - $colony->getWorkers())) > 0) {
420
                $this->msg[] = sprintf(
421
                    _('Es sind %d Arbeitslose ausgewandert'),
422
                    ($colony->getWorkless() - $free)
423
                );
424
                $colony->setWorkless($free);
425
            } else {
426
                $this->msg[] = _('Es sind alle Arbeitslosen ausgewandert');
427
                $colony->setWorkless(0);
428
            }
429
        }
430
        $this->proceedImmigration(
431
            $colony,
432
            $production
433
        );
434
435
        if ($doLog) {
436
            $endTime = microtime(true);
437
            $this->loggerUtil->log(sprintf("\tstorage, seconds: %F", $endTime - $startTime));
438
        }
439
    }
440
441
    private function proceedModules(ColonyInterface $colony): void
442
    {
443
        foreach ($this->moduleQueueRepository->getByColony((int) $colony->getId()) as $queue) {
444
            $buildingFunction = $queue->getBuildingFunction();
445
446
            //spare parts and system components are generated by ship tick manager, to avoid dead locks
447
            if (
448
                $buildingFunction === BuildingEnum::BUILDING_FUNCTION_FABRICATION_HALL ||
449
                $buildingFunction === BuildingEnum::BUILDING_FUNCTION_TECH_CENTER
450
            ) {
451
                continue;
452
            }
453
454
            if ($this->colonyFunctionManager->hasActiveFunction($colony, $buildingFunction, false)) {
455
                $this->colonyStorageManager->upperStorage(
456
                    $colony,
457
                    $queue->getModule()->getCommodity(),
458
                    $queue->getAmount()
459
                );
460
461
                $this->msg[] = sprintf(
462
                    _('Es wurden %d %s hergestellt'),
463
                    $queue->getAmount(),
464
                    $queue->getModule()->getName()
465
                );
466
                $this->moduleQueueRepository->delete($queue);
467
            }
468
        }
469
    }
470
471
    /**
472
     * @param array<int, ColonyProduction> $production
473
     */
474
    private function proceedImmigration(
475
        ColonyInterface $colony,
476
        array $production
477
    ): void {
478
        // @todo
479
        $colony->setWorkless(
480
            $colony->getWorkless() +
481
                $this->colonyLibFactory->createColonyPopulationCalculator($colony, $production)->getGrowth()
482
        );
483
    }
484
485
    private function proceedEmigration(ColonyInterface $colony): void
486
    {
487
        if ($colony->getWorkless()) {
488
            $bev = rand(1, $colony->getWorkless());
489
            $colony->setWorkless($colony->getWorkless() - $bev);
490
            $this->msg[] = $bev . " Einwohner sind ausgewandert";
491
        }
492
    }
493
494
    private function sendMessages(ColonyInterface $colony): void
495
    {
496
        if ($this->msg === []) {
497
            return;
498
        }
499
        $text = "Tickreport der Kolonie " . $colony->getName() . "\n";
500
        foreach ($this->msg as $msg) {
501
            $text .= $msg . "\n";
502
        }
503
504
        $href = sprintf(_('colony.php?SHOW_COLONY=1&id=%d'), $colony->getId());
505
506
        $this->privateMessageSender->send(
507
            UserEnum::USER_NOONE,
508
            (int) $colony->getUserId(),
509
            $text,
510
            PrivateMessageFolderSpecialEnum::PM_SPECIAL_COLONY,
511
            $href
512
        );
513
514
        $this->msg = [];
515
    }
516
517
    /**
518
     * @param Collection<int, BuildingCommodityInterface> $buildingProduction
519
     * @param array<ColonyProduction> $production
520
     */
521
    private function mergeProduction(
522
        Collection $buildingProduction,
523
        array &$production
524
    ): void {
525
        foreach ($buildingProduction as $obj) {
526
            $commodityId = $obj->getCommodityId();
527
            if (!array_key_exists($commodityId, $production)) {
528
                $data = $this->colonyLibFactory->createColonyProduction(
529
                    $obj->getCommodity(),
530
                    $obj->getAmount() * -1
531
                );
532
533
                $production[$commodityId] = $data;
534
            } else {
535
                if ($obj->getAmount() < 0) {
536
                    $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

536
                    $production[$commodityId]->upperProduction(/** @scrutinizer ignore-type */ abs($obj->getAmount()));
Loading history...
537
                } else {
538
                    $production[$commodityId]->lowerProduction($obj->getAmount());
539
                }
540
            }
541
        }
542
    }
543
}
544