Remove Universal Skin API from core

This commit is contained in:
Pig Fang 2019-12-30 23:29:44 +08:00
parent 22128d360c
commit 611f6c8cee
13 changed files with 123 additions and 212 deletions

View File

@ -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)) {

View File

@ -1,33 +0,0 @@
<?php
namespace App\Listeners;
use App\Events;
use App\Models\Player;
use Cache;
use Illuminate\Contracts\Events\Dispatcher;
class CachePlayerJson
{
public function subscribe(Dispatcher $events)
{
$events->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);
}
}

View File

@ -0,0 +1,13 @@
<?php
namespace App\Listeners;
use Cache;
class CleanPlayerJson
{
public function handle($event)
{
Cache::forget('json-'.$event->player->pid);
}
}

View File

@ -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);
}
}

View File

@ -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%");

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -58,6 +58,7 @@
- 移除「第三方评论」功能,如有需要请安装独立插件
- 移除通过 UI 来开启或关闭 Redis 的功能
- 移除「传统皮肤加载方式」(如有需要,请安装插件)
- 移除 Universal Skin API如有需要请安装插件
## 内部更改

View File

@ -1,13 +1,11 @@
<?php
Route::group(['middleware' => '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');

View File

@ -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');

View File

@ -1,39 +0,0 @@
<?php
namespace Tests;
use App\Events\GetPlayerJson;
use App\Events\PlayerProfileUpdated;
use App\Models\Player;
use Cache;
use Event;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class CachePlayerJsonTest extends TestCase
{
use DatabaseTransactions;
public function setUp(): void
{
parent::setUp();
option(['enable_json_cache' => 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);
}
}

View File

@ -0,0 +1,28 @@
<?php
namespace Tests;
use App\Events\PlayerProfileUpdated;
use App\Models\Player;
use Cache;
use Event;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class CleanPlayerJsonTest extends TestCase
{
use DatabaseTransactions;
public function setUp(): void
{
parent::setUp();
option(['enable_json_cache' => 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);
}
}

View File

@ -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);
}
}