Lines 100.00% 40 / 40
Functions and Methods 100.00% 3 / 3
Classes and Traits 100.00% 1 / 1
Name Lines Functions and Methods CRAP Classes and Traits
PlayerDataLoadedEvent 100.00% 40 / 40 100.00% 3 / 3 7 100.00% 1 / 1
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 from 100.00% 37 / 37 100.00% 1 / 1 5
 summary 100.00% 2 / 2 100.00% 1 / 1 1
1<?php
2
3declare(strict_types=1);
4
5namespace Lorekeeper\Server\Event\Player;
6
7use Lorekeeper\Core\Currency\AdventureCoins;
8use Lorekeeper\Core\Currency\Gold;
9use Lorekeeper\Core\Player\PlayerLevel;
10use Lorekeeper\Core\Player\PlayerLevelProgress;
11use Lorekeeper\Core\Player\PlayerName;
12use Lorekeeper\Core\Player\PlayerQuestMarks;
13use Lorekeeper\Core\Player\Wallet;
14use Lorekeeper\Core\Progression\Experience;
15use Lorekeeper\Core\Support\Gauge;
16use Lorekeeper\Core\Support\Quantity;
17use Lorekeeper\Server\Event\Event;
18use Lorekeeper\Server\Event\NamedAfterClass;
19use Lorekeeper\Server\Event\Quest\Helper\QuestMarks;
20use Lorekeeper\Server\Exception\MalformedEventException;
21use Lorekeeper\Server\Message\JsonMessage;
22use Lorekeeper\Server\Message\Message;
23use Lorekeeper\Server\SocketId;
24use Override;
25use Psl\Option;
26use Psl\Type;
27use Psl\Type\Exception\CoercionException;
28
29final readonly class PlayerDataLoadedEvent implements Event
30{
31    use NamedAfterClass;
32
33    /** @param non-empty-list<PlayerData> $players */
34    public function __construct(
35        public array $players,
36    ) {}
37
38    /** @return Option\Option<static> */
39    #[Override]
40    public static function from(Message $message): Option\Option
41    {
42        if (!$message instanceof JsonMessage || ($message->data['cmd'] ?? null) !== 'initUserDatas') {
43            return Option\none();
44        }
45
46        try {
47            $entries = Type\non_empty_vec(Type\shape([
48                'uid' => Type\positive_int(),
49                'data' => Type\shape([
50                    'strUsername' => Type\non_empty_string(),
51                    'intLevel' => Type\int_range(min: 1, max: 100),
52                    'intExp' => Type\uint(),
53                    'intExpToLevel' => Type\uint(),
54                    'intGold' => Type\uint(),
55                    'intCoins' => Type\uint(),
56                    'iUpg' => Type\int(),
57                    'iBagSlots' => Type\positive_int(),
58                ], allowUnknownFields: true),
59            ], allowUnknownFields: true))
60                ->coerce($message->data['a'] ?? null);
61
62            $players = [];
63            foreach ($entries as $entry) {
64                $data = $entry['data'];
65                $players[] = new PlayerData(
66                    new SocketId($entry['uid']),
67                    new PlayerName($data['strUsername']),
68                    new PlayerLevelProgress(
69                        new PlayerLevel($data['intLevel']),
70                        new Gauge(
71                            new Experience($data['intExp']),
72                            new Experience(max($data['intExp'], $data['intExpToLevel'])),
73                        ),
74                    ),
75                    new Wallet(new Gold($data['intGold']), new AdventureCoins($data['intCoins'])),
76                    $data['iUpg'] > 0,
77                    new Quantity($data['iBagSlots']),
78                    new PlayerQuestMarks(QuestMarks::held($data)),
79                );
80            }
81        } catch (CoercionException $exception) {
82            throw new MalformedEventException(static::class, 'player data is missing or invalid', $exception);
83        }
84
85        return Option\some(new static($players));
86    }
87
88    #[Override]
89    public function summary(): string
90    {
91        return 'Data of '
92        . implode(', ', array_map(static fn(PlayerData $player): string => $player->name->value, $this->players));
93    }
94}