From ce473e794b5a6a63c1e366f5f0b07f95e2306d40 Mon Sep 17 00:00:00 2001 From: Benedikt05 <60869945+Benedikt05@users.noreply.github.com> Date: Thu, 25 Apr 2024 01:15:03 +0200 Subject: [PATCH] feat: 1.20.80 support (#260) --- README.md | 2 +- composer.lock | 27 +++++++------- src/pocketmine/VersionInfo.php | 4 +- .../command/defaults/HudCommand.php | 8 +++- src/pocketmine/entity/Entity.php | 4 ++ src/pocketmine/level/particle/Particle.php | 1 + .../CorrectPlayerMovePredictionPacket.php | 32 +++++++++++----- .../mcpe/protocol/CraftingDataPacket.php | 14 +++---- .../network/mcpe/protocol/ProtocolInfo.php | 4 +- .../mcpe/protocol/ResourcePackStackPacket.php | 19 ++++------ .../network/mcpe/protocol/StartGamePacket.php | 3 ++ .../protocol/UpdatePlayerGameTypePacket.php | 14 ++++--- .../mcpe/protocol/types/WindowTypes.php | 3 ++ .../mcpe/protocol/types/hud/HudElement.php | 2 + .../resources/vanilla/biome_id_map.json | 1 + .../vanilla/canonical_block_states.nbt | Bin 1987768 -> 1986233 bytes .../resources/vanilla/level_sound_id_map.json | 6 +++ .../resources/vanilla/particle_id_map.json | 3 +- .../vanilla/r12_to_current_block_map.bin | Bin 373772 -> 372435 bytes .../vanilla/r16_to_current_item_map.json | 35 ++++++++++++++++++ 20 files changed, 127 insertions(+), 55 deletions(-) diff --git a/README.md b/README.md index 1314df63..a32c8de8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@
A feature-rich server software for Minecraft: BE. This is a Fork of PocketMine-MP and Altay
This fork was created due to the inactivity of Altay.
-It contains support for Minecraft: Bedrock Edition v1.20.70
+It currently supports Minecraft: Bedrock Edition v1.20.80
diff --git a/composer.lock b/composer.lock
index b282fc78..89be8606 100644
--- a/composer.lock
+++ b/composer.lock
@@ -1354,16 +1354,16 @@
},
{
"name": "phpunit/phpunit",
- "version": "9.6.17",
+ "version": "9.6.18",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
- "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd"
+ "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1a156980d78a6666721b7e8e8502fe210b587fcd",
- "reference": "1a156980d78a6666721b7e8e8502fe210b587fcd",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04",
+ "reference": "32c2c2d6580b1d8ab3c10b1e9e4dc263cc69bb04",
"shasum": ""
},
"require": {
@@ -1437,7 +1437,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
- "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.17"
+ "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.18"
},
"funding": [
{
@@ -1453,7 +1453,7 @@
"type": "tidelift"
}
],
- "time": "2024-02-23T13:14:51+00:00"
+ "time": "2024-03-21T12:07:32+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -2257,16 +2257,16 @@
},
{
"name": "sebastian/resource-operations",
- "version": "3.0.3",
+ "version": "3.0.4",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/resource-operations.git",
- "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8"
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
- "reference": "0f4443cb3a1d92ce809899753bc0d5d5a8dd19a8",
+ "url": "https://api.github.com/repos/sebastianbergmann/resource-operations/zipball/05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
+ "reference": "05d5692a7993ecccd56a03e40cd7e5b09b1d404e",
"shasum": ""
},
"require": {
@@ -2278,7 +2278,7 @@
"type": "library",
"extra": {
"branch-alias": {
- "dev-master": "3.0-dev"
+ "dev-main": "3.0-dev"
}
},
"autoload": {
@@ -2299,8 +2299,7 @@
"description": "Provides a list of PHP built-in functions that operate on resources",
"homepage": "https://www.github.com/sebastianbergmann/resource-operations",
"support": {
- "issues": "https://github.com/sebastianbergmann/resource-operations/issues",
- "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.3"
+ "source": "https://github.com/sebastianbergmann/resource-operations/tree/3.0.4"
},
"funding": [
{
@@ -2308,7 +2307,7 @@
"type": "github"
}
],
- "time": "2020-09-28T06:45:17+00:00"
+ "time": "2024-03-14T16:00:52+00:00"
},
{
"name": "sebastian/type",
diff --git a/src/pocketmine/VersionInfo.php b/src/pocketmine/VersionInfo.php
index 74b4f3f7..00b34d58 100644
--- a/src/pocketmine/VersionInfo.php
+++ b/src/pocketmine/VersionInfo.php
@@ -34,6 +34,6 @@
const NAME = "BetterAltay";
const BASE_VERSION = "3.28.0"; //Don't change this anymore. Change the FORK_VERSION instead.
-const FORK_VERSION = "1.20.0";
-const IS_DEVELOPMENT_BUILD = true;
+const FORK_VERSION = "1.21.0";
+const IS_DEVELOPMENT_BUILD = false;
const BUILD_CHANNEL = "master";
diff --git a/src/pocketmine/command/defaults/HudCommand.php b/src/pocketmine/command/defaults/HudCommand.php
index c7e4035b..1a3f0fcb 100644
--- a/src/pocketmine/command/defaults/HudCommand.php
+++ b/src/pocketmine/command/defaults/HudCommand.php
@@ -27,7 +27,7 @@ public function __construct(string $name){
[
new CommandParameter("player", AvailableCommandsPacket::ARG_TYPE_TARGET, false),
new CommandParameter("visible", AvailableCommandsPacket::ARG_TYPE_STRING, false, new CommandEnum("HudVisibility", ["hide", "reset"])),
- new CommandParameter("hud_element", AvailableCommandsPacket::ARG_TYPE_STRING, true, new CommandEnum("HudElement", ["air_bubbles", "all", "armor", "crosshair", "health", "horse_health", "hotbar", "hunger", "paperdoll", "progress_bar", "tooltips", "touch_controls"])),
+ new CommandParameter("hud_element", AvailableCommandsPacket::ARG_TYPE_STRING, true, new CommandEnum("HudElement", ["air_bubbles", "all", "armor", "crosshair", "health", "horse_health", "hotbar", "hunger", "item_text", "paperdoll", "progress_bar", "status_effects", "tooltips", "touch_controls"])),
]
]);
$this->setPermission("altay.command.hud");
@@ -61,8 +61,10 @@ public function execute(CommandSender $sender, string $commandLabel, array $args
"horse_health" => [HudElement::VEHICLE_HEALTH],
"hotbar" => [HudElement::HOTBAR],
"hunger" => [HudElement::FOOD],
+ "item_text" => [HudElement::ITEM_TEXT_POPUP],
"paperdoll" => [HudElement::PAPER_DOLL],
"progress_bar" => [HudElement::XP],
+ "status_effects" => [HudElement::STATUS_EFFECTS],
"tooltips" => [HudElement::TOOLTIPS],
"touch_controls" => [HudElement::TOUCH_CONTROLS],
"all" => [
@@ -76,7 +78,9 @@ public function execute(CommandSender $sender, string $commandLabel, array $args
HudElement::PAPER_DOLL,
HudElement::XP,
HudElement::TOOLTIPS,
- HudElement::TOUCH_CONTROLS
+ HudElement::TOUCH_CONTROLS,
+ HudElement::STATUS_EFFECTS,
+ HudElement::ITEM_TEXT_POPUP,
],
default => throw new InvalidCommandSyntaxException()
};
diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php
index bc3a407c..d9c9fd62 100644
--- a/src/pocketmine/entity/Entity.php
+++ b/src/pocketmine/entity/Entity.php
@@ -389,6 +389,10 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
public const DATA_FLAG_FEELING_HAPPY = 112;
public const DATA_FLAG_SEARCHING = 113;
public const DATA_FLAG_CRAWLING = 114;
+ public const DATA_FLAG_TIMER_FLAG_1 = 115;
+ public const DATA_FLAG_TIMER_FLAG_2 = 116;
+ public const DATA_FLAG_TIMER_FLAG_3 = 117;
+ public const DATA_FLAG_BODY_ROTATION_BLOCKED = 118;
public const DATA_PLAYER_FLAG_SLEEP = 1;
public const DATA_PLAYER_FLAG_DEAD = 2; //TODO: CHECK
diff --git a/src/pocketmine/level/particle/Particle.php b/src/pocketmine/level/particle/Particle.php
index 9eb9899b..3a65e679 100644
--- a/src/pocketmine/level/particle/Particle.php
+++ b/src/pocketmine/level/particle/Particle.php
@@ -119,6 +119,7 @@ abstract class Particle extends Vector3{
public const TYPE_WHITE_SMOKE = 89;
public const TYPE_VAULT_CONNECTION = 90;
public const TYPE_WIND_EXPLOSION = 91;
+ public const TYPE_WOLF_ARMOR_CRACK = 92;
/**
* @return DataPacket|DataPacket[]
diff --git a/src/pocketmine/network/mcpe/protocol/CorrectPlayerMovePredictionPacket.php b/src/pocketmine/network/mcpe/protocol/CorrectPlayerMovePredictionPacket.php
index 2a9ac55a..3685608a 100644
--- a/src/pocketmine/network/mcpe/protocol/CorrectPlayerMovePredictionPacket.php
+++ b/src/pocketmine/network/mcpe/protocol/CorrectPlayerMovePredictionPacket.php
@@ -32,21 +32,23 @@ class CorrectPlayerMovePredictionPacket extends DataPacket/* implements Clientbo
{
public const NETWORK_ID = ProtocolInfo::CORRECT_PLAYER_MOVE_PREDICTION_PACKET;
- /** @var Vector3 */
- private $position;
- /** @var Vector3 */
- private $delta;
- /** @var bool */
- private $onGround;
- /** @var int */
- private $tick;
-
- public static function create(Vector3 $position, Vector3 $delta, bool $onGround, int $tick) : self{
+ public const PREDICTION_TYPE_VEHICLE = 0;
+ public const PREDICTION_TYPE_PLAYER = 1;
+
+ private Vector3 $position;
+ private Vector3 $delta;
+ private bool $onGround;
+ private int $tick;
+ private int $predictionType;
+
+
+ public static function create(Vector3 $position, Vector3 $delta, bool $onGround, int $tick, int $predictionType) : self{
$result = new self;
$result->position = $position;
$result->delta = $delta;
$result->onGround = $onGround;
$result->tick = $tick;
+ $result->predictionType = $predictionType;
return $result;
}
@@ -58,11 +60,17 @@ public function isOnGround() : bool{ return $this->onGround; }
public function getTick() : int{ return $this->tick; }
+ public function getPredictionType() : int{ return $this->predictionType; }
+
protected function decodePayload() : void{
$this->position = $this->getVector3();
$this->delta = $this->getVector3();
$this->onGround = $this->getBool();
$this->tick = $this->getUnsignedVarLong();
+ $this->predictionType = $this->getByte();
+
+ //$this->getLFloat();
+ //$this->getLFloat();
}
protected function encodePayload() : void{
@@ -70,6 +78,10 @@ protected function encodePayload() : void{
$this->putVector3($this->delta);
$this->putBool($this->onGround);
$this->putUnsignedVarLong($this->tick);
+ $this->putByte($this->predictionType);
+ //TODO: vehicleRotation
+ //$this->putLFloat();
+ //$this->putLFloat();
}
public function handle(NetworkSession $session) : bool{
diff --git a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php
index d386c033..ddd9feea 100644
--- a/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php
+++ b/src/pocketmine/network/mcpe/protocol/CraftingDataPacket.php
@@ -58,18 +58,17 @@ class CraftingDataPacket extends DataPacket{
public const ENTRY_SHAPED_CHEMISTRY = 7; //TODO
/** @var object[] */
- public $entries = [];
+ public array $entries = [];
/** @var PotionTypeRecipe[] */
- public $potionTypeRecipes = [];
+ public array $potionTypeRecipes = [];
/** @var PotionContainerChangeRecipe[] */
- public $potionContainerRecipes = [];
+ public array $potionContainerRecipes = [];
/** @var MaterialReducerRecipe[] */
- public $materialReducerRecipes = [];
- /** @var bool */
- public $cleanRecipes = false;
+ public array $materialReducerRecipes = [];
+ public bool $cleanRecipes = false;
/** @var mixed[][] */
- public $decodedEntries = [];
+ public array $decodedEntries = [];
public function clean(){
$this->entries = [];
@@ -247,6 +246,7 @@ private static function writeShapedRecipe(ShapedRecipe $recipe, NetworkBinaryStr
$stream->put(str_repeat("\x00", 16)); //Null UUID
$stream->putString("crafting_table"); //TODO: blocktype (no prefix) (this might require internal API breaks)
$stream->putVarInt(50); //TODO: priority
+ $stream->putBool(true); //TODO: assume symmetry
$stream->putUnsignedVarInt($pos); //TODO: ANOTHER recipe ID, only used on the network
return CraftingDataPacket::ENTRY_SHAPED;
diff --git a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php
index ddbd6023..d4d9b922 100644
--- a/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php
+++ b/src/pocketmine/network/mcpe/protocol/ProtocolInfo.php
@@ -37,11 +37,11 @@ interface ProtocolInfo{
*/
/** Actual Minecraft: PE protocol version */
- public const CURRENT_PROTOCOL = 662;
+ public const CURRENT_PROTOCOL = 671;
/** Current Minecraft PE version reported by the server. This is usually the earliest currently supported version. */
public const MINECRAFT_VERSION = 'v' . self::MINECRAFT_VERSION_NETWORK;
/** Version number sent to clients in ping responses. */
- public const MINECRAFT_VERSION_NETWORK = '1.20.70';
+ public const MINECRAFT_VERSION_NETWORK = '1.20.80';
public const LOGIN_PACKET = 0x01;
public const PLAY_STATUS_PACKET = 0x02;
diff --git a/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php
index 58a676ae..4b02c4dc 100644
--- a/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php
+++ b/src/pocketmine/network/mcpe/protocol/ResourcePackStackPacket.php
@@ -33,19 +33,14 @@
class ResourcePackStackPacket extends DataPacket{
public const NETWORK_ID = ProtocolInfo::RESOURCE_PACK_STACK_PACKET;
- /** @var bool */
- public $mustAccept = false;
-
+ public bool $mustAccept = false;
/** @var ResourcePack[] */
- public $behaviorPackStack = [];
+ public array $behaviorPackStack = [];
/** @var ResourcePack[] */
- public $resourcePackStack = [];
-
- /** @var string */
- public $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
-
- /** @var Experiments */
- public $experiments;
+ public array $resourcePackStack = [];
+ public string $baseGameVersion = ProtocolInfo::MINECRAFT_VERSION_NETWORK;
+ public Experiments $experiments;
+ public bool $includeEditorPacks = false;
protected function decodePayload(){
$this->mustAccept = $this->getBool();
@@ -65,6 +60,7 @@ protected function decodePayload(){
$this->baseGameVersion = $this->getString();
$this->experiments = Experiments::read($this);
+ $this->includeEditorPacks = $this->getBool();
}
protected function encodePayload(){
@@ -86,6 +82,7 @@ protected function encodePayload(){
$this->putString($this->baseGameVersion);
$this->experiments->write($this);
+ $this->putBool($this->includeEditorPacks);
}
public function handle(NetworkSession $session) : bool{
diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php
index 2f01e8fd..fabb66e8 100644
--- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php
+++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php
@@ -59,6 +59,7 @@ class StartGamePacket extends DataPacket{
public SpawnSettings $spawnSettings;
public int $generator = GeneratorType::OVERWORLD;
public int $worldGamemode;
+ public bool $hardcoreMode = false;
public int $difficulty;
public int $spawnX;
public int $spawnY;
@@ -157,6 +158,7 @@ protected function decodePayload(){
$this->spawnSettings = SpawnSettings::read($this);
$this->generator = $this->getVarInt();
$this->worldGamemode = $this->getVarInt();
+ $this->hardcoreMode = $this->getBool();
$this->difficulty = $this->getVarInt();
$this->getBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
$this->hasAchievementsDisabled = $this->getBool();
@@ -256,6 +258,7 @@ protected function encodePayload(){
$this->spawnSettings->write($this);
$this->putVarInt($this->generator);
$this->putVarInt($this->worldGamemode);
+ $this->putBool($this->hardcoreMode);
$this->putVarInt($this->difficulty);
$this->putBlockPosition($this->spawnX, $this->spawnY, $this->spawnZ);
$this->putBool($this->hasAchievementsDisabled);
diff --git a/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php b/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php
index bb2e3dc9..dc6f18ba 100644
--- a/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php
+++ b/src/pocketmine/network/mcpe/protocol/UpdatePlayerGameTypePacket.php
@@ -33,18 +33,18 @@ class UpdatePlayerGameTypePacket extends DataPacket/* implements ClientboundPack
public const NETWORK_ID = ProtocolInfo::UPDATE_PLAYER_GAME_TYPE_PACKET;
/**
- * @var int
* @see GameMode
*/
- private $gameMode;
+ private int $gameMode;
+ private int $playerEntityUniqueId;
+ private int $tick;
- /** @var int */
- private $playerEntityUniqueId;
- public static function create(int $gameMode, int $playerEntityUniqueId) : self{
+ public static function create(int $gameMode, int $playerEntityUniqueId, int $tick) : self{
$result = new self;
$result->gameMode = $gameMode;
$result->playerEntityUniqueId = $playerEntityUniqueId;
+ $result->tick = $tick;
return $result;
}
@@ -52,14 +52,18 @@ public function getGameMode() : int{ return $this->gameMode; }
public function getPlayerEntityUniqueId() : int{ return $this->playerEntityUniqueId; }
+ public function getTick() : int{ return $this->tick; }
+
protected function decodePayload() : void{
$this->gameMode = $this->getVarInt();
$this->playerEntityUniqueId = $this->getEntityUniqueId();
+ $this->tick = $this->getUnsignedVarInt();
}
protected function encodePayload() : void{
$this->putVarInt($this->gameMode);
$this->putEntityUniqueId($this->playerEntityUniqueId);
+ $this->putUnsignedVarInt($this->tick);
}
public function handle(NetworkSession $session) : bool{
diff --git a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php
index 583a3384..0758d653 100644
--- a/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php
+++ b/src/pocketmine/network/mcpe/protocol/types/WindowTypes.php
@@ -62,5 +62,8 @@ interface WindowTypes{
public const HUD = 31;
public const JIGSAW_EDITOR = 32;
public const SMITHING_TABLE = 33;
+ public const CHEST_BOAT = 34;
+ public const DECORATED_POT = 35;
+ public const CRAFTER = 36;
}
diff --git a/src/pocketmine/network/mcpe/protocol/types/hud/HudElement.php b/src/pocketmine/network/mcpe/protocol/types/hud/HudElement.php
index 7ab6b470..6527eb8c 100644
--- a/src/pocketmine/network/mcpe/protocol/types/hud/HudElement.php
+++ b/src/pocketmine/network/mcpe/protocol/types/hud/HudElement.php
@@ -22,4 +22,6 @@ private function __construct(){
public const FOOD = 8;
public const AIR_BUBBLES = 9;
public const VEHICLE_HEALTH = 10;
+ public const STATUS_EFFECTS = 11;
+ public const ITEM_TEXT_POPUP = 12;
}
diff --git a/src/pocketmine/resources/vanilla/biome_id_map.json b/src/pocketmine/resources/vanilla/biome_id_map.json
index aef89842..db32b25b 100644
--- a/src/pocketmine/resources/vanilla/biome_id_map.json
+++ b/src/pocketmine/resources/vanilla/biome_id_map.json
@@ -7,6 +7,7 @@
"birch_forest_hills": 28,
"birch_forest_hills_mutated": 156,
"birch_forest_mutated": 155,
+ "cherry_grove": 192,
"cold_beach": 26,
"cold_ocean": 44,
"cold_taiga": 30,
diff --git a/src/pocketmine/resources/vanilla/canonical_block_states.nbt b/src/pocketmine/resources/vanilla/canonical_block_states.nbt
index c1cbd97c2f3ac690c17014a1aec4b27ed5a746f7..83c833fa3628d14704019a6309ad8af7243d13cb 100644
GIT binary patch
literal 1986233
zcmd?Se~;wImiJZFRkymf=2y?$xpQZFXJ_}>XJHqF03n3X2qA ?k-4Fes@9AqTD6SC*Grd7G_jl_<0fbVt^8$FZ`4cf8oc(
z2n;{x@dm@sh*B7SQmn)9^TH&C9~EOU{G1?-;RnTe3_mMWWcYEpO$O;~*xN8+GK=Kp
zH5n)+t;rxMiA@HH$!s!6PF9maGIE;?lGK99AbH7628qgSGDuEplR<*An+%fGfXN_n
zQ6}TMi9zw>Dp--B$3=JyKO{h7=pi8%Lr;m37<$a(9flqer7-lMSc9R*g$WEjDaK#u
zF+uu5&xx}adQ_;q(9=@83sTYyxC;`O)?J{GtnPw@ i>hs_yRefuFn;XgYS#7rG6P0`+}M6WFVee3RV8;-ljcjrk`IEOFt#J;@A{Qq
zrS-d{O5Y_#b@NgopRMzvNb4m1kT<7~LO~of$OptAzX*f;U5}G4yX`LR%DKGR?0QXp
zQ$ACjex9~%y1dO+$s+IesW%_*j3;M*xv8se*W13W-!V^4Uq6i}eP9n1z^m8+oW~B}
zB6a|mu>-h@9e~sYuVQzBS^!cPs0AQ(fm#4k7pMgwb%9y{QWu=Z?gF&{q%KekK@G-7a(6+Z^1BO?7UeGE
zUuQ6{8!!C42zxO=3D6gQN{GMkV`2n`pYwQw;b%lC3_mH>VfcAr62p&*u^4_%kjC(X
z;yi|*6)H0PxZEa#bT;g5m@t_|^75Jtl#