Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
# BedrockEditionClient
BedrockEditionClient
====================

[Русский](#русский)
[English](#english)


# Русский
Реализация клиента Minecraft: Bedrock Edition на основе [PocketMine-MP](https://github.com/pmmp/PocketMine-MP) с шифрованием

Текущая версия Minecraft, которую поддерживает клиент на данный момент: 1.19.0

### Планы
- [ ] Xbox Live Аутентификация
- [ ] Подключение к Realms
- [ ] И много другого..

### Подготовка к работе
Для запуска клиента "BedrockEditionClient" требуются бинарники PMMP php8

### Установка
```
git clone https://github.com/ipad54/BedrockEditionClient
cd BedrockEditionClient
/bin/php7/bin/php composer.phar install --no-dev --classmap-authoritative
```


### Документация
Документация: [DOCS.md](https://github.com/ipad54/BedrockEditionClient/blob/master/docs/DOCS.md)

___

# English
Implementation of Minecraft: Bedrock Edition Client based on [PocketMine-MP](https://github.com/pmmp/PocketMine-MP) with Encryption

Current Minecraft version: 1.19.0
Expand All @@ -20,4 +53,4 @@ cd BedrockEditionClient


### Documentation
See [DOCS.md](https://github.com/ipad54/BedrockEditionClient/blob/master/DOCS.md)
See [DOCS_EN.md](https://github.com/ipad54/BedrockEditionClient/blob/master/DOCS_EN.md)
5 changes: 2 additions & 3 deletions DOCS.md → docs/DOCS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
### Creating a simple client that writes something to the chat after joining to the server
### Создание простого клиента, который отправляет сообщения в чат после подключения к серверу

```php
<?php
Expand Down Expand Up @@ -34,7 +34,7 @@ while(true){
![Screenshot_180](https://user-images.githubusercontent.com/63200545/174803523-0281e5c8-dc2b-414e-a524-b761754f7957.png)


### Creating spam bots
### Создание спам-ботов

```php
<?php
Expand Down Expand Up @@ -106,4 +106,3 @@ function sendSpam(Client $client) : void{

![Screenshot_181](https://user-images.githubusercontent.com/63200545/174803741-96e8c519-397d-46d8-8b28-8b49d4f74bb8.png)


107 changes: 107 additions & 0 deletions docs/DOCS_EN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
### Creating a simple client that writes something to the chat after joining to the server

```php
<?php

use ipad54\BedrockEditionClient\address\ServerAddress;
use ipad54\BedrockEditionClient\Client;
use ipad54\BedrockEditionClient\player\LoginInfo;
use pocketmine\network\mcpe\protocol\Packet;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\TextPacket;

require_once "vendor/autoload.php";

$client = new Client(ServerAddress::create("127.0.0.1", 19132), new LoginInfo("Shoghicp"));
$client->handleDataPacket(function(Packet $packet) use($client) : void{
if($packet instanceof PlayStatusPacket && $packet->status === PlayStatusPacket::PLAYER_SPAWN){
$pk = new TextPacket();
$pk->message = "A Shoghi has fallen into the river in Lego Mojang";
$pk->type = TextPacket::TYPE_CHAT;
$pk->sourceName = $client->getLoginInfo()->getUsername();

$client->getNetworkSession()->sendDataPacket($pk);
}
});

$client->connect();

while(true){
$client->update();
}
```

![Screenshot_180](https://user-images.githubusercontent.com/63200545/174803523-0281e5c8-dc2b-414e-a524-b761754f7957.png)


### Creating spam bots

```php
<?php

use ipad54\BedrockEditionClient\address\ServerAddress;
use ipad54\BedrockEditionClient\Client;
use ipad54\BedrockEditionClient\player\LoginInfo;
use pocketmine\network\mcpe\protocol\DisconnectPacket;
use pocketmine\network\mcpe\protocol\Packet;
use pocketmine\network\mcpe\protocol\PlayStatusPacket;
use pocketmine\network\mcpe\protocol\TextPacket;

require_once "vendor/autoload.php";

/** @var Client[] $clients */
$clients = [];

for($i = 0; $i <= 50; $i++){
$client = new Client(ServerAddress::create("127.0.0.1", 19132), new LoginInfo("PMMP" . mt_rand(1, 10000)));

$client->handleDataPacket(function(Packet $packet) use ($client) : void{
if($packet instanceof DisconnectPacket){
$client->getLogger()->warning($client->getLoginInfo()->getUsername() . ": disconnected from server, reason " . $packet->message);
}elseif($packet instanceof PlayStatusPacket && $packet->status === PlayStatusPacket::PLAYER_SPAWN){
sendSpam($client);
}
});

$clients[] = $client;
}

$lastConnect = time();
/** @var array<int, int> $lastSpam */
$lastSpam = [];

while(true){
foreach($clients as $client){
if(!$client->isConnected()){
if(time() - $lastConnect > 2){
$lastConnect = time();
$client->connect();

$client->getLogger()->info($client->getLoginInfo()->getUsername() . " connected");
}
}else{
$client->update();

if($client->getNetworkSession()->getPlayer()?->isSpawned()){
$time = $lastSpam[$client->getId()] ?? time() - 1;

if(time() - $time >= 1){
$lastSpam[$client->getId()] = time();
sendSpam($client);
}
}
}
}
}

function sendSpam(Client $client) : void{
$pk = new TextPacket();
$pk->type = TextPacket::TYPE_CHAT;
$pk->sourceName = $client->getLoginInfo()->getUsername();
$pk->message = "test.pmmp.io is the best server in minecraft";

$client->getNetworkSession()->sendDataPacket($pk);
}
```

![Screenshot_181](https://user-images.githubusercontent.com/63200545/174803741-96e8c519-397d-46d8-8b28-8b49d4f74bb8.png)
4 changes: 2 additions & 2 deletions src/ipad54/BedrockEditionClient/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ public function getDataPacketHandlers() : array{

public function connect() : void{
if($this->isConnected()){
throw new \LogicException("Client is already connected!");
throw new \LogicException("Клиент уже подключился!");
}

$this->networkSession = new NetworkSession($this->serverAddress, $this->loginInfo, $this);
$this->networkSession->actuallyConnect();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public function send(string $payload, bool $immediate) : void{
$this->connection->sendEncapsulated($pk, $immediate);
}

public function close(string $reason = "unknown reason") : void{
// TODO: Implement close() method.
public function close(string $reason = "неизвестная причина") : void{
// TODO: Реализовать метод close()
}
}
}
72 changes: 36 additions & 36 deletions src/ipad54/BedrockEditionClient/network/NetworkSession.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,22 +119,22 @@ public function setHandler(?PacketHandler $handler) : void{

if($this->handler !== null){
$this->handler->setUp();
$this->logger->debug("A new packet handler has been set (".get_class($handler).")");
$this->logger->debug("Был установлен новый обработчик пакетов (".get_class($handler).")");
}
}

public function createPlayer(StartGamePacket $packet) : void{
if($this->player !== null){
throw new \LogicException("Player is already created!");
throw new \LogicException("Игрок уже создан!");
}
$this->player = new Player($this, $this->loginInfo, $packet, $this->client->getId());

$this->logger->debug("Player was created, eid: ".$this->client->getId());
$this->logger->debug("Игрок был создан, айди: ".$this->client->getId());
}

public function startEncryption(string $handshakeJwt) : void{
if($this->cipher !== null){
throw new \LogicException("Encryption is already started!");
throw new \LogicException("Шифрование уже запущено!");
}

[$header, $body] = JwtUtils::parse($handshakeJwt);
Expand All @@ -148,7 +148,7 @@ public function startEncryption(string $handshakeJwt) : void{

$this->cipher = EncryptionContext::fakeGCM($encryptionKey);

$this->logger->debug("Encryption was started, key: ".bin2hex($encryptionKey));
$this->logger->debug("Было запущено шифрование, ключ: ".bin2hex($encryptionKey));
}

public function handleEncoded(string $payload) : void{
Expand All @@ -166,7 +166,7 @@ public function handleEncoded(string $payload) : void{

public function handleDataPacket(Packet $packet, string $buffer) : void{
if(!($packet instanceof ClientboundPacket)){
throw new PacketHandlingException("Unexpected non-clientbound packet");
throw new PacketHandlingException("Неожиданный пакет, не связанный с клиентом");
}

$packet->decode(PacketSerializer::decoder($buffer, 0, $this->serializerContext));
Expand Down Expand Up @@ -199,7 +199,7 @@ public function sendDataPacket(ServerboundPacket $packet, bool $immediate = fals

public function processLogin() : void{
if($this->loggedIn){
throw new \LogicException("Client already loggedIn!");
throw new \LogicException("Клиент уже вошел в систему!");
}

[$chainDataJwt, $clientDataJwt] = $this->buildLoginData();
Expand All @@ -208,7 +208,7 @@ public function processLogin() : void{

$this->loggedIn = true;

$this->logger->debug("LoginPacket was sent, nickname: ".$this->loginInfo->getUsername());
$this->logger->debug("LoginPacket был отправлен, ник: ".$this->loginInfo->getUsername());
}

protected function buildLoginData() : array{
Expand All @@ -228,7 +228,7 @@ protected function buildLoginData() : array{
$chainDataJwt->chain = [JwtUtils::create($header, [
"exp" => time() + 3600,
"extraData" => [
"XUID" => "", //TODO: Xbox auth
"XUID" => "", //TODO: Xbox авторизация
"displayName" => $this->loginInfo->getUsername(),
"identity" => $this->loginInfo->getUuid()->toString(),
],
Expand All @@ -239,45 +239,45 @@ protected function buildLoginData() : array{
$skin = $this->loginInfo->getSkin();

$clientDataJwt = JwtUtils::create($header, [
"AnimatedImageData" => [], //TODO: Hardcoded value
"ArmSize" => "wide", //TODO: Hardcoded value
"CapeData" => "", //TODO: Hardcoded value
"CapeId" => "", //TODO: Hardcoded value
"CapeImageHeight" => 0, //TODO: Hardcoded value
"CapeImageWidth" => 0, //TODO: Hardcoded value
"CapeOnClassicSkin" => false, //TODO: Hardcoded value
"AnimatedImageData" => [], //TODO: Жестко закодировнное значение
"ArmSize" => "wide", //TODO: Жестко закодировнное значение
"CapeData" => "", //TODO: Жестко закодировнное значение
"CapeId" => "", //TODO: Жестко закодировнное значение
"CapeImageHeight" => 0, //TODO: Жестко закодировнное значение
"CapeImageWidth" => 0, //TODO: Жестко закодировнное значение
"CapeOnClassicSkin" => false, //TODO: Жестко закодировнное значение
"ClientRandomId" => $this->client->getId(),
"CurrentInputMode" => 2, //TODO: Hardcoded value
"DefaultInputMode" => 1, //TODO: Hardcoded value
"CurrentInputMode" => 2, //TODO: Жестко закодировнное значение
"DefaultInputMode" => 1, //TODO: Жестко закодировнное значение
"DeviceId" => $this->loginInfo->getDeviceId(),
"DeviceModel" => $this->loginInfo->getDeviceModel(),
"DeviceOS" => $this->loginInfo->getDeviceOS(),
"GameVersion" => ProtocolInfo::MINECRAFT_VERSION_NETWORK,
"GuiScale" => 0, //TODO: Hardcoded value
"GuiScale" => 0, //TODO: Жестко закодировнное значение
"LanguageCode" => $this->loginInfo->getLocale(),
"PersonaPieces" => [], //TODO: Hardcoded value
"PersonaSkin" => false, //TODO: Hardcoded value
"PieceTintColors" => [], //TODO: Hardcoded value
"PlatformOfflineId" => "", //TODO: Hardcoded value
"PlatformOnlineId" => "", //TODO: Hardcoded value
"PlayFabId" => "f79a424e50f4736", //TODO: Hardcoded value
"PremiumSkin" => false, //TODO: Hardcoded value
"PersonaPieces" => [], //TODO: Жестко закодировнное значение
"PersonaSkin" => false, //TODO: Жестко закодировнное значение
"PieceTintColors" => [], //TODO: Жестко закодировнное значение
"PlatformOfflineId" => "", //TODO: Жестко закодировнное значение
"PlatformOnlineId" => "", //TODO: Жестко закодировнное значение
"PlayFabId" => "f79a424e50f4736", //TODO: Жестко закодировнное значение
"PremiumSkin" => false, //TODO: Жестко закодировнное значение
"SelfSignedId" => base64_encode(random_bytes(16)),
"ServerAddress" => $this->serverAddress->getIp() . ":" . $this->serverAddress->getPort(),
"SkinAnimationData" => "", //TODO: Hardcoded value
"SkinColor" => "#b37b62", //TODO: Hardcoded value
"SkinAnimationData" => "", //TODO: Жестко закодировнное значение
"SkinColor" => "#b37b62", //TODO: Жестко закодировнное значение
"SkinData" => base64_encode($skin->getSkinData()),
"SkinGeometryData" => "", //TODO: Hardcoded value
"SkinGeometryDataEngineVersion" => "MS4xNC4w", //TODO: Hardcoded value
"SkinGeometryData" => "", //TODO: Жестко закодировнное значение
"SkinGeometryDataEngineVersion" => "MS4xNC4w", //TODO: Жестко закодировнное значение
"SkinId" => $skin->getSkinId(),
"SkinImageHeight" => 64, //TODO: Hardcoded value
"SkinImageWidth" => 64, //TODO: Hardcoded value
"SkinImageHeight" => 64, //TODO: Жестко закодировнное значение
"SkinImageWidth" => 64, //TODO: Жестко закодировнное значение
"SkinResourcePatch" => base64_encode(json_encode(["geometry" => ["default" => "geometry.humanoid.custom"]])),
"ThirdPartyName" => "", //TODO: Hardcoded value
"ThirdPartyNameOnly" => false, //TODO: Hardcoded value
"UIProfile" => 1 //TODO: Hardcoded value
"ThirdPartyName" => "", //TODO: Жестко закодировнное значение
"ThirdPartyNameOnly" => false, //TODO: Жестко закодировнное значение
"UIProfile" => 1 //TODO: Жестко закодировнное значение
], $localPriv);

return [$chainDataJwt, $clientDataJwt];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public function handlePlayStatus(PlayStatusPacket $packet) : bool{

$this->networkSession->setHandler(null);

$this->networkSession->getClient()->getLogger()->debug("Player was spawned");
$this->networkSession->getClient()->getLogger()->debug("Игрок появился в мире");
}
return true;
}
}
}
Loading