Passed
Push — dev ( 66fe9c...34655c )
by Nico
18:31
created

ColonyTick::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 40
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 19
nc 1
nop 19
dl 0
loc 40
ccs 0
cts 20
cp 0
crap 2
rs 9.6333
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 Doctrine\ORM\EntityManagerInterface;
7
use Stu\Component\Building\BuildingEnum;
8
use Stu\Component\Building\BuildingManagerInterface;
9
use Stu\Component\Colony\ColonyFunctionManagerInterface;
10
use Stu\Component\Colony\Storage\ColonyStorageManagerInterface;
11
use Stu\Component\Ship\System\ShipSystemManagerInterface;
12
use Stu\Lib\ColonyProduction\ColonyProduction;
13
use Stu\Module\Award\Lib\CreateUserAwardInterface;
14
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
15
use Stu\Module\Crew\Lib\CrewCreatorInterface;
16
use Stu\Module\Database\Lib\CreateDatabaseEntryInterface;
17
use Stu\Module\Logging\LoggerUtilFactoryInterface;
18
use Stu\Module\Logging\LoggerUtilInterface;
19
use Stu\Module\Message\Lib\PrivateMessageFolderSpecialEnum;
20
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
21
use Stu\Module\PlayerSetting\Lib\UserEnum;
22
use Stu\Module\Research\ResearchState;
23
use Stu\Module\Ship\Lib\ShipCreatorInterface;
24
use Stu\Orm\Entity\BuildingCommodityInterface;
25
use Stu\Orm\Entity\ColonyDepositMiningInterface;
26
use Stu\Orm\Entity\ColonyInterface;
27
use Stu\Orm\Entity\CommodityInterface;
28
use Stu\Orm\Entity\PlanetFieldInterface;
29
use Stu\Orm\Repository\ColonyDepositMiningRepositoryInterface;
30
use Stu\Orm\Repository\ColonyRepositoryInterface;
31
use Stu\Orm\Repository\ModuleQueueRepositoryInterface;
32
use Stu\Orm\Repository\PlanetFieldRepositoryInterface;
33
use Stu\Orm\Repository\ResearchedRepositoryInterface;
34
use Stu\Orm\Repository\ShipRepositoryInterface;
35
use Stu\Orm\Repository\ShipRumpUserRepositoryInterface;
36
37
final class ColonyTick implements ColonyTickInterface
38
{
39
    private ResearchedRepositoryInterface $researchedRepository;
40
41
    private ShipRumpUserRepositoryInterface $shipRumpUserRepository;
42
43
    private ModuleQueueRepositoryInterface $moduleQueueRepository;
44
45
    private PlanetFieldRepositoryInterface $planetFieldRepository;
46
47
    private PrivateMessageSenderInterface $privateMessageSender;
48
49
    private ColonyStorageManagerInterface $colonyStorageManager;
50
51
    private ColonyRepositoryInterface $colonyRepository;
52
53
    private CreateDatabaseEntryInterface $createDatabaseEntry;
54
55
    private BuildingManagerInterface $buildingManager;
56
57
    private CrewCreatorInterface $crewCreator;
58
59
    private ShipCreatorInterface $shipCreator;
60
61
    private ShipRepositoryInterface $shipRepository;
62
63
    private ShipSystemManagerInterface $shipSystemManager;
64
65
    private CreateUserAwardInterface $createUserAward;
66
67
    private ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository;
68
69
    private EntityManagerInterface $entityManager;
70
71
    private LoggerUtilInterface $loggerUtil;
72
73
    private ColonyLibFactoryInterface $colonyLibFactory;
74
75
    private ColonyFunctionManagerInterface $colonyFunctionManager;
76
77
    private array $commodityArray;
78
79
    private array $msg = [];
80
81
    public function __construct(
82
        ResearchedRepositoryInterface $researchedRepository,
83
        ShipRumpUserRepositoryInterface $shipRumpUserRepository,
84
        ModuleQueueRepositoryInterface $moduleQueueRepository,
85
        PlanetFieldRepositoryInterface $planetFieldRepository,
86
        PrivateMessageSenderInterface $privateMessageSender,
87
        ColonyStorageManagerInterface $colonyStorageManager,
88
        ColonyRepositoryInterface $colonyRepository,
89
        CreateDatabaseEntryInterface $createDatabaseEntry,
90
        BuildingManagerInterface $buildingManager,
91
        CrewCreatorInterface $crewCreator,
92
        ShipCreatorInterface $shipCreator,
93
        ShipRepositoryInterface $shipRepository,
94
        ShipSystemManagerInterface $shipSystemManager,
95
        CreateUserAwardInterface $createUserAward,
96
        ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository,
97
        EntityManagerInterface $entityManager,
98
        ColonyLibFactoryInterface $colonyLibFactory,
99
        ColonyFunctionManagerInterface $colonyFunctionManager,
100
        LoggerUtilFactoryInterface $loggerUtilFactory
101
    ) {
102
        $this->researchedRepository = $researchedRepository;
103
        $this->shipRumpUserRepository = $shipRumpUserRepository;
104
        $this->moduleQueueRepository = $moduleQueueRepository;
105
        $this->planetFieldRepository = $planetFieldRepository;
106
        $this->privateMessageSender = $privateMessageSender;
107
        $this->colonyStorageManager = $colonyStorageManager;
108
        $this->colonyRepository = $colonyRepository;
109
        $this->createDatabaseEntry = $createDatabaseEntry;
110
        $this->buildingManager = $buildingManager;
111
        $this->crewCreator = $crewCreator;
112
        $this->shipCreator = $shipCreator;
113
        $this->shipRepository = $shipRepository;
114
        $this->shipSystemManager = $shipSystemManager;
115
        $this->createUserAward = $createUserAward;
116
        $this->colonyDepositMiningRepository = $colonyDepositMiningRepository;
117
        $this->entityManager = $entityManager;
118
        $this->loggerUtil = $loggerUtilFactory->getLoggerUtil();
119
        $this->colonyLibFactory = $colonyLibFactory;
120
        $this->colonyFunctionManager = $colonyFunctionManager;
121
    }
122
123
    public function work(ColonyInterface $colony, array $commodityArray): void
124
    {
125
        $doLog = $this->loggerUtil->doLog();
126
        if ($doLog) {
127
            $startTime = microtime(true);
128
        }
129
130
        $this->commodityArray = $commodityArray;
131
132
        $userDepositMinings = $colony->getUserDepositMinings();
133
134
        $this->mainLoop($colony, $userDepositMinings);
135
136
        $this->colonyRepository->save($colony);
137
138
        $this->proceedModules($colony);
139
        $this->sendMessages($colony);
140
141
        if ($doLog) {
142
            $endTime = microtime(true);
143
            $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...
144
        }
145
    }
146
147
    /**
148
     * @param ColonyDepositMiningInterface[] $userDepositMinings
149
     */
150
    private function mainLoop(ColonyInterface $colony, array $userDepositMinings)
151
    {
152
        $doLog = $this->loggerUtil->doLog();
153
154
        if ($doLog) {
155
            $startTime = microtime(true);
156
        }
157
158
        $i = 1;
159
        $storage = $colony->getStorage();
160
161
        $production = $this->colonyLibFactory->createColonyCommodityProduction($colony)->getProduction();
162
163
        while (true) {
164
            $rewind = 0;
165
            foreach ($production as $commodityId => $pro) {
166
                if ($pro->getProduction() >= 0) {
167
                    continue;
168
                }
169
170
                $depositMining = $userDepositMinings[$commodityId] ?? null;
171
                if ($depositMining !== null) {
172
                    if ($depositMining->isEnoughLeft((int) abs($pro->getProduction()))) {
173
                        continue;
174
                    }
175
                }
176
177
                $storageItem = $storage[$pro->getCommodityId()] ?? null;
178
                if ($storageItem !== null && $storageItem->getAmount() + $pro->getProduction() >= 0) {
179
                    continue;
180
                }
181
                //echo "coloId:" . $colony->getId() . ", production:" . $pro->getProduction() . ", commodityId:" . $commodityId . ", commodity:" . $this->commodityArray[$commodityId]->getName() . "\n";
182
                $field = $this->getBuildingToDeactivateByCommodity($colony, $commodityId);
183
                $name = '';
184
                // echo $i." hit by commodity ".$field->getFieldId()." - produce ".$pro->getProduction()." MT ".microtime()."\n";
185
                $this->deactivateBuilding($field, $production, $this->commodityArray[$commodityId], $name);
186
                $rewind = 1;
187
            }
188
189
            if ($rewind == 0 && $colony->getWorkers() > $colony->getMaxBev()) {
190
                $field = $this->getBuildingToDeactivateByLivingSpace($colony);
191
                $name = 'Wohnraum';
192
                $this->deactivateBuilding($field, $production, null, $name);
193
                $rewind = 1;
194
            }
195
196
            $energyProduction = $this->planetFieldRepository->getEnergyProductionByColony($colony->getId());
197
198
            if ($rewind == 0 && $energyProduction < 0 && $colony->getEps() + $energyProduction < 0) {
199
                $field = $this->getBuildingToDeactivateByEpsUsage($colony);
200
                $name = 'Energie';
201
                //echo $i . " hit by eps " . $field->getFieldId() . " - complete usage " . $colony->getEpsProduction() . " - usage " . $field->getBuilding()->getEpsProduction() . " MT " . microtime() . "\n";
202
                $this->deactivateBuilding($field, $production, null, $name);
203
                $rewind = 1;
204
            }
205
            if ($rewind == 1) {
206
                $i++;
207
                if ($i == 100) {
208
                    // SECURITY
209
                    //echo "HIT SECURITY BREAK\n";
210
                    break;
211
                }
212
                continue;
213
            }
214
            break;
215
        }
216
        $colony->setEps(
217
            min(
218
                $colony->getMaxEps(),
219
                $colony->getEps() + $this->planetFieldRepository->getEnergyProductionByColony($colony->getId())
220
            )
221
        );
222
223
        if ($doLog) {
224
            $endTime = microtime(true);
225
            $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...
226
        }
227
228
        $this->proceedStorage($colony, $userDepositMinings, $production);
229
    }
230
231
    /**
232
     * @param array<ColonyProduction> $production
233
     */
234
    private function deactivateBuilding(
235
        PlanetFieldInterface $field,
236
        array &$production,
237
        CommodityInterface $commodity = null,
238
        string $name
239
    ): void {
240
        if ($name != '') {
241
            $ext = $name;
242
        } else {
243
            $ext = $commodity->getName();
0 ignored issues
show
Bug introduced by
The method getName() does not exist on null. ( Ignorable by Annotation )

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

243
            /** @scrutinizer ignore-call */ 
244
            $ext = $commodity->getName();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
244
        }
245
        $building = $field->getBuilding();
246
247
        $this->buildingManager->deactivate($field);
248
        $this->entityManager->flush();
249
250
        $this->mergeProduction($building->getCommodities(), $production);
251
252
        $this->msg[] = $building->getName() . " auf Feld " . $field->getFieldId() . " deaktiviert (Mangel an " . $ext . ")";
253
    }
254
255
    private function getBuildingToDeactivateByCommodity(ColonyInterface $colony, int $commodityId): PlanetFieldInterface
256
    {
257
        $fields = $this->planetFieldRepository->getCommodityConsumingByColonyAndCommodity(
258
            $colony->getId(),
259
            $commodityId,
260
            [1]
261
        );
262
263
        return 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

263
        return current(/** @scrutinizer ignore-type */ $fields);
Loading history...
264
    }
265
266
    private function getBuildingToDeactivateByEpsUsage(ColonyInterface $colony): PlanetFieldInterface
267
    {
268
        $fields = $this->planetFieldRepository->getEnergyConsumingByColony($colony->getId(), [1], 1);
269
270
        return 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

270
        return current(/** @scrutinizer ignore-type */ $fields);
Loading history...
271
    }
272
273
    private function getBuildingToDeactivateByLivingSpace(ColonyInterface $colony): PlanetFieldInterface
274
    {
275
        $fields = $this->planetFieldRepository->getWorkerConsumingByColonyAndState($colony->getId(), [1], 1);
276
277
        return 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

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

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