mirror of
https://github.com/elyby/mojang-api.git
synced 2025-05-31 14:11:44 +05:30
Init
This commit is contained in:
265
src/Api.php
Normal file
265
src/Api.php
Normal file
@@ -0,0 +1,265 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang;
|
||||
|
||||
use Ely\Mojang\Middleware\ResponseConverterMiddleware;
|
||||
use Ely\Mojang\Middleware\RetryMiddleware;
|
||||
use GuzzleHttp\Client as GuzzleClient;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Request;
|
||||
use GuzzleHttp\Psr7\Uri;
|
||||
use Ramsey\Uuid\Uuid;
|
||||
|
||||
class Api {
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
private $client;
|
||||
|
||||
public function __construct(ClientInterface $client) {
|
||||
$this->client = $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $handler HTTP handler function to use with the stack. If no
|
||||
* handler is provided, the best handler for your
|
||||
* system will be utilized.
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public static function create(callable $handler = null): self {
|
||||
$stack = HandlerStack::create($handler);
|
||||
// use after method because middleware executes in reverse order
|
||||
$stack->after('http_errors', ResponseConverterMiddleware::create(), 'mojang_response_converter');
|
||||
$stack->push(RetryMiddleware::create(), 'retry');
|
||||
|
||||
return new static(new GuzzleClient([
|
||||
'handler' => $stack,
|
||||
'timeout' => 10,
|
||||
]));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @param int $atTime
|
||||
*
|
||||
* @return \Ely\Mojang\Response\ProfileInfo
|
||||
*
|
||||
* @throws \Ely\Mojang\Exception\MojangApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @url http://wiki.vg/Mojang_API#Username_-.3E_UUID_at_time
|
||||
*/
|
||||
public function usernameToUUID(string $username, int $atTime = null): Response\ProfileInfo {
|
||||
$query = [];
|
||||
if ($atTime !== null) {
|
||||
$query['atTime'] = $atTime;
|
||||
}
|
||||
|
||||
$response = $this->getClient()->request('GET', "https://api.mojang.com/users/profiles/minecraft/{$username}", [
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
$data = $this->decode($response->getBody()->getContents());
|
||||
|
||||
return Response\ProfileInfo::createFromResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uuid
|
||||
*
|
||||
* @return \Ely\Mojang\Response\ProfileResponse
|
||||
*
|
||||
* @throws \Ely\Mojang\Exception\MojangApiException
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @url http://wiki.vg/Mojang_API#UUID_-.3E_Profile_.2B_Skin.2FCape
|
||||
*/
|
||||
public function uuidToTextures(string $uuid): Response\ProfileResponse {
|
||||
$response = $this->getClient()->request('GET', "https://sessionserver.mojang.com/session/minecraft/profile/{$uuid}", [
|
||||
'query' => [
|
||||
'unsigned' => false,
|
||||
],
|
||||
]);
|
||||
$body = $this->decode($response->getBody()->getContents());
|
||||
|
||||
return new Response\ProfileResponse($body['id'], $body['name'], $body['properties']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to exchange username to the corresponding textures.
|
||||
*
|
||||
* @param string $username
|
||||
*
|
||||
* @return \Ely\Mojang\Response\ProfileResponse
|
||||
*
|
||||
* @throws GuzzleException
|
||||
* @throws \Ely\Mojang\Exception\MojangApiException
|
||||
*/
|
||||
public function usernameToTextures(string $username): Response\ProfileResponse {
|
||||
return $this->uuidToTextures($this->usernameToUUID($username)->getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $login
|
||||
* @param string $password
|
||||
* @param string $clientToken
|
||||
*
|
||||
* @return \Ely\Mojang\Response\AuthenticateResponse
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @url https://wiki.vg/Authentication#Authenticate
|
||||
*/
|
||||
public function authenticate(
|
||||
string $login,
|
||||
string $password,
|
||||
string $clientToken = null
|
||||
): Response\AuthenticateResponse {
|
||||
if ($clientToken === null) {
|
||||
/** @noinspection PhpUnhandledExceptionInspection */
|
||||
$clientToken = Uuid::uuid4()->toString();
|
||||
}
|
||||
|
||||
$response = $this->getClient()->request('POST', 'https://authserver.mojang.com/authenticate', [
|
||||
'json' => [
|
||||
'username' => $login,
|
||||
'password' => $password,
|
||||
'clientToken' => $clientToken,
|
||||
'requestUser' => true,
|
||||
'agent' => [
|
||||
'name' => 'Minecraft',
|
||||
'version' => 1,
|
||||
],
|
||||
],
|
||||
]);
|
||||
$body = $this->decode($response->getBody()->getContents());
|
||||
|
||||
return new Response\AuthenticateResponse(
|
||||
$body['accessToken'],
|
||||
$body['clientToken'],
|
||||
$body['availableProfiles'],
|
||||
$body['selectedProfile'],
|
||||
$body['user']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
* @throws \GuzzleHttp\Exception\GuzzleException
|
||||
*
|
||||
* @url https://wiki.vg/Authentication#Validate
|
||||
*/
|
||||
public function validate(string $accessToken): bool {
|
||||
try {
|
||||
$response = $this->getClient()->request('POST', 'https://authserver.mojang.com/authenticate', [
|
||||
'json' => [
|
||||
'accessToken' => $accessToken,
|
||||
],
|
||||
]);
|
||||
if ($response->getStatusCode() === 204) {
|
||||
return true;
|
||||
}
|
||||
} catch (Exception\ForbiddenException $e) {
|
||||
// Suppress exception and let it just exit below
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
* @param string $accountUuid
|
||||
* @param \Psr\Http\Message\StreamInterface|resource|string $skinContents
|
||||
* @param bool $isSlim
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @url https://wiki.vg/Mojang_API#Upload_Skin
|
||||
*/
|
||||
public function uploadSkin(string $accessToken, string $accountUuid, $skinContents, bool $isSlim): void {
|
||||
$this->getClient()->request('PUT', "https://api.mojang.com/user/profile/{$accountUuid}/skin", [
|
||||
'multipart' => [
|
||||
[
|
||||
'name' => 'file',
|
||||
'contents' => $skinContents,
|
||||
'filename' => 'char.png',
|
||||
],
|
||||
[
|
||||
'name' => 'model',
|
||||
'contents' => $isSlim ? 'slim' : '',
|
||||
],
|
||||
],
|
||||
'headers' => [
|
||||
'Authorization' => 'Bearer ' . $accessToken,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $accessToken
|
||||
* @param string $accountUuid
|
||||
* @param string $serverId
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @url https://wiki.vg/Protocol_Encryption#Client
|
||||
*/
|
||||
public function joinServer(string $accessToken, string $accountUuid, string $serverId): void {
|
||||
$this->getClient()->request('POST', 'https://sessionserver.mojang.com/session/minecraft/join', [
|
||||
'json' => [
|
||||
'accessToken' => $accessToken,
|
||||
'selectedProfile' => $accountUuid,
|
||||
'serverId' => $serverId,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $username
|
||||
* @param string $serverId
|
||||
*
|
||||
* @return \Ely\Mojang\Response\ProfileResponse
|
||||
*
|
||||
* @throws \Ely\Mojang\Exception\NoContentException
|
||||
* @throws GuzzleException
|
||||
*
|
||||
* @url https://wiki.vg/Protocol_Encryption#Server
|
||||
*/
|
||||
public function hasJoinedServer(string $username, string $serverId): Response\ProfileResponse {
|
||||
$uri = (new Uri('https://sessionserver.mojang.com/session/minecraft/hasJoined'))
|
||||
->withQuery(http_build_query([
|
||||
'username' => $username,
|
||||
'serverId' => $serverId,
|
||||
], '', '&', PHP_QUERY_RFC3986));
|
||||
$request = new Request('GET', $uri);
|
||||
$response = $this->getClient()->send($request);
|
||||
$rawBody = $response->getBody()->getContents();
|
||||
if (empty($rawBody)) {
|
||||
throw new Exception\NoContentException($request, $response);
|
||||
}
|
||||
|
||||
$body = $this->decode($rawBody);
|
||||
|
||||
return new Response\ProfileResponse($body['id'], $body['name'], $body['properties']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ClientInterface
|
||||
*/
|
||||
protected function getClient(): ClientInterface {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
private function decode(string $response): array {
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
}
|
||||
20
src/Exception/ForbiddenException.php
Normal file
20
src/Exception/ForbiddenException.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Exception;
|
||||
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class ForbiddenException extends ClientException implements MojangApiException {
|
||||
|
||||
public function __construct(RequestInterface $request, ResponseInterface $response) {
|
||||
parent::__construct(
|
||||
'The request was executed with a non-existent or expired access token',
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
10
src/Exception/MojangApiException.php
Normal file
10
src/Exception/MojangApiException.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Exception;
|
||||
|
||||
use Throwable;
|
||||
|
||||
interface MojangApiException extends Throwable {
|
||||
|
||||
}
|
||||
16
src/Exception/NoContentException.php
Normal file
16
src/Exception/NoContentException.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Exception;
|
||||
|
||||
use GuzzleHttp\Exception\RequestException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class NoContentException extends RequestException implements MojangApiException {
|
||||
|
||||
public function __construct(RequestInterface $request, ResponseInterface $response) {
|
||||
parent::__construct('No data were received in the response.', $request, $response);
|
||||
}
|
||||
|
||||
}
|
||||
21
src/Exception/TooManyRequestsException.php
Normal file
21
src/Exception/TooManyRequestsException.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Exception;
|
||||
|
||||
use GuzzleHttp\Exception\ClientException;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class TooManyRequestsException extends ClientException implements MojangApiException {
|
||||
|
||||
public function __construct(RequestInterface $request, ResponseInterface $response) {
|
||||
parent::__construct(
|
||||
'The request limit was exceeded. ' .
|
||||
'Read the documentation for the method requested to find out which RPS is allowed.',
|
||||
$request,
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
54
src/Middleware/ResponseConverterMiddleware.php
Normal file
54
src/Middleware/ResponseConverterMiddleware.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Middleware;
|
||||
|
||||
use Ely\Mojang\Exception;
|
||||
use GuzzleHttp\Promise\PromiseInterface;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class ResponseConverterMiddleware {
|
||||
|
||||
/**
|
||||
* @var callable
|
||||
*/
|
||||
private $nextHandler;
|
||||
|
||||
public function __construct(callable $nextHandler) {
|
||||
$this->nextHandler = $nextHandler;
|
||||
}
|
||||
|
||||
public function __invoke(RequestInterface $request, array $options): PromiseInterface {
|
||||
$fn = $this->nextHandler;
|
||||
/** @var PromiseInterface $promise */
|
||||
$promise = $fn($request, $options);
|
||||
|
||||
return $promise->then(static function($response) use ($request) {
|
||||
if ($response instanceof ResponseInterface) {
|
||||
$method = $request->getMethod();
|
||||
$statusCode = $response->getStatusCode();
|
||||
if ($method === 'GET' && $statusCode === 204) {
|
||||
throw new Exception\NoContentException($request, $response);
|
||||
}
|
||||
|
||||
if ($statusCode === 403) {
|
||||
throw new Exception\ForbiddenException($request, $response);
|
||||
}
|
||||
|
||||
if ($statusCode === 429) {
|
||||
throw new Exception\TooManyRequestsException($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
});
|
||||
}
|
||||
|
||||
public static function create(): callable {
|
||||
return static function(callable $handler): callable {
|
||||
return new static($handler);
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
39
src/Middleware/RetryMiddleware.php
Normal file
39
src/Middleware/RetryMiddleware.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Middleware;
|
||||
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Middleware;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
class RetryMiddleware {
|
||||
|
||||
public static function create(): callable {
|
||||
return Middleware::retry([static::class, 'shouldRetry']);
|
||||
}
|
||||
|
||||
public static function shouldRetry(
|
||||
int $retries,
|
||||
RequestInterface $request,
|
||||
?ResponseInterface $response,
|
||||
?GuzzleException $reason
|
||||
): bool {
|
||||
if ($retries >= 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($reason instanceof ConnectException) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($response !== null && (int)floor($response->getStatusCode() / 100) === 5) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
70
src/Response/AuthenticateResponse.php
Normal file
70
src/Response/AuthenticateResponse.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response;
|
||||
|
||||
class AuthenticateResponse {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $accessToken;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $clientToken;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rawAvailableProfiles;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rawSelectedProfile;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $rawUser;
|
||||
|
||||
public function __construct(
|
||||
string $accessToken,
|
||||
string $clientToken,
|
||||
array $availableProfiles,
|
||||
array $selectedProfile,
|
||||
array $user
|
||||
) {
|
||||
$this->accessToken = $accessToken;
|
||||
$this->clientToken = $clientToken;
|
||||
$this->rawAvailableProfiles = $availableProfiles;
|
||||
$this->rawSelectedProfile = $selectedProfile;
|
||||
$this->rawUser = $user;
|
||||
}
|
||||
|
||||
public function getAccessToken(): string {
|
||||
return $this->accessToken;
|
||||
}
|
||||
|
||||
public function getClientToken(): string {
|
||||
return $this->clientToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ProfileInfo[]
|
||||
*/
|
||||
public function getAvailableProfiles(): array {
|
||||
return array_map([ProfileInfo::class, 'createFromResponse'], $this->rawAvailableProfiles);
|
||||
}
|
||||
|
||||
public function getSelectedProfile(): ProfileInfo {
|
||||
return ProfileInfo::createFromResponse($this->rawSelectedProfile);
|
||||
}
|
||||
|
||||
public function getUser(): AuthenticationResponseUserField {
|
||||
return new AuthenticationResponseUserField($this->rawUser['id'], $this->rawUser['properties']);
|
||||
}
|
||||
|
||||
}
|
||||
30
src/Response/AuthenticationResponseUserField.php
Normal file
30
src/Response/AuthenticationResponseUserField.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response;
|
||||
|
||||
use Ely\Mojang\Response\Properties\Factory;
|
||||
|
||||
class AuthenticationResponseUserField {
|
||||
|
||||
private $id;
|
||||
|
||||
private $rawProperties;
|
||||
|
||||
public function __construct(string $id, array $rawProperties) {
|
||||
$this->id = $id;
|
||||
$this->rawProperties = $rawProperties;
|
||||
}
|
||||
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Ely\Mojang\Response\Properties\Property[]
|
||||
*/
|
||||
public function getProperties(): array {
|
||||
return array_map([Factory::class, 'createFromProp'], $this->rawProperties);
|
||||
}
|
||||
|
||||
}
|
||||
72
src/Response/ProfileInfo.php
Normal file
72
src/Response/ProfileInfo.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response;
|
||||
|
||||
class ProfileInfo {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isLegacy;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isDemo;
|
||||
|
||||
public function __construct(string $id, string $name, bool $isLegacy = false, bool $isDemo = false) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->isLegacy = $isLegacy;
|
||||
$this->isDemo = $isDemo;
|
||||
}
|
||||
|
||||
public static function createFromResponse(array $response): self {
|
||||
return new static(
|
||||
$response['id'],
|
||||
$response['name'],
|
||||
$response['legacy'] ?? false,
|
||||
$response['demo'] ?? false
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string user's uuid without dashes
|
||||
*/
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string username at the current time
|
||||
*/
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true means, that account not migrated into Mojang account
|
||||
*/
|
||||
public function isLegacy(): bool {
|
||||
return $this->isLegacy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool true means, that account now in demo mode (not premium user)
|
||||
*/
|
||||
public function isDemo(): bool {
|
||||
return $this->isDemo;
|
||||
}
|
||||
|
||||
}
|
||||
46
src/Response/ProfileResponse.php
Normal file
46
src/Response/ProfileResponse.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response;
|
||||
|
||||
use Ely\Mojang\Response\Properties\Factory;
|
||||
|
||||
class ProfileResponse {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $name;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $props;
|
||||
|
||||
public function __construct(string $id, string $name, array $rawProps) {
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->props = $rawProps;
|
||||
}
|
||||
|
||||
public function getId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Ely\Mojang\Response\Properties\Property[]
|
||||
*/
|
||||
public function getProps(): array {
|
||||
return array_map([Factory::class, 'createFromProp'], $this->props);
|
||||
}
|
||||
|
||||
}
|
||||
22
src/Response/properties/Factory.php
Normal file
22
src/Response/properties/Factory.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class Factory {
|
||||
|
||||
private static $MAP = [
|
||||
'textures' => TexturesProperty::class,
|
||||
];
|
||||
|
||||
public static function createFromProp(array $prop): Property {
|
||||
$name = $prop['name'];
|
||||
if (isset(static::$MAP[$name])) {
|
||||
$className = static::$MAP[$name];
|
||||
return new $className($prop);
|
||||
}
|
||||
|
||||
return new Property($prop);
|
||||
}
|
||||
|
||||
}
|
||||
31
src/Response/properties/Property.php
Normal file
31
src/Response/properties/Property.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class Property {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $name;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
public $value;
|
||||
|
||||
public function __construct(array $prop) {
|
||||
$this->name = $prop['name'];
|
||||
$this->value = $prop['value'];
|
||||
}
|
||||
|
||||
public function getName(): string {
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getValue(): string {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
}
|
||||
26
src/Response/properties/TexturesProperty.php
Normal file
26
src/Response/properties/TexturesProperty.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class TexturesProperty extends Property {
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $signature;
|
||||
|
||||
public function __construct(array $prop) {
|
||||
parent::__construct($prop);
|
||||
$this->signature = $prop['signature'] ?? null;
|
||||
}
|
||||
|
||||
public function getTextures(): TexturesPropertyValue {
|
||||
return TexturesPropertyValue::createFromRawTextures($this->value);
|
||||
}
|
||||
|
||||
public function getSignature(): ?string {
|
||||
return $this->signature;
|
||||
}
|
||||
|
||||
}
|
||||
90
src/Response/properties/TexturesPropertyValue.php
Normal file
90
src/Response/properties/TexturesPropertyValue.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class TexturesPropertyValue {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $username;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private $textures;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private $timestamp;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $signatureRequired;
|
||||
|
||||
public function __construct(
|
||||
string $profileId,
|
||||
string $profileName,
|
||||
array $textures,
|
||||
int $timestamp,
|
||||
bool $signatureRequired = false
|
||||
) {
|
||||
$this->id = $profileId;
|
||||
$this->username = $profileName;
|
||||
$this->textures = $textures;
|
||||
$this->timestamp = (int)floor($timestamp / 1000);
|
||||
$this->signatureRequired = $signatureRequired;
|
||||
}
|
||||
|
||||
public static function createFromRawTextures(string $rawTextures): self {
|
||||
$decoded = json_decode(base64_decode($rawTextures), true);
|
||||
return new static(
|
||||
$decoded['profileId'],
|
||||
$decoded['profileName'],
|
||||
$decoded['textures'],
|
||||
$decoded['timestamp'],
|
||||
$decoded['signatureRequired'] ?? false
|
||||
);
|
||||
}
|
||||
|
||||
public function getProfileId(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getProfileName(): string {
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function getTimestamp(): int {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
public function isSignatureRequired(): bool {
|
||||
return $this->signatureRequired;
|
||||
}
|
||||
|
||||
public function getSkin(): ?TexturesPropertyValueSkin {
|
||||
if (!isset($this->textures['SKIN'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return TexturesPropertyValueSkin::createFromTextures($this->textures['SKIN']);
|
||||
}
|
||||
|
||||
public function getCape(): ?TexturesPropertyValueCape {
|
||||
if (!isset($this->textures['CAPE'])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new TexturesPropertyValueCape($this->textures['CAPE']['url']);
|
||||
}
|
||||
|
||||
}
|
||||
21
src/Response/properties/TexturesPropertyValueCape.php
Normal file
21
src/Response/properties/TexturesPropertyValueCape.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class TexturesPropertyValueCape {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
public function __construct(string $skinUrl) {
|
||||
$this->url = $skinUrl;
|
||||
}
|
||||
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/Response/properties/TexturesPropertyValueSkin.php
Normal file
36
src/Response/properties/TexturesPropertyValueSkin.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Ely\Mojang\Response\Properties;
|
||||
|
||||
class TexturesPropertyValueSkin {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isSlim;
|
||||
|
||||
public function __construct(string $skinUrl, bool $isSlim = false) {
|
||||
$this->url = $skinUrl;
|
||||
$this->isSlim = $isSlim;
|
||||
}
|
||||
|
||||
public static function createFromTextures(array $textures): self {
|
||||
$model = &$textures['metainfo']['model']; // ampersand to avoid notice about unexpected key
|
||||
return new static($textures['url'], $model === 'slim');
|
||||
}
|
||||
|
||||
public function getUrl(): string {
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
public function isSlim(): bool {
|
||||
return $this->isSlim;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user