diff --git a/app/Http/Controllers/TextureController.php b/app/Http/Controllers/TextureController.php index 149eca92..fd6235ab 100644 --- a/app/Http/Controllers/TextureController.php +++ b/app/Http/Controllers/TextureController.php @@ -8,6 +8,7 @@ use App\Models\Player; use App\Models\Texture; use App\Models\User; use App\Services\Minecraft; +use Cache; use Carbon\Carbon; use Event; use Exception; @@ -19,27 +20,20 @@ use Symfony\Component\HttpFoundation\Response as SymfonyResponse; class TextureController extends Controller { - public function json($player_name, $api = '') + public function json($player) { - $player = $this->getPlayerInstance($player_name); + $player = $this->getPlayerInstance($player); + if (option('enable_json_cache')) { + $json = Cache::rememberForever('json-'.$player->pid, function () use ($player) { + return $player->toJson(); + }); - if ($api == 'csl') { - $content = $player->getJsonProfile(Player::CSL_API); - } elseif ($api == 'usm') { - $content = $player->getJsonProfile(Player::USM_API); - } else { - $content = $player->getJsonProfile(option('api_type')); + return response($json) + ->header('Content-Type', 'application/json') + ->setLastModified($player->last_modified); } - return response($content, 200, [ - 'Content-type' => 'application/json', - 'Last-Modified' => $player->last_modified, - ]); - } - - public function jsonWithApi($api, $player_name) - { - return $this->json($player_name, $api); + return response()->json($player)->setLastModified($player->last_modified); } public function texture($hash, $headers = [], $message = '') @@ -59,11 +53,6 @@ class TextureController extends Controller return abort(404, $message); } - public function textureWithApi($api, $hash) - { - return $this->texture($hash); - } - public function avatarByTid($tid, $size = 128) { if ($t = Texture::find($tid)) { diff --git a/app/Listeners/CachePlayerJson.php b/app/Listeners/CachePlayerJson.php deleted file mode 100644 index 7151fbed..00000000 --- a/app/Listeners/CachePlayerJson.php +++ /dev/null @@ -1,33 +0,0 @@ -listen(Events\GetPlayerJson::class, [$this, 'remember']); - $events->listen(Events\PlayerProfileUpdated::class, [$this, 'forget']); - } - - public function remember($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($event) - { - Cache::forget("json-{$event->player->pid}-".Player::CSL_API); - Cache::forget("json-{$event->player->pid}-".Player::USM_API); - } -} diff --git a/app/Listeners/CleanPlayerJson.php b/app/Listeners/CleanPlayerJson.php new file mode 100644 index 00000000..c277e26e --- /dev/null +++ b/app/Listeners/CleanPlayerJson.php @@ -0,0 +1,13 @@ +player->pid); + } +} diff --git a/app/Models/Player.php b/app/Models/Player.php index 8e880d92..48a6fc05 100644 --- a/app/Models/Player.php +++ b/app/Models/Player.php @@ -2,9 +2,8 @@ namespace App\Models; -use App\Events\GetPlayerJson; use App\Events\PlayerProfileUpdated; -use Event; +use App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; @@ -40,7 +39,39 @@ class Player extends Model public function user() { - return $this->belongsTo('App\Models\User', 'uid'); + return $this->belongsTo(Models\User::class, 'uid'); + } + + public function skin() + { + return $this->belongsTo(Models\Texture::class, 'tid_skin'); + } + + public function cape() + { + return $this->belongsTo(Models\Texture::class, 'tid_cape'); + } + + public function getModelAttribute() + { + return optional($this->skin)->model ?? 'default'; + } + + /** + * CustomSkinAPI R1. + */ + public function toJson($options = 0) + { + $model = $this->model; + $profile = [ + 'username' => $this->name, + 'skins' => [ + $model => optional($this->skin)->hash, + ], + 'cape' => optional($this->cape)->hash, + ]; + + return json_encode($profile, $options | JSON_UNESCAPED_UNICODE); } /** @@ -58,43 +89,4 @@ class Player extends Model return false; } - - public function getJsonProfile($api_type) - { - // Support both CustomSkinLoader API & UniSkinAPI - if ($api_type == self::CSL_API || $api_type == self::USM_API) { - $responses = Event::dispatch(new GetPlayerJson($this, $api_type)); - - // If listeners return nothing - if (isset($responses[0]) && $responses[0] !== null) { - return $responses[0]; // @codeCoverageIgnore - } else { - return $this->generateJsonProfile($api_type); - } - } else { - throw new \InvalidArgumentException('The given api type should be Player::CSL_API or Player::USM_API.'); - } - } - - public function generateJsonProfile($api_type) - { - $json[($api_type == self::CSL_API) ? 'username' : 'player_name'] = $this->name; - - $texture = Texture::find($this->tid_skin); - $model = empty($texture) ? 'default' : ($texture->type === 'steve' ? 'default' : 'slim'); - - if ($api_type == self::USM_API) { - $json['last_update'] = strtotime($this->last_modified); - $json['model_preference'] = [$model]; - } - - $skinHash = $this->getTexture('skin'); - if ($model == 'slim') { - $json['skins']['slim'] = $skinHash; - } - $json['skins']['default'] = $skinHash; - $json['cape'] = $this->getTexture('cape'); - - return json_encode($json, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE); - } } diff --git a/app/Models/Texture.php b/app/Models/Texture.php index e3411ca3..7f27be4e 100644 --- a/app/Models/Texture.php +++ b/app/Models/Texture.php @@ -22,6 +22,12 @@ class Texture extends Model 'deleting' => \App\Events\TextureDeleting::class, ]; + public function getModelAttribute() + { + // Don't worry about cape... + return $this->type === 'alex' ? 'slim' : 'default'; + } + public function scopeLike($query, $field, $value) { return $query->where($field, 'LIKE', "%$value%"); diff --git a/app/Providers/EventServiceProvider.php b/app/Providers/EventServiceProvider.php index 2984d3cf..4a489212 100644 --- a/app/Providers/EventServiceProvider.php +++ b/app/Providers/EventServiceProvider.php @@ -56,7 +56,7 @@ class EventServiceProvider extends ServiceProvider Event::subscribe(Listeners\CachePlayerExists::class); } if (option('enable_json_cache')) { - Event::subscribe(Listeners\CachePlayerJson::class); + Event::listen(Events\PlayerProfileUpdated::class, Listeners\CleanPlayerJson::class); } } } diff --git a/resources/misc/changelogs/en/5.0.0.md b/resources/misc/changelogs/en/5.0.0.md index 2e0dc194..53b7a685 100644 --- a/resources/misc/changelogs/en/5.0.0.md +++ b/resources/misc/changelogs/en/5.0.0.md @@ -58,6 +58,7 @@ - Removed "3rd-party comment", and please install separated plugin if you need it. - Removed enabling or disabling Redis via Web UI. - Removed Legacy API from core. (Install plugin if you need it.) +- Removed Universal Skin API from core. (Install plugin if you need it.) ## Internal Changes diff --git a/resources/misc/changelogs/zh_CN/5.0.0.md b/resources/misc/changelogs/zh_CN/5.0.0.md index 42be3690..9c0b0f95 100644 --- a/resources/misc/changelogs/zh_CN/5.0.0.md +++ b/resources/misc/changelogs/zh_CN/5.0.0.md @@ -58,6 +58,7 @@ - 移除「第三方评论」功能,如有需要请安装独立插件 - 移除通过 UI 来开启或关闭 Redis 的功能 - 移除「传统皮肤加载方式」(如有需要,请安装插件) +- 移除 Universal Skin API(如有需要,请安装插件) ## 内部更改 diff --git a/routes/static.php b/routes/static.php index f3d92684..5e7c1851 100644 --- a/routes/static.php +++ b/routes/static.php @@ -1,13 +1,11 @@ 'player'], function () { - // Json profile - Route::get('/{player_name}.json', 'TextureController@json'); - Route::get('/{api}/{player_name}.json', 'TextureController@jsonWithApi')->where('api', 'usm|csl'); + Route::get('/{player}.json', 'TextureController@json'); + Route::get('/csl/{player}.json', 'TextureController@json'); }); Route::get('/textures/{hash}', 'TextureController@texture'); -Route::get('/{api}/textures/{hash}', 'TextureController@textureWithApi')->where('api', 'usm|csl'); Route::get('/avatar/player/{size}/{name}.png', 'TextureController@avatarByPlayer'); Route::get('/avatar/user/{uid}/{size?}', 'TextureController@avatar'); diff --git a/tests/HttpTest/ControllersTest/TextureControllerTest.php b/tests/HttpTest/ControllersTest/TextureControllerTest.php index 2b7df430..f3a0f5ac 100644 --- a/tests/HttpTest/ControllersTest/TextureControllerTest.php +++ b/tests/HttpTest/ControllersTest/TextureControllerTest.php @@ -5,6 +5,7 @@ namespace Tests; use App\Models\Player; use App\Models\Texture; use App\Models\User; +use Cache; use Carbon\Carbon; use Event; use Exception; @@ -36,7 +37,6 @@ class TextureControllerTest extends TestCase $player->user->permission = User::NORMAL; $player->user->save(); - // Default API is CSL API $this->getJson("/{$player->name}.json") ->assertJson([ 'username' => $player->name, @@ -45,16 +45,18 @@ class TextureControllerTest extends TestCase ], 'cape' => null, ])->assertHeader('Last-Modified'); - } - public function testJsonWithApi() - { - $steve = factory(Texture::class)->create(); - $alex = factory(Texture::class, 'alex')->create(); - $player = factory(Player::class)->create(['tid_skin' => $steve->tid]); + option(['enable_json_cache' => true]); + Cache::shouldReceive('rememberForever') + ->withArgs(function ($key, $closure) use ($player) { + $this->assertEquals('json-'.$player->pid, $key); + $this->assertEquals($player->toJson(), $closure()); - // CSL API - $this->getJson("/csl/{$player->name}.json") + return true; + }) + ->once() + ->andReturn($player->toJson()); + $this->getJson("/{$player->name}.json") ->assertJson([ 'username' => $player->name, 'skins' => [ @@ -62,43 +64,6 @@ class TextureControllerTest extends TestCase ], 'cape' => null, ])->assertHeader('Last-Modified'); - - // USM API - $this->getJson("/usm/{$player->name}.json") - ->assertJson([ - 'player_name' => $player->name, - 'model_preference' => ['default'], - 'skins' => [ - 'default' => $steve->hash, - ], - 'cape' => null, - ])->assertHeader('Last-Modified'); - - $player->tid_skin = $alex->tid; - $player->save(); - - // CSL API - $this->getJson("/csl/{$player->name}.json") - ->assertJson([ - 'username' => $player->name, - 'skins' => [ - 'slim' => $alex->hash, - 'default' => $alex->hash, - ], - 'cape' => null, - ]); - - // USM API - $this->getJson("/usm/{$player->name}.json") - ->assertJson([ - 'player_name' => $player->name, - 'model_preference' => ['slim'], - 'skins' => [ - 'slim' => $alex->hash, - 'default' => $alex->hash, - ], - 'cape' => null, - ]); } public function testTexture() @@ -130,27 +95,6 @@ class TextureControllerTest extends TestCase $this->get('/textures/'.$steve->hash)->assertNotFound(); } - public function testTextureWithApi() - { - Storage::fake('textures'); - $steve = factory(Texture::class)->create(); - Storage::disk('textures')->put($steve->hash, ''); - - $this->get('/csl/textures/'.$steve->hash) - ->assertHeader('Content-Type', 'image/png') - ->assertHeader('Last-Modified') - ->assertHeader('Accept-Ranges', 'bytes') - ->assertHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) - ->assertStatus(200); - - $this->get('/usm/textures/'.$steve->hash) - ->assertHeader('Content-Type', 'image/png') - ->assertHeader('Last-Modified') - ->assertHeader('Accept-Ranges', 'bytes') - ->assertHeader('Content-Length', Storage::disk('textures')->size($steve->hash)) - ->assertSuccessful(); - } - public function testAvatarByTid() { $this->get('/avatar/1')->assertHeader('Content-Type', 'image/png'); diff --git a/tests/ListenersTest/CachePlayerJsonTest.php b/tests/ListenersTest/CachePlayerJsonTest.php deleted file mode 100644 index ea73433d..00000000 --- a/tests/ListenersTest/CachePlayerJsonTest.php +++ /dev/null @@ -1,39 +0,0 @@ - true]); - $provider = new \App\Providers\EventServiceProvider(app()); - $provider->boot(); - } - - public function testRemember() - { - $player = factory(Player::class)->create(); - event(new GetPlayerJson($player, Player::CSL_API)); - $this->assertTrue(Cache::has("json-{$player->pid}-".Player::CSL_API)); - } - - public function testForget() - { - $player = factory(Player::class)->create(); - event(new PlayerProfileUpdated($player)); - Cache::shouldReceive('forget') - ->with("json-{$player->pid}-".Player::CSL_API) - ->with("json-{$player->pid}-".Player::USM_API); - } -} diff --git a/tests/ListenersTest/CleanPlayerJsonTest.php b/tests/ListenersTest/CleanPlayerJsonTest.php new file mode 100644 index 00000000..982e4ca0 --- /dev/null +++ b/tests/ListenersTest/CleanPlayerJsonTest.php @@ -0,0 +1,28 @@ + true]); + app()->register(\App\Providers\EventServiceProvider::class); + } + + public function testHandle() + { + $player = factory(Player::class)->create(); + event(new PlayerProfileUpdated($player)); + Cache::shouldReceive('forget')->with('json-'.$player->pid); + } +} diff --git a/tests/ModelsTest/PlayerTest.php b/tests/ModelsTest/PlayerTest.php index a38f6cb2..9c2f38c3 100644 --- a/tests/ModelsTest/PlayerTest.php +++ b/tests/ModelsTest/PlayerTest.php @@ -21,10 +21,21 @@ class PlayerTest extends TestCase $this->assertFalse($player->getTexture('invalid_type')); } - public function testGetJsonProfile() + public function testGetModelAttribute() { - $player = factory(Player::class)->make(); - $this->expectException(\InvalidArgumentException::class); - $this->assertNull($player->getJsonProfile(-1)); + $player = factory(Player::class)->create(); + $this->assertEquals('default', $player->model); + + $alex = factory(Texture::class, 'alex')->create(); + $player->tid_skin = $alex->tid; + $player->save(); + $player->refresh(); + $this->assertEquals('slim', $player->model); + + $steve = factory(Texture::class)->create(); + $player->tid_skin = $steve->tid; + $player->save(); + $player->refresh(); + $this->assertEquals('default', $player->model); } }