From 8073fa0dfd75260ad7164ae3aa616bcb9a75a2cf Mon Sep 17 00:00:00 2001 From: Nik497926 <2000-nikitos@bk.ru> Date: Fri, 8 Aug 2025 21:25:19 +0300 Subject: [PATCH] Add skin type detection and update functionality - Implemented `isSkinSlim` method to determine if a skin is slim based on transparency and color checks. - Added `getUserSkinType` method to retrieve and update the skin type for a user, including database checks. - Introduced `updateUserSkinType` method to save the skin type in the database if it doesn't exist or update it if it does. - Included necessary error handling for database operations. --- .../2024_03_20_create_skin_types_table.php | 23 +++ src/Models/SkinType.php | 23 +++ src/Resources/SkinResource.php | 19 +++ src/SkinAPI.php | 145 ++++++++++++++++++ 4 files changed, 210 insertions(+) create mode 100644 database/migrations/2024_03_20_create_skin_types_table.php create mode 100644 src/Models/SkinType.php create mode 100644 src/Resources/SkinResource.php diff --git a/database/migrations/2024_03_20_create_skin_types_table.php b/database/migrations/2024_03_20_create_skin_types_table.php new file mode 100644 index 0000000..c8b5542 --- /dev/null +++ b/database/migrations/2024_03_20_create_skin_types_table.php @@ -0,0 +1,23 @@ +id(); + $table->foreignId('user_id')->unique()->constrained()->onDelete('cascade'); + $table->boolean('is_slim')->default(false); + $table->timestamps(); + }); + } + + public function down(): void + { + Schema::dropIfExists('skin_types'); + } +}; diff --git a/src/Models/SkinType.php b/src/Models/SkinType.php new file mode 100644 index 0000000..033aea8 --- /dev/null +++ b/src/Models/SkinType.php @@ -0,0 +1,23 @@ + 'boolean' + ]; + + public function user() + { + return $this->belongsTo(User::class); + } +} diff --git a/src/Resources/SkinResource.php b/src/Resources/SkinResource.php new file mode 100644 index 0000000..e3a9c38 --- /dev/null +++ b/src/Resources/SkinResource.php @@ -0,0 +1,19 @@ +exists("skins/{$this->id}.png"); + + return [ + 'slim' => $skinExists ? SkinAPI::getUserSkinType($this->id) : false + ]; + } +} diff --git a/src/SkinAPI.php b/src/SkinAPI.php index 10ad22c..901cd14 100644 --- a/src/SkinAPI.php +++ b/src/SkinAPI.php @@ -4,6 +4,7 @@ use Azuriom\Plugin\SkinApi\Render\RenderType; use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Facades\Schema; use Illuminate\Validation\Rule; class SkinAPI @@ -120,4 +121,148 @@ public static function makeAvatarWithTypeForUser(RenderType $type, string $user) imagepng($image, Storage::disk('public')->path("{$type->value}/{$user}.png")); } + + /** + * Check if the skin is slim by checking specific areas for transparency, black or white pixels + * + * @param string $skinPath Full path to the skin file + * @return bool true if slim, false if default + */ + public static function isSkinSlim(string $skinPath): bool + { + if (!file_exists($skinPath)) { + return false; + } + + // Load the skin image + $skin = @imagecreatefrompng($skinPath); + if (!$skin) { + return false; + } + + // Get image dimensions + $width = imagesx($skin); + $scale = $width / 64; // Compute scale like in JS + + // Helper functions to check areas + $hasTransparency = function($x, $y, $w, $h) use ($skin, $scale) { + for ($px = $x * $scale; $px < ($x + $w) * $scale; $px++) { + for ($py = $y * $scale; $py < ($y + $h) * $scale; $py++) { + $color = imagecolorat($skin, $px, $py); + $alpha = ($color >> 24) & 0x7F; + if ($alpha == 127) { // Fully transparent + return true; + } + } + } + return false; + }; + + $isAreaColor = function($x, $y, $w, $h, $targetR, $targetG, $targetB) use ($skin, $scale) { + for ($px = $x * $scale; $px < ($x + $w) * $scale; $px++) { + for ($py = $y * $scale; $py < ($y + $h) * $scale; $py++) { + $color = imagecolorat($skin, $px, $py); + $r = ($color >> 16) & 0xFF; + $g = ($color >> 8) & 0xFF; + $b = $color & 0xFF; + if ($r !== $targetR || $g !== $targetG || $b !== $targetB) { + return false; + } + } + } + return true; + }; + + $isAreaBlack = function($x, $y, $w, $h) use ($isAreaColor) { + return $isAreaColor($x, $y, $w, $h, 0, 0, 0); + }; + + $isAreaWhite = function($x, $y, $w, $h) use ($isAreaColor) { + return $isAreaColor($x, $y, $w, $h, 255, 255, 255); + }; + + // Check exactly the same coordinates and areas as in JavaScript + $isSlim = ( + $hasTransparency(50, 16, 2, 4) || + $hasTransparency(54, 20, 2, 12) || + $hasTransparency(42, 48, 2, 4) || + $hasTransparency(46, 52, 2, 12) + ) || ( + $isAreaBlack(50, 16, 2, 4) && + $isAreaBlack(54, 20, 2, 12) && + $isAreaBlack(42, 48, 2, 4) && + $isAreaBlack(46, 52, 2, 12) + ) || ( + $isAreaWhite(50, 16, 2, 4) && + $isAreaWhite(54, 20, 2, 12) && + $isAreaWhite(42, 48, 2, 4) && + $isAreaWhite(46, 52, 2, 12) + ); + + imagedestroy($skin); + return $isSlim; + } + + /** + * Get the skin type for a user + * + * @param int $userId + * @return bool true if slim, false if default + */ + public static function getUserSkinType(int $userId): bool + { + try { + if (!Schema::hasTable('skin_types')) { + return false; + } + + $skinType = \Azuriom\Plugin\SkinApi\Models\SkinType::firstWhere('user_id', $userId); + + if ($skinType === null) { + $skinPath = Storage::disk('public')->path("skins/{$userId}.png"); + + if (!file_exists($skinPath)) { + return false; + } + + $isSlim = self::isSkinSlim($skinPath); + self::updateUserSkinType($userId, $isSlim); + return $isSlim; + } + + return $skinType->is_slim; + } catch (\Exception $e) { + return false; + } + } + + /** + * Update the skin type for a user + * + * @param int $userId + * @param bool $isSlim + * @return void + */ + public static function updateUserSkinType(int $userId, bool $isSlim): void + { + try { + if (!Schema::hasTable('skin_types')) { + throw new \Exception('Table skin_types does not exist'); + } + + $existing = \Azuriom\Plugin\SkinApi\Models\SkinType::where('user_id', $userId)->first(); + + if ($existing) { + $existing->is_slim = $isSlim; + $existing->save(); + } else { + $new = new \Azuriom\Plugin\SkinApi\Models\SkinType(); + $new->user_id = $userId; + $new->is_slim = $isSlim; + $new->save(); + } + } catch (\Exception $e) { + throw $e; + } + } }