• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattiabasone / minepic / 4096419641

pending completion
4096419641

push

github

Mattia Basone
mockery

744 of 864 relevant lines covered (86.11%)

4.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

61.29
/app/Resolvers/UuidResolver.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Minepic\Resolvers;
6

7
use Illuminate\Contracts\Events\Dispatcher;
8
use Log;
9
use Minepic\Cache\UserNotFoundCache;
10
use Minepic\Events\Account\AccountCreatedEvent;
11
use Minepic\Events\Account\UsernameChangeEvent;
12
use Minepic\Helpers\Storage\Files\SkinsStorage;
13
use Minepic\Minecraft\MojangAccount;
14
use Minepic\Minecraft\MojangClient;
15
use Minepic\Models\Account;
16

17
class UuidResolver
18
{
19
    /**
20
     * Requested string.
21
     *
22
     * @var string
23
     */
24
    private string $request;
25
    /**
26
     * @var null|string
27
     */
28
    private ?string $uuid = null;
29
    /**
30
     * Userdata from/to DB.
31
     *
32
     * @var null|Account
33
     */
34
    private ?Account $account;
35
    /**
36
     * User data has been updated?
37
     *
38
     * @var bool
39
     */
40
    private bool $dataUpdated = false;
41
    /**
42
     * Set force update.
43
     *
44
     * @var bool
45
     */
46
    private bool $forceUpdate = false;
47

48
    /**
49
     * @param MojangClient $mojangClient    Client for Mojang API
50
     * @param Dispatcher   $eventDispatcher
51
     */
52
    public function __construct(
53
        private MojangClient $mojangClient,
54
        private Dispatcher $eventDispatcher
55
    ) {
56
    }
24✔
57

58
    /**
59
     * @return null|string
60
     */
61
    public function getUuid(): ?string
62
    {
63
        return $this->uuid;
9✔
64
    }
65

66
    /**
67
     * Return loaded user data.
68
     *
69
     * @return Account
70
     */
71
    public function getAccount(): Account
72
    {
73
        return $this->account ?? new Account();
12✔
74
    }
75

76
    /**
77
     * Insert user data in database.
78
     **
79
     * @throws \Throwable
80
     *
81
     * @return bool
82
     */
83
    public function insertNewUuid(): bool
84
    {
85
        if ($this->request === '' || UserNotFoundCache::has($this->request)) {
6✔
86
            return false;
3✔
87
        }
88

89
        $mojangAccount = $this->getFullUserdataApi();
4✔
90
        if ($mojangAccount instanceof MojangAccount) {
4✔
91
            $this->account = Account::create([
2✔
92
                'username' => $mojangAccount->getUsername(),
2✔
93
                'uuid' => $mojangAccount->getUuid(),
2✔
94
                'skin' => $mojangAccount->getSkin(),
2✔
95
                'cape' => $mojangAccount->getCape(),
2✔
96
            ]);
2✔
97

98
            $this->saveRemoteSkin();
2✔
99

100
            $this->uuid = $this->account->uuid;
2✔
101
            $this->eventDispatcher->dispatch(new AccountCreatedEvent($this->account));
2✔
102

103
            return true;
2✔
104
        }
105

106
        UserNotFoundCache::add($this->request);
2✔
107

108
        return false;
2✔
109
    }
110

111
    /**
112
     * Check requested string and initialize objects.
113
     *
114
     * @param null|string $uuid
115
     *
116
     * @throws \Throwable
117
     * @return bool
118
     */
119
    public function resolve(?string $uuid): bool
120
    {
121
        $this->dataUpdated = false;
15✔
122
        $this->request = $uuid ?? '';
15✔
123

124
        if ($uuid === null) {
15✔
125
            Log::debug('UUID is null');
×
126

127
            return false;
×
128
        }
129

130
        if ($this->initializeUuidRequest()) {
15✔
131
            return true;
11✔
132
        }
133

134
        $this->setFailedRequest('Account not found');
4✔
135

136
        return false;
4✔
137
    }
138

139
    /**
140
     * Return if data has been updated.
141
     */
142
    public function userDataUpdated(): bool
143
    {
144
        return $this->dataUpdated;
×
145
    }
146

147
    /**
148
     * Save skin image.
149
     *
150
     * @throws \Throwable
151
     *
152
     * @return bool
153
     */
154
    public function saveRemoteSkin(): bool
155
    {
156
        if ($this->account instanceof Account === false) {
3✔
157
            return false;
×
158
        }
159

160
        if (!empty($this->account->skin) && $this->account->skin !== '') {
3✔
161
            try {
162
                $skinData = $this->mojangClient->getSkin($this->account->skin);
3✔
163

164
                return SkinsStorage::save($this->account->uuid, $skinData);
3✔
165
            } catch (\Exception $e) {
×
166
                Log::error($e->getTraceAsString());
×
167
            }
168
        }
169

170
        return SkinsStorage::copyAsSteve($this->account->uuid);
×
171
    }
172

173
    /**
174
     * Set force update.
175
     *
176
     * @param bool $forceUpdate
177
     */
178
    public function setForceUpdate(bool $forceUpdate): void
179
    {
180
        $this->forceUpdate = $forceUpdate;
1✔
181
    }
182

183
    /**
184
     * Check if cache is still valid.
185
     *
186
     * @return bool
187
     */
188
    private function checkDbCache(): bool
189
    {
190
        $accountUpdatedAtTimestamp = (int) ($this->account->updated_at->timestamp ?? 0);
9✔
191

192
        return (time() - $accountUpdatedAtTimestamp) < (int) env('USERDATA_CACHE_TIME');
9✔
193
    }
194

195
    /**
196
     * Check if an UUID is in the database.
197
     *
198
     * @return bool Returns true/false
199
     */
200
    private function requestedUuidInDb(): bool
201
    {
202
        $this->account = Account::query()
15✔
203
            ->whereUuid($this->request)
15✔
204
            ->first();
15✔
205

206
        if ($this->account === null) {
15✔
207
            return false;
6✔
208
        }
209

210
        $this->uuid = $this->account->uuid;
9✔
211

212
        return true;
9✔
213
    }
214

215
    /**
216
     * Update current user fail count.
217
     */
218
    private function updateUserFailUpdate(): bool
219
    {
220
        if (isset($this->account->uuid)) {
×
221
            ++$this->account->fail_count;
×
222

223
            return $this->account->save();
×
224
        }
225

226
        return false;
×
227
    }
228

229
    /**
230
     * Update db user data.
231
     */
232
    private function updateDbUser(): bool
233
    {
234
        if (isset($this->account->username) && $this->account->uuid !== '') {
×
235
            // Get data from API
236
            $mojangAccount = $this->getFullUserdataApi();
×
237
            if ($mojangAccount instanceof MojangAccount) {
×
238
                $previousUsername = $this->account->username;
×
239
                // Update database
240
                $this->account->username = $mojangAccount->getUsername();
×
241
                $this->account->skin = $mojangAccount->getSkin() ?? '';
×
242
                $this->account->cape = $mojangAccount->getCape() ?? '';
×
243
                $this->account->fail_count = 0;
×
244
                $this->account->save();
×
245

246
                $this->account->refresh();
×
247

248
                // Update skin
249
                $this->saveRemoteSkin();
×
250
                $this->logUsernameChange($this->account, $previousUsername);
×
251

252
                $this->dataUpdated = true;
×
253

254
                return true;
×
255
            }
256

257
            $this->updateUserFailUpdate();
×
258

259
            if (!SkinsStorage::exists($this->account->uuid)) {
×
260
                SkinsStorage::copyAsSteve($this->account->uuid);
×
261
            }
262
        }
263
        $this->dataUpdated = false;
×
264

265
        return false;
×
266
    }
267

268
    /**
269
     * Log the username change.
270
     *
271
     * @param Account $account User Account
272
     * @param string $previousUsername Previous username
273
     */
274
    private function logUsernameChange(Account $account, string $previousUsername): void
275
    {
276
        if ($account->username !== $previousUsername && $previousUsername !== '') {
×
277
            $this->eventDispatcher->dispatch(
×
278
                new UsernameChangeEvent($account->uuid, $previousUsername, $account->username)
×
279
            );
×
280
        }
281
    }
282

283
    /**
284
     * Get userdata from Mojang/Minecraft API.
285
     *
286
     * @return null|MojangAccount
287
     */
288
    private function getFullUserdataApi(): ?MojangAccount
289
    {
290
        try {
291
            return $this->mojangClient->getUuidInfo($this->request);
4✔
292
        } catch (\Throwable $e) {
2✔
293
            Log::error($e->getTraceAsString(), ['request' => $this->request]);
2✔
294

295
            return null;
2✔
296
        }
297
    }
298

299
    /**
300
     * Can I exec force update?
301
     */
302
    private function forceUpdatePossible(): bool
303
    {
304
        return ($this->forceUpdate) &&
9✔
305
            ((time() - (int) $this->account->updated_at->timestamp) > (int) env('MIN_USERDATA_UPDATE_INTERVAL'));
9✔
306
    }
307

308
    /**
309
     * @throws \Throwable
310
     * @return bool
311
     */
312
    private function initializeUuidRequest(): bool
313
    {
314
        if ($this->requestedUuidInDb()) {
15✔
315
            // Check if UUID is in my database
316
            // Data cache still valid?
317
            if (!$this->checkDbCache() || $this->forceUpdatePossible()) {
9✔
318
                Log::debug('Refreshing User DB Data');
×
319
                // Nope, updating data
320
                $this->updateDbUser();
×
321
            }
322

323
            if (!SkinsStorage::exists($this->account->uuid)) {
9✔
324
                $this->saveRemoteSkin();
1✔
325
            }
326

327
            return true;
9✔
328
        }
329

330
        if ($this->insertNewUuid()) {
6✔
331
            return true;
2✔
332
        }
333

334
        return false;
4✔
335
    }
336

337
    /**
338
     * Set failed request.
339
     *
340
     * @param string $errorMessage
341
     */
342
    private function setFailedRequest(string $errorMessage = ''): void
343
    {
344
        Log::notice($errorMessage, ['request' => $this->request]);
4✔
345
        $this->account = null;
4✔
346
        $this->request = '';
4✔
347
    }
348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc