Remove Universal Skin API from core
This commit is contained in:
parent
22128d360c
commit
611f6c8cee
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
13
app/Listeners/CleanPlayerJson.php
Normal file
13
app/Listeners/CleanPlayerJson.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Listeners;
|
||||
|
||||
use Cache;
|
||||
|
||||
class CleanPlayerJson
|
||||
{
|
||||
public function handle($event)
|
||||
{
|
||||
Cache::forget('json-'.$event->player->pid);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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%");
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -58,6 +58,7 @@
|
||||
- 移除「第三方评论」功能,如有需要请安装独立插件
|
||||
- 移除通过 UI 来开启或关闭 Redis 的功能
|
||||
- 移除「传统皮肤加载方式」(如有需要,请安装插件)
|
||||
- 移除 Universal Skin API(如有需要,请安装插件)
|
||||
|
||||
## 内部更改
|
||||
|
||||
|
@ -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');
|
||||
|
@ -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');
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
28
tests/ListenersTest/CleanPlayerJsonTest.php
Normal file
28
tests/ListenersTest/CleanPlayerJsonTest.php
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user