mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-10 21:45:35 +00:00
Merge branch 'next-minor' into next-major
This commit is contained in:
commit
2a81a421f3
@ -113,20 +113,12 @@ class MemoryManager{
|
|||||||
if($m <= 0){
|
if($m <= 0){
|
||||||
$defaultMemory = 0;
|
$defaultMemory = 0;
|
||||||
}else{
|
}else{
|
||||||
switch(mb_strtoupper($matches[2])){
|
$defaultMemory = match(mb_strtoupper($matches[2])){
|
||||||
case "K":
|
"K" => intdiv($m, 1024),
|
||||||
$defaultMemory = intdiv($m, 1024);
|
"M" => $m,
|
||||||
break;
|
"G" => $m * 1024,
|
||||||
case "M":
|
default => $m,
|
||||||
$defaultMemory = $m;
|
};
|
||||||
break;
|
|
||||||
case "G":
|
|
||||||
$defaultMemory = $m * 1024;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$defaultMemory = $m;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,10 +49,7 @@ use pocketmine\lang\KnownTranslationFactory;
|
|||||||
use pocketmine\lang\Language;
|
use pocketmine\lang\Language;
|
||||||
use pocketmine\lang\LanguageNotFoundException;
|
use pocketmine\lang\LanguageNotFoundException;
|
||||||
use pocketmine\lang\Translatable;
|
use pocketmine\lang\Translatable;
|
||||||
use pocketmine\nbt\BigEndianNbtSerializer;
|
|
||||||
use pocketmine\nbt\NbtDataException;
|
|
||||||
use pocketmine\nbt\tag\CompoundTag;
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
use pocketmine\nbt\TreeRoot;
|
|
||||||
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
use pocketmine\network\mcpe\compression\CompressBatchPromise;
|
||||||
use pocketmine\network\mcpe\compression\CompressBatchTask;
|
use pocketmine\network\mcpe\compression\CompressBatchTask;
|
||||||
use pocketmine\network\mcpe\compression\Compressor;
|
use pocketmine\network\mcpe\compression\Compressor;
|
||||||
@ -72,9 +69,13 @@ use pocketmine\network\query\QueryInfo;
|
|||||||
use pocketmine\network\upnp\UPnPNetworkInterface;
|
use pocketmine\network\upnp\UPnPNetworkInterface;
|
||||||
use pocketmine\permission\BanList;
|
use pocketmine\permission\BanList;
|
||||||
use pocketmine\permission\DefaultPermissions;
|
use pocketmine\permission\DefaultPermissions;
|
||||||
|
use pocketmine\player\DatFilePlayerDataProvider;
|
||||||
use pocketmine\player\GameMode;
|
use pocketmine\player\GameMode;
|
||||||
use pocketmine\player\OfflinePlayer;
|
use pocketmine\player\OfflinePlayer;
|
||||||
use pocketmine\player\Player;
|
use pocketmine\player\Player;
|
||||||
|
use pocketmine\player\PlayerDataLoadException;
|
||||||
|
use pocketmine\player\PlayerDataProvider;
|
||||||
|
use pocketmine\player\PlayerDataSaveException;
|
||||||
use pocketmine\player\PlayerInfo;
|
use pocketmine\player\PlayerInfo;
|
||||||
use pocketmine\plugin\PharPluginLoader;
|
use pocketmine\plugin\PharPluginLoader;
|
||||||
use pocketmine\plugin\Plugin;
|
use pocketmine\plugin\Plugin;
|
||||||
@ -161,12 +162,9 @@ use function time;
|
|||||||
use function touch;
|
use function touch;
|
||||||
use function trim;
|
use function trim;
|
||||||
use function yaml_parse;
|
use function yaml_parse;
|
||||||
use function zlib_decode;
|
|
||||||
use function zlib_encode;
|
|
||||||
use const DIRECTORY_SEPARATOR;
|
use const DIRECTORY_SEPARATOR;
|
||||||
use const PHP_EOL;
|
use const PHP_EOL;
|
||||||
use const PHP_INT_MAX;
|
use const PHP_INT_MAX;
|
||||||
use const ZLIB_ENCODING_GZIP;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The class that manages everything
|
* The class that manages everything
|
||||||
@ -251,6 +249,8 @@ class Server{
|
|||||||
private string $dataPath;
|
private string $dataPath;
|
||||||
private string $pluginPath;
|
private string $pluginPath;
|
||||||
|
|
||||||
|
private PlayerDataProvider $playerDataProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var string[]
|
* @var string[]
|
||||||
* @phpstan-var array<string, string>
|
* @phpstan-var array<string, string>
|
||||||
@ -481,49 +481,22 @@ class Server{
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPlayerDataPath(string $username) : string{
|
|
||||||
return Path::join($this->getDataPath(), 'players', strtolower($username) . '.dat');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the server has stored any saved data for this player.
|
* Returns whether the server has stored any saved data for this player.
|
||||||
*/
|
*/
|
||||||
public function hasOfflinePlayerData(string $name) : bool{
|
public function hasOfflinePlayerData(string $name) : bool{
|
||||||
return file_exists($this->getPlayerDataPath($name));
|
return $this->playerDataProvider->hasData($name);
|
||||||
}
|
|
||||||
|
|
||||||
private function handleCorruptedPlayerData(string $name) : void{
|
|
||||||
$path = $this->getPlayerDataPath($name);
|
|
||||||
rename($path, $path . '.bak');
|
|
||||||
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getOfflinePlayerData(string $name) : ?CompoundTag{
|
public function getOfflinePlayerData(string $name) : ?CompoundTag{
|
||||||
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
|
return Timings::$syncPlayerDataLoad->time(function() use ($name) : ?CompoundTag{
|
||||||
$name = strtolower($name);
|
try{
|
||||||
$path = $this->getPlayerDataPath($name);
|
return $this->playerDataProvider->loadData($name);
|
||||||
|
}catch(PlayerDataLoadException $e){
|
||||||
if(file_exists($path)){
|
$this->logger->debug("Failed to load player data for $name: " . $e->getMessage());
|
||||||
$contents = @file_get_contents($path);
|
$this->logger->error($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_playerCorrupted($name)));
|
||||||
if($contents === false){
|
return null;
|
||||||
throw new \RuntimeException("Failed to read player data file \"$path\" (permission denied?)");
|
|
||||||
}
|
|
||||||
$decompressed = @zlib_decode($contents);
|
|
||||||
if($decompressed === false){
|
|
||||||
$this->logger->debug("Failed to decompress raw player data for \"$name\"");
|
|
||||||
$this->handleCorruptedPlayerData($name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
try{
|
|
||||||
return (new BigEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag();
|
|
||||||
}catch(NbtDataException $e){ //corrupt data
|
|
||||||
$this->logger->debug("Failed to decode NBT data for \"$name\": " . $e->getMessage());
|
|
||||||
$this->handleCorruptedPlayerData($name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -537,11 +510,9 @@ class Server{
|
|||||||
|
|
||||||
if(!$ev->isCancelled()){
|
if(!$ev->isCancelled()){
|
||||||
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
Timings::$syncPlayerDataSave->time(function() use ($name, $ev) : void{
|
||||||
$nbt = new BigEndianNbtSerializer();
|
|
||||||
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($ev->getSaveData())), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
|
|
||||||
try{
|
try{
|
||||||
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
$this->playerDataProvider->saveData($name, $ev->getSaveData());
|
||||||
}catch(\RuntimeException $e){
|
}catch(PlayerDataSaveException $e){
|
||||||
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
$this->logger->critical($this->getLanguage()->translate(KnownTranslationFactory::pocketmine_data_saveError($name, $e->getMessage())));
|
||||||
$this->logger->logException($e);
|
$this->logger->logException($e);
|
||||||
}
|
}
|
||||||
@ -1004,6 +975,8 @@ class Server{
|
|||||||
|
|
||||||
$this->queryInfo = new QueryInfo($this);
|
$this->queryInfo = new QueryInfo($this);
|
||||||
|
|
||||||
|
$this->playerDataProvider = new DatFilePlayerDataProvider(Path::join($this->dataPath, "players"));
|
||||||
|
|
||||||
register_shutdown_function([$this, "crashDump"]);
|
register_shutdown_function([$this, "crashDump"]);
|
||||||
|
|
||||||
$loadErrorCount = 0;
|
$loadErrorCount = 0;
|
||||||
|
@ -154,7 +154,7 @@ final class EntityFactory{
|
|||||||
}, ['Snowball', 'minecraft:snowball']);
|
}, ['Snowball', 'minecraft:snowball']);
|
||||||
|
|
||||||
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
$this->register(SplashPotion::class, function(World $world, CompoundTag $nbt) : SplashPotion{
|
||||||
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER));
|
$potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort(SplashPotion::TAG_POTION_ID, PotionTypeIds::WATER));
|
||||||
if($potionType === null){
|
if($potionType === null){
|
||||||
throw new SavedDataLoadingException("No such potion type");
|
throw new SavedDataLoadingException("No such potion type");
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,8 @@ use function sqrt;
|
|||||||
|
|
||||||
class SplashPotion extends Throwable{
|
class SplashPotion extends Throwable{
|
||||||
|
|
||||||
|
public const TAG_POTION_ID = "PotionId"; //TAG_Short
|
||||||
|
|
||||||
public static function getNetworkTypeId() : string{ return EntityIds::SPLASH_POTION; }
|
public static function getNetworkTypeId() : string{ return EntityIds::SPLASH_POTION; }
|
||||||
|
|
||||||
protected bool $linger = false;
|
protected bool $linger = false;
|
||||||
@ -64,7 +66,7 @@ class SplashPotion extends Throwable{
|
|||||||
|
|
||||||
public function saveNBT() : CompoundTag{
|
public function saveNBT() : CompoundTag{
|
||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setShort("PotionId", PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
|
$nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->getPotionType()));
|
||||||
|
|
||||||
return $nbt;
|
return $nbt;
|
||||||
}
|
}
|
||||||
|
101
src/player/DatFilePlayerDataProvider.php
Normal file
101
src/player/DatFilePlayerDataProvider.php
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\player;
|
||||||
|
|
||||||
|
use pocketmine\errorhandler\ErrorToExceptionHandler;
|
||||||
|
use pocketmine\nbt\BigEndianNbtSerializer;
|
||||||
|
use pocketmine\nbt\NbtDataException;
|
||||||
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
use pocketmine\nbt\TreeRoot;
|
||||||
|
use pocketmine\utils\Filesystem;
|
||||||
|
use pocketmine\utils\Utils;
|
||||||
|
use Symfony\Component\Filesystem\Path;
|
||||||
|
use function file_exists;
|
||||||
|
use function file_get_contents;
|
||||||
|
use function rename;
|
||||||
|
use function strtolower;
|
||||||
|
use function zlib_decode;
|
||||||
|
use function zlib_encode;
|
||||||
|
use const ZLIB_ENCODING_GZIP;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores player data in a single .dat file per player. Each file is gzipped big-endian NBT.
|
||||||
|
*/
|
||||||
|
final class DatFilePlayerDataProvider implements PlayerDataProvider{
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
private string $path
|
||||||
|
){}
|
||||||
|
|
||||||
|
private function getPlayerDataPath(string $username) : string{
|
||||||
|
return Path::join($this->path, strtolower($username) . '.dat');
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleCorruptedPlayerData(string $name) : void{
|
||||||
|
$path = $this->getPlayerDataPath($name);
|
||||||
|
rename($path, $path . '.bak');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasData(string $name) : bool{
|
||||||
|
return file_exists($this->getPlayerDataPath($name));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadData(string $name) : ?CompoundTag{
|
||||||
|
$name = strtolower($name);
|
||||||
|
$path = $this->getPlayerDataPath($name);
|
||||||
|
|
||||||
|
if(!file_exists($path)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
$contents = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => file_get_contents($path));
|
||||||
|
}catch(\ErrorException $e){
|
||||||
|
throw new PlayerDataLoadException("Failed to read player data file \"$path\": " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
$decompressed = ErrorToExceptionHandler::trapAndRemoveFalse(fn() => zlib_decode($contents));
|
||||||
|
}catch(\ErrorException $e){
|
||||||
|
$this->handleCorruptedPlayerData($name);
|
||||||
|
throw new PlayerDataLoadException("Failed to decompress raw player data for \"$name\": " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
return (new BigEndianNbtSerializer())->read($decompressed)->mustGetCompoundTag();
|
||||||
|
}catch(NbtDataException $e){ //corrupt data
|
||||||
|
$this->handleCorruptedPlayerData($name);
|
||||||
|
throw new PlayerDataLoadException("Failed to decode NBT data for \"$name\": " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveData(string $name, CompoundTag $data) : void{
|
||||||
|
$nbt = new BigEndianNbtSerializer();
|
||||||
|
$contents = Utils::assumeNotFalse(zlib_encode($nbt->write(new TreeRoot($data)), ZLIB_ENCODING_GZIP), "zlib_encode() failed unexpectedly");
|
||||||
|
try{
|
||||||
|
Filesystem::safeFilePutContents($this->getPlayerDataPath($name), $contents);
|
||||||
|
}catch(\RuntimeException $e){
|
||||||
|
throw new PlayerDataSaveException("Failed to write player data file: " . $e->getMessage(), 0, $e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
src/player/PlayerDataLoadException.php
Normal file
28
src/player/PlayerDataLoadException.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\player;
|
||||||
|
|
||||||
|
final class PlayerDataLoadException extends \RuntimeException{
|
||||||
|
|
||||||
|
}
|
52
src/player/PlayerDataProvider.php
Normal file
52
src/player/PlayerDataProvider.php
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\player;
|
||||||
|
|
||||||
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles storage of player data. Implementations must treat player names in a case-insensitive manner.
|
||||||
|
*/
|
||||||
|
interface PlayerDataProvider{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether there are any data associated with the given player name.
|
||||||
|
*/
|
||||||
|
public function hasData(string $name) : bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data associated with the given player name, or null if there is no data.
|
||||||
|
* TODO: we need an async version of this
|
||||||
|
*
|
||||||
|
* @throws PlayerDataLoadException
|
||||||
|
*/
|
||||||
|
public function loadData(string $name) : ?CompoundTag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves data for the give player name.
|
||||||
|
*
|
||||||
|
* @throws PlayerDataSaveException
|
||||||
|
*/
|
||||||
|
public function saveData(string $name, CompoundTag $data) : void;
|
||||||
|
}
|
28
src/player/PlayerDataSaveException.php
Normal file
28
src/player/PlayerDataSaveException.php
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* ____ _ _ __ __ _ __ __ ____
|
||||||
|
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||||
|
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||||
|
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||||
|
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* @author PocketMine Team
|
||||||
|
* @link http://www.pocketmine.net/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace pocketmine\player;
|
||||||
|
|
||||||
|
final class PlayerDataSaveException extends \RuntimeException{
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user