diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index cdb2201e..d06280b0 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use Cache; use Redis; use Option; use Carbon\Carbon; @@ -234,7 +235,7 @@ class AdminController extends Controller ->with('forms', compact('general', 'announ', 'meta')); } - public function resource() + public function resource(Request $request) { $resources = Option::form('resources', OptionForm::AUTO_DETECT, function ($form) { $form->checkbox('force_ssl')->label()->hint(); @@ -261,7 +262,7 @@ class AdminController extends Controller ->handle(); $redis = Option::form('redis', 'Redis', function ($form) { - $form->checkbox('enable_redis')->label(); + $form->checkbox('enable_redis')->label()->description(); }); if (option('enable_redis')) { @@ -278,8 +279,30 @@ class AdminController extends Controller $redis->handle(); + $cache = Option::form('cache', OptionForm::AUTO_DETECT, function ($form) { + $form->checkbox('enable_avatar_cache')->label(); + $form->checkbox('enable_preview_cache')->label(); + $form->checkbox('enable_json_cache', 'JSON Profile')->label(); + $form->checkbox('enable_notfound_cache', '404')->label(); + }) + ->type('warning') + ->addButton([ + 'text' => trans('options.cache.clear'), + 'type' => 'a', + 'class' => 'pull-right', + 'style' => 'warning', + 'href' => '?clear-cache', + ]) + ->addMessage(trans('options.cache.driver', ['driver' => config('cache.default')]), 'info'); + + if ($request->has('clear-cache')) { + Cache::flush(); + $cache->addMessage(trans('options.cache.cleared'), 'success'); + } + $cache->handle(); + return view('admin.resource') - ->with('forms', compact('resources', 'redis')); + ->with('forms', compact('resources', 'redis', 'cache')); } public function getUserData(Request $request) diff --git a/app/Http/Controllers/TextureController.php b/app/Http/Controllers/TextureController.php index fbd9491f..e44ecc5b 100644 --- a/app/Http/Controllers/TextureController.php +++ b/app/Http/Controllers/TextureController.php @@ -111,14 +111,7 @@ class TextureController extends Controller return $responses[0]; // @codeCoverageIgnore } else { $png = Minecraft::generateAvatarFromSkin(Storage::disk('textures')->read($t->hash), $size); - - ob_start(); - imagepng($png); - imagedestroy($png); - $image = ob_get_contents(); - ob_end_clean(); - - return Response::png($image); + return Response::png(png($png)); } } } catch (Exception $e) { @@ -168,13 +161,7 @@ class TextureController extends Controller $png = Minecraft::generatePreviewFromSkin($binary, $size, ($t->type == 'alex'), 'both', 4); } - ob_start(); - imagepng($png); - imagedestroy($png); - $image = ob_get_contents(); - ob_end_clean(); - - return Response::png($image); + return Response::png(png($png)); } } } catch (Exception $e) { @@ -216,13 +203,8 @@ class TextureController extends Controller Storage::disk('textures')->read($hash), $size ); - ob_start(); - imagepng($png); - $image = ob_get_contents(); - ob_end_clean(); - imagedestroy($png); - return Response::png($image); + return Response::png(png($png)); } return abort(404); diff --git a/app/Listeners/CacheAvatarPreview.php b/app/Listeners/CacheAvatarPreview.php new file mode 100644 index 00000000..f49d2539 --- /dev/null +++ b/app/Listeners/CacheAvatarPreview.php @@ -0,0 +1,25 @@ +texture; + $size = $event->size; + $key = "avatar-{$texture->tid}-$size"; + + $content = Cache::rememberForever($key, function () use ($texture, $size) { + $res = Storage::disk('textures')->read($texture->hash); + return png(Minecraft::generateAvatarFromSkin($res, $size)); + }); + + return response()->png($content); + } +} diff --git a/app/Listeners/CachePlayerExists.php b/app/Listeners/CachePlayerExists.php new file mode 100644 index 00000000..f8e9a530 --- /dev/null +++ b/app/Listeners/CachePlayerExists.php @@ -0,0 +1,41 @@ +listen(Events\CheckPlayerExists::class, [$this, 'remember']); + $events->listen(Events\PlayerWasAdded::class, [$this, 'forget']); + } + + public function remember(Events\CheckPlayerExists $event) + { + $key = "notfound-{$event->playerName}"; + + if ($event->playerName && is_null(Cache::get($key))) { + $player = Player::where('name', $event->playerName)->first(); + + if (! $player) { + Cache::forever($key, '1'); + return false; + } else { + return true; + } + } else { + return false; + } + } + + public function forget(Events\PlayerWasAdded $event) + { + Cache::forget("notfound-{$event->player->name}"); + } +} diff --git a/app/Listeners/CachePlayerJson.php b/app/Listeners/CachePlayerJson.php new file mode 100644 index 00000000..d88df8e4 --- /dev/null +++ b/app/Listeners/CachePlayerJson.php @@ -0,0 +1,35 @@ +listen(GetPlayerJson::class, [$this, 'remember']); + $events->listen(PlayerProfileUpdated::class, [$this, 'forget']); + } + + public function remember(GetPlayerJson $event) + { + $key = "json-{$event->player->pid}-{$event->apiType}"; + $content = Cache::rememberForever($key, function () use ($event) { + return $event->player->generateJsonProfile($event->apiType); + }); + + return $content; + } + + public function forget(PlayerProfileUpdated $event) + { + Cache::forget("json-{$event->player->pid}-".Player::CSL_API); + Cache::forget("json-{$event->player->pid}-".Player::USM_API); + } +} diff --git a/app/Listeners/CacheSkinPreview.php b/app/Listeners/CacheSkinPreview.php new file mode 100644 index 00000000..0e10b083 --- /dev/null +++ b/app/Listeners/CacheSkinPreview.php @@ -0,0 +1,33 @@ +texture; + $size = $event->size; + $key = "preview-{$texture->tid}-{$size}"; + + $content = Cache::rememberForever($key, function () use ($texture, $size) { + $res = Storage::disk('textures')->read($texture->hash); + + if ($texture->type == 'cape') { + $png = Minecraft::generatePreviewFromCape($res, $size * 0.8, $size * 1.125, $size); + } else { + $png = Minecraft::generatePreviewFromSkin($res, $size, $texture->type == 'alex', 'both', 4); + } + + return png($png); + }); + + return response()->png($content, 200, [ + 'Last-Modified' => Storage::disk('textures')->lastModified($texture->hash) + ]); + } +} diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 6a8e58cc..05ed2e35 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -2,6 +2,9 @@ namespace App\Providers; +use Event; +use App\Events; +use App\Listeners; use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider; class EventServiceProvider extends ServiceProvider @@ -23,5 +26,18 @@ class EventServiceProvider extends ServiceProvider public function boot() { parent::boot(); + + if (option('enable_avatar_cache')) { + Event::listen(Events\GetAvatarPreview::class, Listeners\CacheAvatarPreview::class); + } + if (option('enable_preview_cache')) { + Event::listen(Events\GetSkinPreview::class, Listeners\CacheSkinPreview::class); + } + if (option('enable_notfound_cache')) { + Event::subscribe(Listeners\CachePlayerExists::class); + } + if (option('enable_json_cache')) { + Event::subscribe(Listeners\CachePlayerJson::class); + } } } diff --git a/app/helpers.php b/app/helpers.php index 2a58a566..99cee619 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -491,3 +491,15 @@ if (! function_exists('nl2p')) { return str_replace('
', '', $result); } } + +if (! function_exists('png')) { + function png($resource) + { + ob_start(); + imagepng($resource); + $image = ob_get_contents(); + ob_end_clean(); + imagedestroy($resource); + return $image; + } +} diff --git a/resources/lang/en/options.yml b/resources/lang/en/options.yml index d7886900..627462dc 100644 --- a/resources/lang/en/options.yml +++ b/resources/lang/en/options.yml @@ -146,6 +146,8 @@ meta: meta_extras: title: Other Custom Tags +res-warning: This page is ONLY for advanced users. If you aren't familiar with these, please don't modify them! + resources: title: Resource Files hint: Please check these options if you enabled CDN for your site. @@ -177,6 +179,24 @@ redis: enable_redis: title: Enable label: Enable Redis + description: Redis will be used to store cache, session and etc. connect: success: Connected to Redis server successfully. failed: 'Failed to connect Redis server. Error: :msg' + +cache: + title: Cache Configuration + clear: Clear Cache + cleared: Cache has been cleared. + driver: Current cache driver is 「:driver」. + + enable_avatar_cache: + title: Avatar + label: Enable caching avatar + enable_preview_cache: + title: Texture Preivew + label: Enable caching texture preivew + enable_json_cache: + label: Enable caching Json Profile + enable_notfound_cache: + label: Enable caching whether player is existed or not diff --git a/resources/lang/zh_CN/options.yml b/resources/lang/zh_CN/options.yml index 68e5e292..c7a4bead 100644 --- a/resources/lang/zh_CN/options.yml +++ b/resources/lang/zh_CN/options.yml @@ -146,6 +146,8 @@ meta: meta_extras: title: 其它自定义 标签 +res-warning: 本页面仅供高级用户使用。如果您不清楚这些设置的含义,请不要随意修改它们! + resources: title: 资源文件配置 hint: 如果启用了 CDN 缓存请适当修改这些配置 @@ -176,6 +178,24 @@ redis: enable_redis: title: 启用 label: 使用 Redis + description: Redis 将被用于存储缓存和 Session 等。 connect: success: 成功连接 Redis 服务器。 failed: '连接 Redis 服务器失败。错误消息: :msg' + +cache: + title: 缓存配置 + clear: 清除缓存 + cleared: 缓存已清除。 + driver: 当前缓存驱动为 「:driver」 + + enable_avatar_cache: + title: 头像 + label: 启用头像缓存 + enable_preview_cache: + title: 材质预览 + label: 启用材质预览缓存 + enable_json_cache: + label: 启用 Json Profile 缓存 + enable_notfound_cache: + label: 启用「角色存在与否」的缓存 diff --git a/resources/views/admin/resource.blade.php b/resources/views/admin/resource.blade.php index f059d4c8..15e27f6b 100644 --- a/resources/views/admin/resource.blade.php +++ b/resources/views/admin/resource.blade.php @@ -16,13 +16,21 @@