mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 16:51:42 +00:00
Implement difficulty per-world (#878)
* Moved Server::getDifficultyFromString() to Level * Added ability to set difficulty in worlds section of pocketmine.yml for generation
This commit is contained in:
parent
e64076ec81
commit
38fad4b963
@ -803,6 +803,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
|
||||
$this->usedChunks = [];
|
||||
$this->level->sendTime($this);
|
||||
$this->level->sendDifficulty($this);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1924,7 +1925,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
$pk->seed = -1;
|
||||
$pk->dimension = DimensionIds::OVERWORLD; //TODO: implement this properly
|
||||
$pk->worldGamemode = Player::getClientFriendlyGamemode($this->server->getGamemode());
|
||||
$pk->difficulty = $this->server->getDifficulty();
|
||||
$pk->difficulty = $this->level->getDifficulty();
|
||||
$pk->spawnX = $spawnPosition->getFloorX();
|
||||
$pk->spawnY = $spawnPosition->getFloorY();
|
||||
$pk->spawnZ = $spawnPosition->getFloorZ();
|
||||
@ -2427,7 +2428,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
|
||||
}elseif($target instanceof Player){
|
||||
if(($target->getGamemode() & 0x01) > 0){
|
||||
return true;
|
||||
}elseif($this->server->getConfigBoolean("pvp") !== true or $this->server->getDifficulty() === 0){
|
||||
}elseif($this->server->getConfigBoolean("pvp") !== true or $this->level->getDifficulty() === Level::DIFFICULTY_PEACEFUL){
|
||||
$cancelled = true;
|
||||
}
|
||||
|
||||
|
@ -515,36 +515,17 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @deprecated Moved to {@link Level#getDifficultyFromString}
|
||||
*
|
||||
* @param string $str
|
||||
* @return int
|
||||
*/
|
||||
public static function getDifficultyFromString(string $str) : int{
|
||||
switch(strtolower(trim($str))){
|
||||
case "0":
|
||||
case "peaceful":
|
||||
case "p":
|
||||
return 0;
|
||||
|
||||
case "1":
|
||||
case "easy":
|
||||
case "e":
|
||||
return 1;
|
||||
|
||||
case "2":
|
||||
case "normal":
|
||||
case "n":
|
||||
return 2;
|
||||
|
||||
case "3":
|
||||
case "hard":
|
||||
case "h":
|
||||
return 3;
|
||||
}
|
||||
return -1;
|
||||
return Level::getDifficultyFromString($str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Server global difficulty. Note that this may be overridden in individual Levels.
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int{
|
||||
@ -1589,8 +1570,8 @@ class Server{
|
||||
$this->logger->warning($this->getLanguage()->translateString("pocketmine.server.authProperty", ["enable", "true"]));
|
||||
}
|
||||
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||
$this->setConfigInt("difficulty", 3);
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < Level::DIFFICULTY_HARD){
|
||||
$this->setConfigInt("difficulty", Level::DIFFICULTY_HARD);
|
||||
}
|
||||
|
||||
if(\pocketmine\DEBUG >= 0){
|
||||
@ -1671,21 +1652,23 @@ class Server{
|
||||
|
||||
foreach((array) $this->getProperty("worlds", []) as $name => $worldSetting){
|
||||
if($this->loadLevel($name) === false){
|
||||
$seed = $this->getProperty("worlds.$name.seed", time());
|
||||
$options = $this->getProperty("worlds.$name");
|
||||
|
||||
$seed = $this->getProperty($options["seed"], time());
|
||||
if(is_string($seed) and !is_numeric($seed)){
|
||||
$seed = Utils::javaStringHash($seed);
|
||||
}elseif(!is_int($seed)){
|
||||
$seed = (int) $seed;
|
||||
}
|
||||
|
||||
$options = explode(":", $this->getProperty("worlds.$name.generator", Generator::getGenerator("default")));
|
||||
$generator = Generator::getGenerator(array_shift($options));
|
||||
if(count($options) > 0){
|
||||
$options = [
|
||||
"preset" => implode(":", $options)
|
||||
];
|
||||
if(isset($options["generator"])){
|
||||
$generatorOptions = explode(":", $options["generator"]);
|
||||
$generator = Generator::getGenerator(array_shift($generatorOptions));
|
||||
if(count($options) > 0){
|
||||
$options["preset"] = implode(":", $generatorOptions);
|
||||
}
|
||||
}else{
|
||||
$options = [];
|
||||
$generator = Generator::getGenerator("default");
|
||||
}
|
||||
|
||||
$this->generateLevel($name, $seed, $generator, $options);
|
||||
@ -2006,8 +1989,8 @@ class Server{
|
||||
$this->properties->reload();
|
||||
$this->maxPlayers = $this->getConfigInt("max-players", 20);
|
||||
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||
$this->setConfigInt("difficulty", 3);
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < Level::DIFFICULTY_HARD){
|
||||
$this->setConfigInt("difficulty", Level::DIFFICULTY_HARD);
|
||||
}
|
||||
|
||||
$this->banByIP->load();
|
||||
|
@ -27,8 +27,7 @@ use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\command\utils\InvalidCommandSyntaxException;
|
||||
use pocketmine\event\TranslationContainer;
|
||||
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\level\Level;
|
||||
|
||||
class DifficultyCommand extends VanillaCommand{
|
||||
|
||||
@ -50,18 +49,19 @@ class DifficultyCommand extends VanillaCommand{
|
||||
throw new InvalidCommandSyntaxException();
|
||||
}
|
||||
|
||||
$difficulty = Server::getDifficultyFromString($args[0]);
|
||||
$difficulty = Level::getDifficultyFromString($args[0]);
|
||||
|
||||
if($sender->getServer()->isHardcore()){
|
||||
$difficulty = 3;
|
||||
$difficulty = Level::DIFFICULTY_HARD;
|
||||
}
|
||||
|
||||
if($difficulty !== -1){
|
||||
$sender->getServer()->setConfigInt("difficulty", $difficulty);
|
||||
|
||||
$pk = new SetDifficultyPacket();
|
||||
$pk->difficulty = $sender->getServer()->getDifficulty();
|
||||
$sender->getServer()->broadcastPacket($sender->getServer()->getOnlinePlayers(), $pk);
|
||||
//TODO: add per-world support
|
||||
foreach($sender->getServer()->getLevels() as $level){
|
||||
$level->setDifficulty($difficulty);
|
||||
}
|
||||
|
||||
Command::broadcastCommandMessage($sender, new TranslationContainer("commands.difficulty.success", [$difficulty]));
|
||||
}else{
|
||||
|
@ -391,14 +391,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
if($this->isAlive()){
|
||||
$food = $this->getFood();
|
||||
$health = $this->getHealth();
|
||||
$difficulty = $this->server->getDifficulty();
|
||||
$difficulty = $this->level->getDifficulty();
|
||||
|
||||
$this->foodTickTimer += $tickDiff;
|
||||
if($this->foodTickTimer >= 80){
|
||||
$this->foodTickTimer = 0;
|
||||
}
|
||||
|
||||
if($difficulty === 0 and $this->foodTickTimer % 10 === 0){ //Peaceful
|
||||
if($difficulty === Level::DIFFICULTY_PEACEFUL and $this->foodTickTimer % 10 === 0){
|
||||
if($food < 20){
|
||||
$this->addFood(1.0);
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
if($e !== null){
|
||||
if($e->isOnFire() > 0){
|
||||
$this->setOnFire(2 * $this->server->getDifficulty());
|
||||
$this->setOnFire(2 * $this->level->getDifficulty());
|
||||
}
|
||||
|
||||
$deltaX = $this->x - $e->x;
|
||||
|
@ -77,6 +77,7 @@ use pocketmine\network\mcpe\protocol\BatchPacket;
|
||||
use pocketmine\network\mcpe\protocol\DataPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetDifficultyPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetTimePacket;
|
||||
use pocketmine\network\mcpe\protocol\UpdateBlockPacket;
|
||||
use pocketmine\Player;
|
||||
@ -112,6 +113,11 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
const TIME_FULL = 24000;
|
||||
|
||||
const DIFFICULTY_PEACEFUL = 0;
|
||||
const DIFFICULTY_EASY = 1;
|
||||
const DIFFICULTY_NORMAL = 2;
|
||||
const DIFFICULTY_HARD = 3;
|
||||
|
||||
/** @var Tile[] */
|
||||
private $tiles = [];
|
||||
|
||||
@ -224,6 +230,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
private $closed = false;
|
||||
|
||||
|
||||
|
||||
public static function chunkHash(int $x, int $z){
|
||||
return (($x & 0xFFFFFFFF) << 32) | ($z & 0xFFFFFFFF);
|
||||
}
|
||||
@ -259,6 +267,36 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @return int
|
||||
*/
|
||||
public static function getDifficultyFromString(string $str) : int{
|
||||
switch(strtolower(trim($str))){
|
||||
case "0":
|
||||
case "peaceful":
|
||||
case "p":
|
||||
return Level::DIFFICULTY_PEACEFUL;
|
||||
|
||||
case "1":
|
||||
case "easy":
|
||||
case "e":
|
||||
return Level::DIFFICULTY_EASY;
|
||||
|
||||
case "2":
|
||||
case "normal":
|
||||
case "n":
|
||||
return Level::DIFFICULTY_NORMAL;
|
||||
|
||||
case "3":
|
||||
case "hard":
|
||||
case "h":
|
||||
return Level::DIFFICULTY_HARD;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init the default level data
|
||||
*
|
||||
@ -2742,6 +2780,37 @@ class Level implements ChunkManager, Metadatable{
|
||||
return $this->provider->getWorldHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int{
|
||||
return $this->provider->getDifficulty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $difficulty
|
||||
*/
|
||||
public function setDifficulty(int $difficulty){
|
||||
if($difficulty < 0 or $difficulty > 3){
|
||||
throw new \InvalidArgumentException("Invalid difficulty level $difficulty");
|
||||
}
|
||||
$this->provider->setDifficulty($difficulty);
|
||||
|
||||
$this->sendDifficulty();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[] ...$targets
|
||||
*/
|
||||
public function sendDifficulty(Player ...$targets){
|
||||
if(count($targets) === 0){
|
||||
$targets = $this->getPlayers();
|
||||
}
|
||||
|
||||
$pk = new SetDifficultyPacket();
|
||||
$pk->difficulty = $this->getDifficulty();
|
||||
$this->server->broadcastPacket($targets, $pk);
|
||||
}
|
||||
|
||||
public function populateChunk(int $x, int $z, bool $force = false) : bool{
|
||||
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)]) or (count($this->chunkPopulationQueue) >= $this->chunkPopulationQueueSize and !$force)){
|
||||
|
@ -206,6 +206,18 @@ interface LevelProvider{
|
||||
*/
|
||||
public function setSpawn(Vector3 $pos);
|
||||
|
||||
/**
|
||||
* Returns the world difficulty. This will be one of the Level constants.
|
||||
* @return int
|
||||
*/
|
||||
public function getDifficulty() : int;
|
||||
|
||||
/**
|
||||
* Sets the world difficulty.
|
||||
* @param int $difficulty
|
||||
*/
|
||||
public function setDifficulty(int $difficulty);
|
||||
|
||||
/**
|
||||
* @return Chunk[]
|
||||
*/
|
||||
|
@ -168,7 +168,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
$levelData = new CompoundTag("", [
|
||||
//Vanilla fields
|
||||
new IntTag("DayCycleStopTime", -1),
|
||||
new IntTag("Difficulty", 2),
|
||||
new IntTag("Difficulty", Level::getDifficultyFromString((string) ($options["difficulty"] ?? "normal"))),
|
||||
new ByteTag("ForceGameType", 0),
|
||||
new IntTag("GameType", 0),
|
||||
new IntTag("Generator", $generatorType),
|
||||
@ -197,7 +197,7 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
//Additional PocketMine-MP fields
|
||||
new CompoundTag("GameRules", []),
|
||||
new ByteTag("hardcore", 0),
|
||||
new ByteTag("hardcore", ($options["hardcore"] ?? false) === true ? 1 : 0),
|
||||
new StringTag("generatorName", Generator::getGeneratorName($generator)),
|
||||
new StringTag("generatorOptions", $options["preset"] ?? "")
|
||||
]);
|
||||
@ -253,6 +253,14 @@ class LevelDB extends BaseLevelProvider{
|
||||
return ["preset" => $this->levelData["generatorOptions"]];
|
||||
}
|
||||
|
||||
public function getDifficulty() : int{
|
||||
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL;
|
||||
}
|
||||
|
||||
public function setDifficulty(int $difficulty){
|
||||
$this->levelData->Difficulty = new IntTag("Difficulty", $difficulty);
|
||||
}
|
||||
|
||||
public function getLoadedChunks() : array{
|
||||
return $this->chunks;
|
||||
}
|
||||
|
@ -248,7 +248,8 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
//TODO, add extra details
|
||||
$levelData = new CompoundTag("Data", [
|
||||
new ByteTag("hardcore", 0),
|
||||
new ByteTag("hardcore", ($options["hardcore"] ?? false) === true ? 1 : 0),
|
||||
new ByteTag("Difficulty", Level::getDifficultyFromString((string) ($options["difficulty"] ?? "normal"))),
|
||||
new ByteTag("initialized", 1),
|
||||
new IntTag("GameType", 0),
|
||||
new IntTag("generatorVersion", 1), //2 in MCPE
|
||||
@ -282,6 +283,14 @@ class McRegion extends BaseLevelProvider{
|
||||
return ["preset" => $this->levelData["generatorOptions"]];
|
||||
}
|
||||
|
||||
public function getDifficulty() : int{
|
||||
return isset($this->levelData->Difficulty) ? $this->levelData->Difficulty->getValue() : Level::DIFFICULTY_NORMAL;
|
||||
}
|
||||
|
||||
public function setDifficulty(int $difficulty){
|
||||
$this->levelData->Difficulty = new ByteTag("Difficulty", $difficulty);
|
||||
}
|
||||
|
||||
public function getChunk(int $chunkX, int $chunkZ, bool $create = false){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
if(isset($this->chunks[$index])){
|
||||
|
Loading…
x
Reference in New Issue
Block a user