ColonyTick   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 327
Duplicated Lines 0 %

Test Coverage

Coverage 32.05%

Importance

Changes 0
Metric Value
eloc 130
dl 0
loc 327
ccs 50
cts 156
cp 0.3205
rs 9.2
c 0
b 0
f 0
wmc 40

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getBuildingToDeactivateByLivingSpace() 0 12 2
A mergeProduction() 0 16 4
A deactivateBuilding() 0 21 3
A getBuildingToDeactivateByEpsUsage() 0 17 2
A getBuildingToDeactivateByCommodity() 0 19 2
A mainLoop() 0 37 5
B checkStorage() 0 35 7
A __construct() 0 14 1
A work() 0 11 1
A proceedModules() 0 26 5
A checkEnergyProduction() 0 17 3
A checkLivingSpace() 0 16 3
A sendMessages() 0 21 2

How to fix   Complexity   

Complex Class

Complex classes like ColonyTick often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ColonyTick, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Stu\Module\Tick\Colony;
4
5
use Doctrine\Common\Collections\Collection;
6
use InvalidArgumentException;
7
use RuntimeException;
8
use Stu\Component\Building\BuildingFunctionEnum;
9
use Stu\Component\Building\BuildingManagerInterface;
10
use Stu\Component\Colony\ColonyFunctionManagerInterface;
11
use Stu\Lib\Transfer\Storage\StorageManagerInterface;
12
use Stu\Lib\ColonyProduction\ColonyProduction;
13
use Stu\Lib\Information\InformationFactoryInterface;
14
use Stu\Lib\Information\InformationWrapper;
15
use Stu\Module\Colony\Lib\ColonyLibFactoryInterface;
16
use Stu\Module\Commodity\Lib\CommodityCacheInterface;
17
use Stu\Module\Message\Lib\PrivateMessageFolderTypeEnum;
18
use Stu\Module\Message\Lib\PrivateMessageSenderInterface;
19
use Stu\Module\PlayerSetting\Lib\UserConstants;
0 ignored issues
show
Bug introduced by
The type Stu\Module\PlayerSetting\Lib\UserConstants 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...
20
use Stu\Module\Tick\Colony\Component\ColonyTickComponentInterface;
21
use Stu\Orm\Entity\BuildingCommodity;
22
use Stu\Orm\Entity\Colony;
23
use Stu\Orm\Entity\Commodity;
24
use Stu\Orm\Entity\PlanetField;
25
use Stu\Orm\Repository\ColonyDepositMiningRepositoryInterface;
26
use Stu\Orm\Repository\ColonyRepositoryInterface;
27
use Stu\Orm\Repository\ModuleQueueRepositoryInterface;
28
use Stu\Orm\Repository\PlanetFieldRepositoryInterface;
29
30
final class ColonyTick implements ColonyTickInterface
31
{
32
    private InformationWrapper $information;
33
34
    /** @param array<ColonyTickComponentInterface> $components */
35 1
    public function __construct(
36
        private readonly ModuleQueueRepositoryInterface $moduleQueueRepository,
37
        private readonly PlanetFieldRepositoryInterface $planetFieldRepository,
38
        private readonly ColonyRepositoryInterface $colonyRepository,
39
        private readonly ColonyDepositMiningRepositoryInterface $colonyDepositMiningRepository,
40
        private readonly PrivateMessageSenderInterface $privateMessageSender,
41
        private readonly StorageManagerInterface $storageManager,
42
        private readonly BuildingManagerInterface $buildingManager,
43
        private readonly ColonyLibFactoryInterface $colonyLibFactory,
44
        private readonly ColonyFunctionManagerInterface $colonyFunctionManager,
45
        private readonly CommodityCacheInterface $commodityCache,
46
        private readonly InformationFactoryInterface $informationFactory,
47
        private readonly array $components
48 1
    ) {}
49
50 1
    #[\Override]
51
    public function work(Colony $colony): void
52
    {
53 1
        $this->information = $this->informationFactory->createInformationWrapper();
54
55 1
        $deactivatedFields = $this->mainLoop($colony);
56
57 1
        $this->colonyRepository->save($colony);
58
59 1
        $this->proceedModules($colony, $deactivatedFields);
60 1
        $this->sendMessages($colony);
61
    }
62
63
    /**
64
     * @return array<int>
65
     */
66 1
    private function mainLoop(Colony $colony): array
67
    {
68 1
        $i = 1;
69 1
        $production = $this->colonyLibFactory->createColonyCommodityProduction($colony)->getProduction();
70
71 1
        $deactivatedFields = [-1];
72
73 1
        while (true) {
74
75 1
            $rewind = $this->checkStorage($colony, $production, $deactivatedFields);
76 1
            $rewind |= $this->checkLivingSpace($colony, $production, $deactivatedFields);
77 1
            $rewind |= $this->checkEnergyProduction($colony, $production, $deactivatedFields);
78
79 1
            if ($rewind !== 0) {
80
                $i++;
81
                if ($i == 100) {
82
                    // SECURITY
83
                    //echo "HIT SECURITY BREAK\n";
84
                    break;
85
                }
86
                continue;
87
            }
88 1
            break;
89
        }
90 1
        $changeable = $colony->getChangeable();
91 1
        $changeable->setEps(
92 1
            min(
93 1
                $colony->getMaxEps(),
94 1
                $changeable->getEps() + $this->planetFieldRepository->getEnergyProductionByHost($colony, $deactivatedFields)
95 1
            )
96 1
        );
97
98 1
        foreach ($this->components as $component) {
99 1
            $component->work($colony, $production, $this->information);
100
        }
101
102 1
        return $deactivatedFields;
103
    }
104
105
    /**
106
     * @param array<int, ColonyProduction> $production
107
     * @param array<int> $deactivatedFields
108
     */
109 1
    private function checkStorage(
110
        Colony $colony,
111
        array &$production,
112
        array &$deactivatedFields
113
    ): bool {
114
115 1
        $result = false;
116
117 1
        foreach ($production as $pro) {
118 1
            if ($pro->getProduction() >= 0) {
119 1
                continue;
120
            }
121
122
            $commodityId = $pro->getCommodityId();
123
124
            $depositMining = $this->colonyDepositMiningRepository->getCurrentUserDepositMinings($colony)[$commodityId] ?? null;
125
            if ($depositMining !== null && $depositMining->isEnoughLeft(abs($pro->getProduction()))) {
0 ignored issues
show
Bug introduced by
It seems like abs($pro->getProduction()) can also be of type double; however, parameter $neededAmount of Stu\Orm\Entity\ColonyDepositMining::isEnoughLeft() 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

125
            if ($depositMining !== null && $depositMining->isEnoughLeft(/** @scrutinizer ignore-type */ abs($pro->getProduction()))) {
Loading history...
126
                continue;
127
            }
128
129
            $storage = $colony->getStorage();
130
            $storageItem = $storage[$commodityId] ?? null;
131
            if ($storageItem !== null && $storageItem->getAmount() + $pro->getProduction() >= 0) {
132
                continue;
133
            }
134
            //echo "coloId:" . $colony->getId() . ", production:" . $pro->getProduction() . ", commodityId:" . $commodityId . ", commodity:" . $this->commodityCache->get($commodityId)->getName() . "\n";
135
            $field = $this->getBuildingToDeactivateByCommodity($colony, $commodityId, $deactivatedFields);
136
            // echo $i." hit by commodity ".$field->getFieldId()." - produce ".$pro->getProduction()." MT ".microtime()."\n";
137
            $this->deactivateBuilding($field, $production, $this->commodityCache->get($commodityId));
138
            $deactivatedFields[] = $field->getFieldId();
139
140
            $result = true;
141
        }
142
143 1
        return $result;
144
    }
145
146
    /**
147
     * @param array<int, ColonyProduction> $production
148
     * @param array<int> $deactivatedFields
149
     */
150 1
    private function checkLivingSpace(
151
        Colony $colony,
152
        array &$production,
153
        array &$deactivatedFields
154
    ): bool {
155 1
        if ($colony->getWorkers() > $colony->getChangeable()->getMaxBev()) {
156
            $field = $this->getBuildingToDeactivateByLivingSpace($colony, $deactivatedFields);
157
            if ($field !== null) {
158
                $this->deactivateBuilding($field, $production, 'Wohnraum');
159
                $deactivatedFields[] = $field->getFieldId();
160
161
                return true;
162
            }
163
        }
164
165 1
        return false;
166
    }
167
168
    /**
169
     * @param array<int, ColonyProduction> $production
170
     * @param array<int> $deactivatedFields
171
     */
172 1
    private function checkEnergyProduction(
173
        Colony $colony,
174
        array &$production,
175
        array &$deactivatedFields
176
    ): bool {
177 1
        $energyProduction = $this->planetFieldRepository->getEnergyProductionByHost($colony, $deactivatedFields);
178
179 1
        if ($energyProduction < 0 && $colony->getChangeable()->getEps() + $energyProduction < 0) {
180
            $field = $this->getBuildingToDeactivateByEpsUsage($colony, $deactivatedFields);
181
            //echo $i . " hit by eps " . $field->getFieldId() . " - complete usage " . $colony->getEpsProduction() . " - usage " . $field->getBuilding()->getEpsProduction() . " MT " . microtime() . "\n";
182
            $this->deactivateBuilding($field, $production, 'Energie');
183
            $deactivatedFields[] = $field->getFieldId();
184
185
            return true;
186
        }
187
188 1
        return false;
189
    }
190
191
    /**
192
     * @param array<int, ColonyProduction> $production
193
     */
194
    private function deactivateBuilding(
195
        PlanetField $field,
196
        array &$production,
197
        Commodity|string $cause
198
    ): void {
199
        $ext = $cause instanceof Commodity ? $cause->getName() : $cause;
200
        $building = $field->getBuilding();
201
202
        if ($building === null) {
203
            throw new InvalidArgumentException('can not deactivate field without building');
204
        }
205
206
        $this->buildingManager->deactivate($field);
207
208
        $this->mergeProduction($building->getCommodities(), $production);
209
210
        $this->information->addInformationf(
211
            "%s auf Feld %d deaktiviert (Mangel an %s)",
212
            $building->getName(),
213
            $field->getFieldId(),
214
            $ext
215
        );
216
    }
217
218
    /**
219
     * @param array<int> $deactivatedFields
220
     */
221
    private function getBuildingToDeactivateByCommodity(
222
        Colony $colony,
223
        int $commodityId,
224
        array $deactivatedFields
225
    ): PlanetField {
226
        $fields = $this->planetFieldRepository->getCommodityConsumingByHostAndCommodity(
227
            $colony,
228
            $commodityId,
229
            [1],
230
            1,
231
            $deactivatedFields
232
        );
233
234
        $result = current($fields);
235
        if (!$result) {
236
            throw new RuntimeException('no building found');
237
        }
238
239
        return $result;
240
    }
241
242
    /**
243
     * @param array<int> $deactivatedFields
244
     */
245
    private function getBuildingToDeactivateByEpsUsage(
246
        Colony $colony,
247
        array $deactivatedFields
248
    ): PlanetField {
249
        $fields = $this->planetFieldRepository->getEnergyConsumingByHost(
250
            $colony,
251
            [1],
252
            1,
253
            $deactivatedFields
254
        );
255
256
        $result = current($fields);
257
        if (!$result) {
258
            throw new RuntimeException('no building found');
259
        }
260
261
        return $result;
262
    }
263
264
    /**
265
     * @param array<int> $deactivatedFields
266
     */
267
    private function getBuildingToDeactivateByLivingSpace(
268
        Colony $colony,
269
        array $deactivatedFields
270
    ): ?PlanetField {
271
        $fields = $this->planetFieldRepository->getWorkerConsumingByColonyAndState(
272
            $colony->getId(),
273
            [1],
274
            1,
275
            $deactivatedFields
276
        );
277
278
        return $fields === [] ? null : current($fields);
279
    }
280
281
    /**
282
     * @param array<int> $deactivatedFields
283
     */
284 1
    private function proceedModules(Colony $colony, array $deactivatedFields): void
285
    {
286 1
        foreach ($this->moduleQueueRepository->getByColony($colony->getId()) as $queue) {
287 1
            $buildingFunction = $queue->getBuildingFunction();
288
289
            //spare parts and system components are generated by spacecraft tick manager, to avoid dead locks
290
            if (
291 1
                $buildingFunction === BuildingFunctionEnum::FABRICATION_HALL ||
292 1
                $buildingFunction === BuildingFunctionEnum::TECH_CENTER
293
            ) {
294
                continue;
295
            }
296
297 1
            if ($this->colonyFunctionManager->hasActiveFunction($colony, $buildingFunction, false, $deactivatedFields)) {
298
                $this->storageManager->upperStorage(
299
                    $colony,
300
                    $queue->getModule()->getCommodity(),
301
                    $queue->getAmount()
302
                );
303
304
                $this->information->addInformationf(
305
                    _('Es wurden %d %s hergestellt'),
306
                    $queue->getAmount(),
307
                    $queue->getModule()->getName()
308
                );
309
                $this->moduleQueueRepository->delete($queue);
310
            }
311
        }
312
    }
313
314 1
    private function sendMessages(Colony $colony): void
315
    {
316 1
        if ($this->information->isEmpty()) {
317 1
            return;
318
        }
319
320
        $text = sprintf(
321
            "Tickreport der Kolonie %s\n%s",
322
            $colony->getName(),
323
            $this->information->getInformationsAsString()
324
        );
325
326
        $this->privateMessageSender->send(
327
            UserConstants::USER_NOONE,
328
            $colony->getUserId(),
329
            $text,
330
            PrivateMessageFolderTypeEnum::SPECIAL_COLONY,
331
            $colony
332
        );
333
334
        $this->information = $this->informationFactory->createInformationWrapper();
335
    }
336
337
    /**
338
     * @param Collection<int, BuildingCommodity> $buildingProduction
339
     * @param array<int, ColonyProduction> $production
340
     */
341
    private function mergeProduction(
342
        Collection $buildingProduction,
343
        array &$production
344
    ): void {
345
        foreach ($buildingProduction as $obj) {
346
            $commodityId = $obj->getCommodityId();
347
            if (!array_key_exists($commodityId, $production)) {
348
                $data = $this->colonyLibFactory->createColonyProduction(
349
                    $obj->getCommodity(),
350
                    $obj->getAmount() * -1
351
                );
352
                $production[$commodityId] = $data;
353
            } elseif ($obj->getAmount() < 0) {
354
                $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

354
                $production[$commodityId]->upperProduction(/** @scrutinizer ignore-type */ abs($obj->getAmount()));
Loading history...
355
            } else {
356
                $production[$commodityId]->lowerProduction($obj->getAmount());
357
            }
358
        }
359
    }
360
}
361