mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 00:07:30 +00:00
Merge branch 'mcpe-0.11'
This commit is contained in:
commit
86184a230e
File diff suppressed because it is too large
Load Diff
@ -67,13 +67,14 @@ namespace {
|
||||
namespace pocketmine {
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\wizard\Installer;
|
||||
|
||||
const VERSION = "1.4.1";
|
||||
const API_VERSION = "1.11.0";
|
||||
const CODENAME = "絶好(Zekkou)ケーキ(Cake)";
|
||||
const MINECRAFT_VERSION = "v0.10.5 alpha";
|
||||
const VERSION = "1.5dev";
|
||||
const API_VERSION = "1.12.0";
|
||||
const CODENAME = "活発(Kappatsu)フグ(Fugu)";
|
||||
const MINECRAFT_VERSION = "v0.11.0 alpha build 1";
|
||||
|
||||
/*
|
||||
* Startup code. Do not look at it, it may harm you.
|
||||
@ -119,13 +120,14 @@ namespace pocketmine {
|
||||
ini_set("memory_limit", -1);
|
||||
define("pocketmine\\START_TIME", microtime(true));
|
||||
|
||||
$opts = getopt("", ["enable-ansi", "disable-ansi", "data:", "plugins:", "no-wizard", "enable-profiler"]);
|
||||
$opts = getopt("", ["data:", "plugins:", "no-wizard", "enable-profiler"]);
|
||||
|
||||
define("pocketmine\\DATA", isset($opts["data"]) ? $opts["data"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR);
|
||||
define("pocketmine\\PLUGIN_PATH", isset($opts["plugins"]) ? $opts["plugins"] . DIRECTORY_SEPARATOR : \getcwd() . DIRECTORY_SEPARATOR . "plugins" . DIRECTORY_SEPARATOR);
|
||||
|
||||
define("pocketmine\\ANSI", (Utils::getOS() !== "win" or isset($opts["enable-ansi"])) and !isset($opts["disable-ansi"]));
|
||||
Terminal::init();
|
||||
|
||||
define("pocketmine\\ANSI", Terminal::hasFormattingCodes());
|
||||
|
||||
if(!file_exists(\pocketmine\DATA)){
|
||||
mkdir(\pocketmine\DATA, 0777, true);
|
||||
@ -468,6 +470,8 @@ namespace pocketmine {
|
||||
$logger->shutdown();
|
||||
$logger->join();
|
||||
|
||||
echo Terminal::$FORMAT_RESET . "\n";
|
||||
|
||||
exit(0);
|
||||
|
||||
}
|
||||
|
@ -32,12 +32,14 @@ use pocketmine\command\ConsoleCommandSender;
|
||||
use pocketmine\command\PluginIdentifiableCommand;
|
||||
use pocketmine\command\SimpleCommandMap;
|
||||
use pocketmine\entity\Arrow;
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\FallingSand;
|
||||
use pocketmine\entity\Human;
|
||||
use pocketmine\entity\Item as DroppedItem;
|
||||
use pocketmine\entity\PrimedTNT;
|
||||
use pocketmine\entity\Snowball;
|
||||
use pocketmine\entity\Squid;
|
||||
use pocketmine\entity\Villager;
|
||||
use pocketmine\entity\Zombie;
|
||||
use pocketmine\event\HandlerList;
|
||||
@ -54,11 +56,12 @@ use pocketmine\level\format\anvil\Anvil;
|
||||
use pocketmine\level\format\leveldb\LevelDB;
|
||||
use pocketmine\level\format\LevelProviderManager;
|
||||
use pocketmine\level\format\mcregion\McRegion;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\generator\Flat;
|
||||
use pocketmine\level\generator\GenerationInstanceManager;
|
||||
use pocketmine\level\generator\GenerationRequestManager;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\Normal;
|
||||
use pocketmine\level\generator\normal\Normal;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\metadata\EntityMetadataStore;
|
||||
use pocketmine\metadata\LevelMetadataStore;
|
||||
@ -73,6 +76,9 @@ use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\Long;
|
||||
use pocketmine\nbt\tag\Short;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\network\CompressBatchedTask;
|
||||
use pocketmine\network\Network;
|
||||
use pocketmine\network\protocol\BatchPacket;
|
||||
use pocketmine\network\protocol\DataPacket;
|
||||
use pocketmine\network\query\QueryHandler;
|
||||
use pocketmine\network\RakLibInterface;
|
||||
@ -86,7 +92,6 @@ use pocketmine\plugin\Plugin;
|
||||
use pocketmine\plugin\PluginLoadOrder;
|
||||
use pocketmine\plugin\PluginManager;
|
||||
use pocketmine\scheduler\CallbackTask;
|
||||
use pocketmine\scheduler\GarbageCollectionTask;
|
||||
use pocketmine\scheduler\SendUsageTask;
|
||||
use pocketmine\scheduler\ServerScheduler;
|
||||
use pocketmine\tile\Chest;
|
||||
@ -95,11 +100,11 @@ use pocketmine\tile\Sign;
|
||||
use pocketmine\tile\Tile;
|
||||
use pocketmine\updater\AutoUpdater;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\Cache;
|
||||
use pocketmine\utils\Config;
|
||||
use pocketmine\utils\LevelException;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\ServerException;
|
||||
use pocketmine\utils\Terminal;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\utils\TextWrapper;
|
||||
use pocketmine\utils\Utils;
|
||||
@ -141,9 +146,6 @@ class Server{
|
||||
/** @var ServerScheduler */
|
||||
private $scheduler = null;
|
||||
|
||||
/** @var GenerationRequestManager */
|
||||
private $generationManager = null;
|
||||
|
||||
/**
|
||||
* Counts the ticks since the server start
|
||||
*
|
||||
@ -188,10 +190,11 @@ class Server{
|
||||
/** @var LevelMetadataStore */
|
||||
private $levelMetadata;
|
||||
|
||||
/** @var SourceInterface[] */
|
||||
private $interfaces = [];
|
||||
/** @var RakLibInterface */
|
||||
private $mainInterface;
|
||||
/** @var Network */
|
||||
private $network;
|
||||
|
||||
private $networkCompressionAsync = true;
|
||||
private $networkCompressionLevel = 7;
|
||||
|
||||
private $serverID;
|
||||
|
||||
@ -214,8 +217,12 @@ class Server{
|
||||
/** @var Player[] */
|
||||
private $players = [];
|
||||
|
||||
private $identifiers = [];
|
||||
|
||||
/** @var Level[] */
|
||||
private $levels = [];
|
||||
private $offendingLevels = [];
|
||||
private $offendingTicks = [];
|
||||
|
||||
/** @var Level */
|
||||
private $levelDefault = null;
|
||||
@ -312,7 +319,7 @@ class Server{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
* @deprecated
|
||||
*/
|
||||
public function getServerName(){
|
||||
return $this->getConfigString("motd", "Minecraft: PE Server");
|
||||
@ -560,13 +567,6 @@ class Server{
|
||||
return $this->scheduler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GenerationRequestManager
|
||||
*/
|
||||
public function getGenerationManager(){
|
||||
return $this->generationManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
@ -592,67 +592,54 @@ class Server{
|
||||
return round((array_sum($this->useAverage) / count($this->useAverage)) * 100, 2);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param $address
|
||||
* @param int $timeout
|
||||
*/
|
||||
public function blockAddress($address, $timeout = 300){
|
||||
$this->network->blockAddress($address, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param $address
|
||||
* @param $port
|
||||
* @param $payload
|
||||
*/
|
||||
public function sendPacket($address, $port, $payload){
|
||||
$this->network->sendPacket($address, $port, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @return SourceInterface[]
|
||||
*/
|
||||
public function getInterfaces(){
|
||||
return $this->interfaces;
|
||||
return $this->network->getInterfaces();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param SourceInterface $interface
|
||||
*/
|
||||
public function addInterface(SourceInterface $interface){
|
||||
$this->interfaces[spl_object_hash($interface)] = $interface;
|
||||
$this->network->registerInterface($interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param SourceInterface $interface
|
||||
*/
|
||||
public function removeInterface(SourceInterface $interface){
|
||||
$interface->shutdown();
|
||||
unset($this->interfaces[spl_object_hash($interface)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
* @param string $payload
|
||||
*/
|
||||
public function sendPacket($address, $port, $payload){
|
||||
$this->mainInterface->putRaw($address, $port, $payload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks an IP address from the main interface. Setting timeout to -1 will block it forever
|
||||
*
|
||||
* @param string $address
|
||||
* @param int $timeout
|
||||
*/
|
||||
public function blockAddress($address, $timeout = 300){
|
||||
$this->mainInterface->blockAddress($address, $timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
* @param string $payload
|
||||
*/
|
||||
public function handlePacket($address, $port, $payload){
|
||||
try{
|
||||
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
|
||||
$this->queryHandler->handle($address, $port, $payload);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
$this->blockAddress($address, 600);
|
||||
}
|
||||
//TODO: add raw packet events
|
||||
$this->network->unregisterInterface($interface);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -890,6 +877,7 @@ class Server{
|
||||
foreach($this->players as $identifier => $p){
|
||||
if($player === $p){
|
||||
unset($this->players[$identifier]);
|
||||
unset($this->identifiers[spl_object_hash($player)]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1127,7 +1115,7 @@ class Server{
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $seed
|
||||
* @param string $generator Class name that extends pocketmine\level\generator\Generator
|
||||
* @param string $generator Class name that extends pocketmine\level\generator\Noise
|
||||
* @param array $options
|
||||
*
|
||||
* @return bool
|
||||
@ -1174,21 +1162,14 @@ class Server{
|
||||
|
||||
$this->getLogger()->notice("Spawn terrain for level \"$name\" is being generated in the background");
|
||||
|
||||
|
||||
$radiusSquared = ($this->getViewDistance() + 1) / M_PI;
|
||||
$radius = ceil(sqrt($radiusSquared));
|
||||
|
||||
$centerX = $level->getSpawnLocation()->getX() >> 4;
|
||||
$centerZ = $level->getSpawnLocation()->getZ() >> 4;
|
||||
|
||||
$order = [];
|
||||
|
||||
for($X = -$radius; $X <= $radius; ++$X){
|
||||
for($Z = -$radius; $Z <= $radius; ++$Z){
|
||||
for($X = -4; $X <= 4; ++$X){
|
||||
for($Z = -4; $Z <= 4; ++$Z){
|
||||
$distance = $X ** 2 + $Z ** 2;
|
||||
if($distance > $radiusSquared){
|
||||
continue;
|
||||
}
|
||||
$chunkX = $X + $centerX;
|
||||
$chunkZ = $Z + $centerZ;
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
@ -1202,7 +1183,7 @@ class Server{
|
||||
|
||||
foreach($order as $index => $distance){
|
||||
Level::getXZ($index, $chunkX, $chunkZ);
|
||||
$level->generateChunk($chunkX, $chunkZ);
|
||||
$level->generateChunk($chunkX, $chunkZ, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1509,7 +1490,6 @@ class Server{
|
||||
$this->properties = new Config($this->dataPath . "server.properties", Config::PROPERTIES, [
|
||||
"motd" => "Minecraft: PE Server",
|
||||
"server-port" => 19132,
|
||||
"memory-limit" => -1,
|
||||
"white-list" => false,
|
||||
"announce-player-achievements" => true,
|
||||
"spawn-protection" => 16,
|
||||
@ -1534,6 +1514,14 @@ class Server{
|
||||
|
||||
ServerScheduler::$WORKERS = $this->getProperty("settings.async-workers", ServerScheduler::$WORKERS);
|
||||
|
||||
if($this->getProperty("network.batch-threshold", 256) >= 0){
|
||||
Network::$BATCH_THRESHOLD = (int) $this->getProperty("network.batch-threshold", 256);
|
||||
}else{
|
||||
Network::$BATCH_THRESHOLD = -1;
|
||||
}
|
||||
$this->networkCompressionLevel = $this->getProperty("network.compression-level", 7);
|
||||
$this->networkCompressionAsync = $this->getProperty("network.async-compression", true);
|
||||
|
||||
$this->scheduler = new ServerScheduler();
|
||||
|
||||
if($this->getConfigBoolean("enable-rcon", false) === true){
|
||||
@ -1563,11 +1551,11 @@ class Server{
|
||||
$value = ["M" => 1, "G" => 1024];
|
||||
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
|
||||
if($real < 128){
|
||||
$this->logger->warning($this->getName() . " may not work right with less than 128MB of RAM");
|
||||
$this->logger->warning($this->getName() . " may not work right with less than 128MB of memory");
|
||||
}
|
||||
@ini_set("memory_limit", $memory);
|
||||
}else{
|
||||
$this->setConfigString("memory-limit", -1);
|
||||
$this->logger->notice("The memory limit will only affect the main thread, and it's unreliable.");
|
||||
$this->logger->notice("To control the memory usage, reduce the amount of threads and chunks loaded");
|
||||
}
|
||||
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||
@ -1578,14 +1566,8 @@ class Server{
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->setLogDebug(\pocketmine\DEBUG > 1);
|
||||
}
|
||||
define("ADVANCED_CACHE", $this->getProperty("settings.advanced-cache", false));
|
||||
if(ADVANCED_CACHE == true){
|
||||
$this->logger->info("Advanced cache enabled");
|
||||
}
|
||||
|
||||
Level::$COMPRESSION_LEVEL = $this->getProperty("chunk-sending.compression-level", 8);
|
||||
|
||||
if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0){
|
||||
if(\pocketmine\DEBUG >= 0){
|
||||
@cli_set_process_title($this->getName() . " " . $this->getPocketMineVersion());
|
||||
}
|
||||
|
||||
@ -1593,7 +1575,10 @@ class Server{
|
||||
define("BOOTUP_RANDOM", @Utils::getRandomBytes(16));
|
||||
$this->serverID = Binary::readLong(substr(Utils::getUniqueID(true, $this->getIp() . $this->getPort()), 0, 8));
|
||||
|
||||
$this->addInterface($this->mainInterface = new RakLibInterface($this));
|
||||
$this->network = new Network($this);
|
||||
$this->network->setName($this->getMotd());
|
||||
$this->network->registerInterface(new RakLibInterface($this));
|
||||
|
||||
|
||||
$this->logger->info("This server is running " . $this->getName() . " version " . ($version->isDev() ? TextFormat::YELLOW : "") . $version->get(true) . TextFormat::WHITE . " \"" . $this->getCodename() . "\" (API " . $this->getApiVersion() . ")");
|
||||
$this->logger->info($this->getName() . " is distributed under the LGPL License");
|
||||
@ -1610,6 +1595,9 @@ class Server{
|
||||
InventoryType::init();
|
||||
Block::init();
|
||||
Item::init();
|
||||
Biome::init();
|
||||
Effect::init();
|
||||
/** TODO: @deprecated */
|
||||
TextWrapper::init();
|
||||
$this->craftingManager = new CraftingManager();
|
||||
|
||||
@ -1627,12 +1615,6 @@ class Server{
|
||||
|
||||
$this->enablePlugins(PluginLoadOrder::STARTUP);
|
||||
|
||||
if($this->getProperty("chunk-generation.use-async", true)){
|
||||
$this->generationManager = new GenerationRequestManager($this);
|
||||
}else{
|
||||
$this->generationManager = new GenerationInstanceManager($this);
|
||||
}
|
||||
|
||||
LevelProviderManager::addProvider($this, Anvil::class);
|
||||
LevelProviderManager::addProvider($this, McRegion::class);
|
||||
if(extension_loaded("leveldb")){
|
||||
@ -1687,7 +1669,6 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([Cache::class, "cleanup"]), $this->getProperty("ticks-per.cache-cleanup", 900), $this->getProperty("ticks-per.cache-cleanup", 900));
|
||||
if($this->getAutoSave() and $this->getProperty("ticks-per.autosave", 6000) > 0){
|
||||
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([$this, "doAutoSave"]), $this->getProperty("ticks-per.autosave", 6000), $this->getProperty("ticks-per.autosave", 6000));
|
||||
}
|
||||
@ -1696,8 +1677,6 @@ class Server{
|
||||
$this->scheduler->scheduleDelayedRepeatingTask(new CallbackTask([$this, "doLevelGC"]), $this->getProperty("chunk-gc.period-in-ticks", 600), $this->getProperty("chunk-gc.period-in-ticks", 600));
|
||||
}
|
||||
|
||||
$this->scheduler->scheduleRepeatingTask(new GarbageCollectionTask(), 900);
|
||||
|
||||
$this->enablePlugins(PluginLoadOrder::POSTWORLD);
|
||||
|
||||
$this->start();
|
||||
@ -1753,6 +1732,11 @@ class Server{
|
||||
public static function broadcastPacket(array $players, DataPacket $packet){
|
||||
$packet->encode();
|
||||
$packet->isEncoded = true;
|
||||
if(Network::$BATCH_THRESHOLD >= 0 and strlen($packet->buffer) >= Network::$BATCH_THRESHOLD){
|
||||
Server::getInstance()->batchPackets($players, [$packet->buffer]);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach($players as $player){
|
||||
$player->dataPacket($packet);
|
||||
}
|
||||
@ -1761,6 +1745,53 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a list of packets in a batch to a list of players
|
||||
*
|
||||
* @param Player[] $players
|
||||
* @param DataPacket[]|string $packets
|
||||
*/
|
||||
public function batchPackets(array $players, array $packets){
|
||||
$str = "";
|
||||
|
||||
foreach($packets as $p){
|
||||
if(is_object($p)){
|
||||
$p->encode();
|
||||
$str .= $p->buffer;
|
||||
}else{
|
||||
$str .= $p;
|
||||
}
|
||||
}
|
||||
|
||||
$targets = [];
|
||||
foreach($players as $p){
|
||||
$targets[] = $this->identifiers[spl_object_hash($p)];
|
||||
}
|
||||
|
||||
if($this->networkCompressionAsync){
|
||||
$task = new CompressBatchedTask();
|
||||
$task->targets = $targets;
|
||||
$task->data = $str;
|
||||
$task->level = $this->networkCompressionLevel;
|
||||
$this->getScheduler()->scheduleAsyncTask($task);
|
||||
}else{
|
||||
$this->broadcastPacketsCallback(zlib_encode($str, ZLIB_ENCODING_DEFLATE, $this->networkCompressionLevel), $targets);
|
||||
}
|
||||
}
|
||||
|
||||
public function broadcastPacketsCallback($data, array $identifiers){
|
||||
$pk = new BatchPacket();
|
||||
$pk->payload = $data;
|
||||
$pk->encode();
|
||||
$pk->isEncoded = true;
|
||||
|
||||
foreach($identifiers as $i){
|
||||
if(isset($this->players[$i])){
|
||||
$this->players[$i]->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param int $type
|
||||
@ -1856,11 +1887,11 @@ class Server{
|
||||
$value = ["M" => 1, "G" => 1024];
|
||||
$real = ((int) substr($memory, 0, -1)) * $value[substr($memory, -1)];
|
||||
if($real < 256){
|
||||
$this->logger->warning($this->getName() . " may not work right with less than 256MB of RAM", true, true, 0);
|
||||
$this->logger->warning($this->getName() . " may not work right with less than 256MB of memory", true, true, 0);
|
||||
}
|
||||
@ini_set("memory_limit", $memory);
|
||||
}else{
|
||||
$this->setConfigString("memory-limit", -1);
|
||||
$this->logger->notice("The memory limit will only affect the main thread, and it's unreliable.");
|
||||
$this->logger->notice("To control the memory usage, reduce the amount of threads and chunks loaded");
|
||||
}
|
||||
|
||||
if($this->getConfigBoolean("hardcore", false) === true and $this->getDifficulty() < 3){
|
||||
@ -1904,7 +1935,7 @@ class Server{
|
||||
$this->rcon->stop();
|
||||
}
|
||||
|
||||
if($this->getProperty("settings.upnp-forwarding", false) === true){
|
||||
if($this->getProperty("network.upnp-forwarding", false) === true){
|
||||
$this->logger->info("[UPnP] Removing port forward...");
|
||||
UPnP::RemovePortForward($this->getPort());
|
||||
}
|
||||
@ -1919,10 +1950,6 @@ class Server{
|
||||
$this->unloadLevel($level, true);
|
||||
}
|
||||
|
||||
if($this->generationManager instanceof GenerationRequestManager){
|
||||
$this->generationManager->shutdown();
|
||||
}
|
||||
|
||||
HandlerList::unregisterAll();
|
||||
|
||||
$this->scheduler->cancelAllTasks();
|
||||
@ -1932,8 +1959,9 @@ class Server{
|
||||
|
||||
$this->console->kill();
|
||||
|
||||
foreach($this->interfaces as $interface){
|
||||
foreach($this->network->getInterfaces() as $interface){
|
||||
$interface->shutdown();
|
||||
$this->network->unregisterInterface($interface);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$this->logger->emergency("Crashed while crashing, killing process");
|
||||
@ -1946,14 +1974,12 @@ class Server{
|
||||
* Starts the PocketMine-MP server and starts processing ticks and packets
|
||||
*/
|
||||
public function start(){
|
||||
|
||||
if($this->getConfigBoolean("enable-query", true) === true){
|
||||
$this->queryHandler = new QueryHandler();
|
||||
|
||||
}
|
||||
|
||||
foreach($this->getIPBans()->getEntries() as $entry){
|
||||
$this->blockAddress($entry->getName(), -1);
|
||||
$this->network->blockAddress($entry->getName(), -1);
|
||||
}
|
||||
|
||||
if($this->getProperty("settings.send-usage", true) !== false){
|
||||
@ -1962,7 +1988,7 @@ class Server{
|
||||
}
|
||||
|
||||
|
||||
if($this->getProperty("settings.upnp-forwarding", false) == true){
|
||||
if($this->getProperty("network.upnp-forwarding", false) == true){
|
||||
$this->logger->info("[UPnP] Trying to port forward...");
|
||||
UPnP::PortForward($this->getPort());
|
||||
}
|
||||
@ -2114,14 +2140,33 @@ class Server{
|
||||
|
||||
public function addPlayer($identifier, Player $player){
|
||||
$this->players[$identifier] = $player;
|
||||
$this->identifiers[spl_object_hash($player)] = $identifier;
|
||||
}
|
||||
|
||||
private function checkTickUpdates($currentTick){
|
||||
|
||||
$tickLimit = 50 / count($this->levels);
|
||||
$levelTimes = [];
|
||||
|
||||
$startTime = microtime(true);
|
||||
//Do level ticks
|
||||
foreach($this->getLevels() as $level){
|
||||
if(isset($this->offendingLevels[$level->getId()]) and $this->offendingTicks[$level->getId()]-- > 0){
|
||||
continue;
|
||||
}
|
||||
try{
|
||||
$levelTime = microtime(true);
|
||||
$level->doTick($currentTick);
|
||||
$levelMs = (microtime(true) - $levelTime) * 1000;
|
||||
$levelTimes[$level->getId()] = $levelMs;
|
||||
if(isset($this->offendingLevels[$level->getId()])){
|
||||
if($levelMs < $tickLimit and --$this->offendingLevels[$level->getId()] <= 0){
|
||||
unset($this->offendingLevels[$level->getId()]);
|
||||
unset($this->offendingTicks[$level->getId()]);
|
||||
}else{
|
||||
$this->offendingTicks[$level->getId()] = $this->offendingLevels[$level->getId()];
|
||||
}
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$this->logger->critical("Could not tick level " . $level->getName() . ": " . $e->getMessage());
|
||||
if(\pocketmine\DEBUG > 1 and $this->logger instanceof MainLogger){
|
||||
@ -2129,6 +2174,26 @@ class Server{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$totalTime = (microtime(true) - $startTime) * 1000;
|
||||
|
||||
if($totalTime > 50){
|
||||
arsort($levelTimes);
|
||||
foreach($levelTimes as $levelId => $t){
|
||||
$totalTime -= $t;
|
||||
if(!isset($this->offendingLevels[$levelId])){
|
||||
$this->offendingLevels[$levelId] = max(1, min(10, floor($t / 50)));
|
||||
}elseif($this->offendingLevels[$levelId] < 10){ //Limit?
|
||||
++$this->offendingLevels[$levelId];
|
||||
}
|
||||
$this->offendingTicks[$levelId] = $this->offendingLevels[$levelId];
|
||||
|
||||
if($totalTime <= 50){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function doAutoSave(){
|
||||
@ -2139,6 +2204,7 @@ class Server{
|
||||
$player->save();
|
||||
}elseif(!$player->isConnected()){
|
||||
unset($this->players[$index]);
|
||||
unset($this->identifiers[spl_object_hash($player)]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2175,12 +2241,12 @@ class Server{
|
||||
"os" => Utils::getOS(),
|
||||
"name" => $this->getName(),
|
||||
"memory_total" => $this->getConfigString("memory-limit"),
|
||||
"memory_usage" => memory_get_usage(),
|
||||
"memory_usage" => $this->getMemoryUsage(),
|
||||
"php_version" => PHP_VERSION,
|
||||
"version" => $version->get(true),
|
||||
"build" => $version->getBuild(),
|
||||
"mc_version" => \pocketmine\MINECRAFT_VERSION,
|
||||
"protocol" => network\protocol\Info::CURRENT_PROTOCOL,
|
||||
"protocol" => \pocketmine\network\protocol\Info::CURRENT_PROTOCOL,
|
||||
"online" => count($this->players),
|
||||
"max" => $this->getMaxPlayers(),
|
||||
"plugins" => $plist,
|
||||
@ -2188,11 +2254,94 @@ class Server{
|
||||
|
||||
$this->scheduler->scheduleAsyncTask($this->lastSendUsage);
|
||||
}
|
||||
|
||||
public function getNetwork(){
|
||||
return $this->network;
|
||||
}
|
||||
|
||||
private function titleTick(){
|
||||
if(defined("pocketmine\\DEBUG") and \pocketmine\DEBUG >= 0 and \pocketmine\ANSI === true){
|
||||
echo "\x1b]0;" . $this->getName() . " " . $this->getPocketMineVersion() . " | Online " . count($this->players) . "/" . $this->getMaxPlayers() . " | RAM " . round((memory_get_usage() / 1024) / 1024, 2) . "/" . round((memory_get_usage(true) / 1024) / 1024, 2) . " MB | U " . round($this->mainInterface->getUploadUsage() / 1024, 2) . " D " . round($this->mainInterface->getDownloadUsage() / 1024, 2) . " kB/s | TPS " . $this->getTicksPerSecond() . " | Load " . $this->getTickUsage() . "%\x07";
|
||||
if(!Terminal::hasFormattingCodes()){
|
||||
return;
|
||||
}
|
||||
|
||||
$u = $this->getMemoryUsage(true);
|
||||
$usage = round(($u[0] / 1024) / 1024, 2) . "/".round(($u[1] / 1024) / 1024, 2)." MB @ " . $this->getThreadCount() . " threads";
|
||||
|
||||
echo "\x1b]0;" . $this->getName() . " " .
|
||||
$this->getPocketMineVersion() .
|
||||
" | Online " . count($this->players) . "/" . $this->getMaxPlayers() .
|
||||
" | Memory " . $usage .
|
||||
" | U " . round($this->network->getUpload() / 1024, 2) .
|
||||
" D " . round($this->network->getDownload() / 1024, 2) .
|
||||
" kB/s | TPS " . $this->getTicksPerSecond() .
|
||||
" | Load " . $this->getTickUsage() . "%\x07";
|
||||
|
||||
$this->network->resetStatistics();
|
||||
}
|
||||
|
||||
public function getMemoryUsage($advanced = false){
|
||||
$VmSize = null;
|
||||
$VmRSS = null;
|
||||
if(Utils::getOS() === "linux" or Utils::getOS() === "bsd"){
|
||||
$status = file_get_contents("/proc/self/status");
|
||||
if(preg_match("/VmRSS:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
||||
$VmRSS = $matches[1] * 1024;
|
||||
}
|
||||
|
||||
if(preg_match("/VmSize:[ \t]+([0-9]+) kB/", $status, $matches) > 0){
|
||||
$VmSize = $matches[1] * 1024;
|
||||
}
|
||||
}
|
||||
|
||||
if($VmRSS === null){
|
||||
$VmRSS = memory_get_usage();
|
||||
}
|
||||
|
||||
if(!$advanced){
|
||||
return $VmRSS;
|
||||
}
|
||||
|
||||
if($VmSize === null){
|
||||
$VmSize = memory_get_usage(true);
|
||||
}
|
||||
|
||||
return [$VmRSS, $VmSize];
|
||||
}
|
||||
|
||||
public function getThreadCount(){
|
||||
if(Utils::getOS() === "linux" or Utils::getOS() === "bsd"){
|
||||
|
||||
if(preg_match("/Threads:[ \t]+([0-9]+)/", file_get_contents("/proc/self/status"), $matches) > 0){
|
||||
return (int) $matches[1];
|
||||
}
|
||||
}
|
||||
|
||||
return count(ThreadManager::getInstance()->getAll()) + 3; //RakLib + MainLogger + Main Thread
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $address
|
||||
* @param int $port
|
||||
* @param string $payload
|
||||
*
|
||||
* TODO: move this to Network
|
||||
*/
|
||||
public function handlePacket($address, $port, $payload){
|
||||
try{
|
||||
if(strlen($payload) > 2 and substr($payload, 0, 2) === "\xfe\xfd" and $this->queryHandler instanceof QueryHandler){
|
||||
$this->queryHandler->handle($address, $port, $payload);
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
if(\pocketmine\DEBUG > 1){
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
|
||||
$this->blockAddress($address, 600);
|
||||
}
|
||||
//TODO: add raw packet events
|
||||
}
|
||||
|
||||
|
||||
@ -2212,9 +2361,7 @@ class Server{
|
||||
$this->checkConsole();
|
||||
|
||||
Timings::$connectionTimer->startTiming();
|
||||
foreach($this->interfaces as $interface){
|
||||
$interface->process();
|
||||
}
|
||||
$this->network->processInterfaces();
|
||||
Timings::$connectionTimer->stopTiming();
|
||||
|
||||
Timings::$schedulerTimer->startTiming();
|
||||
@ -2236,16 +2383,6 @@ class Server{
|
||||
}
|
||||
}
|
||||
|
||||
Timings::$generationTimer->startTiming();
|
||||
try{
|
||||
$this->generationManager->process();
|
||||
}catch(\Exception $e){
|
||||
if($this->logger instanceof MainLogger){
|
||||
$this->logger->logException($e);
|
||||
}
|
||||
}
|
||||
Timings::$generationTimer->stopTiming();
|
||||
|
||||
if(($this->tickCounter % 100) === 0){
|
||||
foreach($this->levels as $level){
|
||||
$level->clearCache();
|
||||
@ -2279,6 +2416,7 @@ class Server{
|
||||
Entity::registerEntity(Snowball::class);
|
||||
Entity::registerEntity(Villager::class);
|
||||
Entity::registerEntity(Zombie::class);
|
||||
Entity::registerEntity(Squid::class);
|
||||
|
||||
Entity::registerEntity(Human::class, true);
|
||||
}
|
||||
|
@ -26,10 +26,38 @@ namespace pocketmine;
|
||||
*/
|
||||
abstract class Thread extends \Thread{
|
||||
|
||||
/** @var \ClassLoader */
|
||||
protected $classLoader;
|
||||
|
||||
public function getClassLoader(){
|
||||
return $this->classLoader;
|
||||
}
|
||||
|
||||
public function setClassLoader(\ClassLoader $loader = null){
|
||||
if($loader === null){
|
||||
$loader = Server::getInstance()->getLoader();
|
||||
}
|
||||
$this->classLoader = $loader;
|
||||
}
|
||||
|
||||
public function registerClassLoader(){
|
||||
if(!interface_exists("ClassLoader", false)){
|
||||
require(\pocketmine\PATH . "src/spl/ClassLoader.php");
|
||||
require(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
|
||||
require(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php");
|
||||
}
|
||||
if($this->classLoader !== null){
|
||||
$this->classLoader->register(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function start($options = PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
|
@ -26,10 +26,38 @@ namespace pocketmine;
|
||||
*/
|
||||
abstract class Worker extends \Worker{
|
||||
|
||||
/** @var \ClassLoader */
|
||||
protected $classLoader;
|
||||
|
||||
public function getClassLoader(){
|
||||
return $this->classLoader;
|
||||
}
|
||||
|
||||
public function setClassLoader(\ClassLoader $loader = null){
|
||||
if($loader === null){
|
||||
$loader = Server::getInstance()->getLoader();
|
||||
}
|
||||
$this->classLoader = $loader;
|
||||
}
|
||||
|
||||
public function registerClassLoader(){
|
||||
if(!interface_exists("ClassLoader", false)){
|
||||
require(\pocketmine\PATH . "src/spl/ClassLoader.php");
|
||||
require(\pocketmine\PATH . "src/spl/BaseClassLoader.php");
|
||||
require(\pocketmine\PATH . "src/pocketmine/CompatibleClassLoader.php");
|
||||
}
|
||||
if($this->classLoader !== null){
|
||||
$this->classLoader->register(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function start($options = PTHREADS_INHERIT_ALL){
|
||||
ThreadManager::getInstance()->add($this);
|
||||
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated() and !$this->isShutdown()){
|
||||
if(!$this->isRunning() and !$this->isJoined() and !$this->isTerminated()){
|
||||
if($this->getClassLoader() === null){
|
||||
$this->setClassLoader();
|
||||
}
|
||||
return parent::start($options);
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,8 @@ namespace pocketmine\block;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\network\protocol\ChatPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class Bed extends Transparent{
|
||||
|
||||
@ -65,10 +65,7 @@ class Bed extends Transparent{
|
||||
$isNight = ($time >= Level::TIME_NIGHT and $time < Level::TIME_SUNRISE);
|
||||
|
||||
if($player instanceof Player and !$isNight){
|
||||
$pk = new ChatPacket();
|
||||
$pk->message = "You can only sleep at night";
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->sendMessage(TextFormat::GRAY . "You can only sleep at night");
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -89,9 +86,7 @@ class Bed extends Transparent{
|
||||
$b = $blockWest;
|
||||
}else{
|
||||
if($player instanceof Player){
|
||||
$pk = new ChatPacket();
|
||||
$pk->message = "This bed is incomplete";
|
||||
$player->dataPacket($pk);
|
||||
$player->sendMessage(TextFormat::GRAY . "This bed is incomplete");
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -99,9 +94,7 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
if($player instanceof Player and $player->sleepOn($b) === false){
|
||||
$pk = new ChatPacket();
|
||||
$pk->message = "This bed is occupied";
|
||||
$player->dataPacket($pk);
|
||||
$player->sendMessage(TextFormat::GRAY . "This bed is occupied");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -25,6 +25,7 @@
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Squid;
|
||||
use pocketmine\entity\Villager;
|
||||
use pocketmine\entity\Zombie;
|
||||
use pocketmine\item\Item;
|
||||
@ -200,6 +201,8 @@ class Block extends Position implements Metadatable{
|
||||
const CARROT_BLOCK = 141;
|
||||
const POTATO_BLOCK = 142;
|
||||
|
||||
const REDSTONE_BLOCK = 152;
|
||||
|
||||
const QUARTZ_BLOCK = 155;
|
||||
const QUARTZ_STAIRS = 156;
|
||||
const DOUBLE_WOOD_SLAB = 157;
|
||||
@ -351,6 +354,7 @@ class Block extends Position implements Metadatable{
|
||||
[Item::LAPIS_BLOCK, 0],
|
||||
[Item::COAL_BLOCK, 0],
|
||||
[Item::EMERALD_BLOCK, 0],
|
||||
[Item::REDSTONE_BLOCK, 0],
|
||||
[Item::SNOW_LAYER, 0],
|
||||
[Item::GLASS, 0],
|
||||
[Item::GLOWSTONE_BLOCK, 0],
|
||||
@ -491,7 +495,7 @@ class Block extends Position implements Metadatable{
|
||||
//TODO: Slime
|
||||
[Item::SPAWN_EGG, Zombie::NETWORK_ID],
|
||||
//TODO: PigZombie
|
||||
//TODO: Replace with Entity constants
|
||||
[Item::SPAWN_EGG, Squid::NETWORK_ID],
|
||||
|
||||
|
||||
//Seeds
|
||||
@ -504,6 +508,12 @@ class Block extends Position implements Metadatable{
|
||||
[Item::POTATO, 0],
|
||||
[Item::BEETROOT_SEEDS, 0],
|
||||
[Item::EGG, 0],
|
||||
[Item::RAW_FISH, 0],
|
||||
[Item::RAW_FISH, 1],
|
||||
[Item::RAW_FISH, 2],
|
||||
[Item::RAW_FISH, 3],
|
||||
[Item::COOKED_FISH, 0],
|
||||
[Item::COOKED_FISH, 1],
|
||||
[Item::DYE, 0],
|
||||
[Item::DYE, 7],
|
||||
[Item::DYE, 6],
|
||||
|
@ -92,10 +92,10 @@ class Cake extends Transparent{
|
||||
public function onActivate(Item $item, Player $player = null){
|
||||
if($player instanceof Player and $player->getHealth() < 20){
|
||||
++$this->meta;
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev = new EntityRegainHealthEvent($player, 3, EntityRegainHealthEvent::CAUSE_EATING));
|
||||
if(!$ev->isCancelled()){
|
||||
$player->heal($ev->getAmount(), $ev);
|
||||
}
|
||||
|
||||
$ev = new EntityRegainHealthEvent($player, 3, EntityRegainHealthEvent::CAUSE_EATING);
|
||||
$player->heal($ev->getAmount(), $ev);
|
||||
|
||||
if($this->meta >= 0x06){
|
||||
$this->getLevel()->setBlock($this, new Air(), true);
|
||||
}else{
|
||||
|
@ -23,6 +23,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\sound\DoorSound;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\network\protocol\LevelEventPacket;
|
||||
use pocketmine\Player;
|
||||
@ -275,14 +276,8 @@ abstract class Door extends Transparent{
|
||||
if($player instanceof Player){
|
||||
unset($players[$player->getId()]);
|
||||
}
|
||||
$pk = new LevelEventPacket();
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->evid = 1003;
|
||||
$pk->data = 0;
|
||||
Server::broadcastPacket($players, $pk);
|
||||
|
||||
$this->level->addSound(new DoorSound($this));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -294,13 +289,7 @@ abstract class Door extends Transparent{
|
||||
if($player instanceof Player){
|
||||
unset($players[$player->getId()]);
|
||||
}
|
||||
$pk = new LevelEventPacket();
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->evid = 1003;
|
||||
$pk->data = 0;
|
||||
Server::broadcastPacket($players, $pk);
|
||||
$this->level->addSound(new DoorSound($this));
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityCombustByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
@ -58,8 +59,10 @@ class Fire extends Flowable{
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
|
||||
$ev = new EntityCombustByBlockEvent($this, $entity, 8);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityCombustByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
@ -51,8 +52,10 @@ class Lava extends Liquid{
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$entity->fallDistance *= 0.5;
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
|
||||
$ev = new EntityCombustByBlockEvent($this, $entity, 15);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
@ -60,9 +63,7 @@ class Lava extends Liquid{
|
||||
$entity->setOnFire($ev->getDuration());
|
||||
}
|
||||
|
||||
if($entity instanceof Player){
|
||||
$entity->onGround = true;
|
||||
}
|
||||
$entity->resetFallDistance();
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){
|
||||
|
68
src/pocketmine/block/Redstone.php
Normal file
68
src/pocketmine/block/Redstone.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class Redstone extends Transparent{
|
||||
|
||||
protected $id = self::REDSTONE_BLOCK;
|
||||
|
||||
public function __construct(){
|
||||
|
||||
}
|
||||
|
||||
public function getHardness(){
|
||||
return 30;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Redstone Block";
|
||||
}
|
||||
|
||||
public function getBreakTime(Item $item){
|
||||
switch($item->isPickaxe()){
|
||||
case 5:
|
||||
return 0.95;
|
||||
case 4:
|
||||
return 1.25;
|
||||
case 3:
|
||||
return 1.9;
|
||||
case 2:
|
||||
return 0.65;
|
||||
case 1:
|
||||
return 3.75;
|
||||
default:
|
||||
return 25;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDrops(Item $item){
|
||||
if($item->isPickaxe() >= 1){
|
||||
return [
|
||||
[Item::REDSTONE_BLOCK, 0, 1],
|
||||
];
|
||||
}else{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ class Slab extends Transparent{
|
||||
6 => "Quartz",
|
||||
7 => "",
|
||||
];
|
||||
return (($this->meta & 0x08) === 0x08 ? "Upper " : "") . $names[$this->meta & 0x07] . " Slab";
|
||||
return (($this->meta & 0x08) > 0 ? "Upper " : "") . $names[$this->meta & 0x07] . " Slab";
|
||||
}
|
||||
|
||||
protected function recalculateBoundingBox(){
|
||||
|
@ -47,9 +47,7 @@ class Water extends Liquid{
|
||||
$entity->extinguish();
|
||||
}
|
||||
|
||||
if($entity instanceof Player){
|
||||
$entity->onGround = true;
|
||||
}
|
||||
$entity->resetFallDistance();
|
||||
}
|
||||
|
||||
public function place(Item $item, Block $block, Block $target, $face, $fx, $fy, $fz, Player $player = null){
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\command\defaults\BanListCommand;
|
||||
use pocketmine\command\defaults\DefaultGamemodeCommand;
|
||||
use pocketmine\command\defaults\DeopCommand;
|
||||
use pocketmine\command\defaults\DifficultyCommand;
|
||||
use pocketmine\command\defaults\EffectCommand;
|
||||
use pocketmine\command\defaults\GamemodeCommand;
|
||||
use pocketmine\command\defaults\GiveCommand;
|
||||
use pocketmine\command\defaults\HelpCommand;
|
||||
@ -37,6 +38,7 @@ use pocketmine\command\defaults\MeCommand;
|
||||
use pocketmine\command\defaults\OpCommand;
|
||||
use pocketmine\command\defaults\PardonCommand;
|
||||
use pocketmine\command\defaults\PardonIpCommand;
|
||||
use pocketmine\command\defaults\ParticleCommand;
|
||||
use pocketmine\command\defaults\PluginsCommand;
|
||||
use pocketmine\command\defaults\ReloadCommand;
|
||||
use pocketmine\command\defaults\SaveCommand;
|
||||
@ -98,6 +100,8 @@ class SimpleCommandMap implements CommandMap{
|
||||
$this->register("pocketmine", new SaveOffCommand("save-off"));
|
||||
$this->register("pocketmine", new SaveCommand("save-all"));
|
||||
$this->register("pocketmine", new GiveCommand("give"));
|
||||
$this->register("pocketmine", new EffectCommand("effect"));
|
||||
$this->register("pocketmine", new ParticleCommand("particle"));
|
||||
$this->register("pocketmine", new GamemodeCommand("gamemode"));
|
||||
$this->register("pocketmine", new KillCommand("kill"));
|
||||
$this->register("pocketmine", new SpawnpointCommand("spawnpoint"));
|
||||
|
@ -75,7 +75,7 @@ class BanIpCommand extends VanillaCommand{
|
||||
}
|
||||
}
|
||||
|
||||
$sender->getServer()->blockAddress($ip, -1);
|
||||
$sender->getServer()->getNetwork()->blockAddress($ip, -1);
|
||||
|
||||
Command::broadcastCommandMessage($sender, "Banned IP Address " . $ip);
|
||||
}
|
||||
|
113
src/pocketmine/command/defaults/EffectCommand.php
Normal file
113
src/pocketmine/command/defaults/EffectCommand.php
Normal file
@ -0,0 +1,113 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\command\Command;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\InstantEffect;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class EffectCommand extends VanillaCommand{
|
||||
|
||||
public function __construct($name){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"Adds/Removes effects on players",
|
||||
"/effect <player> <effect|clear> [seconds] [amplifier]"
|
||||
);
|
||||
$this->setPermission("pocketmine.command.effect");
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, $currentAlias, array $args){
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($args) < 2){
|
||||
$sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayer($args[0]);
|
||||
|
||||
if($player === null){
|
||||
$sender->sendMessage(TextFormat::RED . "Player {$args[0]} not found");
|
||||
return true;
|
||||
}
|
||||
|
||||
if(strtolower($args[1]) === "clear"){
|
||||
foreach($player->getEffects() as $effect){
|
||||
$player->removeEffect($effect->getId());
|
||||
}
|
||||
|
||||
$sender->sendMessage("Took all effects from " . $player->getDisplayName());
|
||||
return true;
|
||||
}
|
||||
|
||||
$effect = Effect::getEffectByName($args[1]);
|
||||
|
||||
if($effect === null){
|
||||
$effect = Effect::getEffect((int) $args[1]);
|
||||
}
|
||||
|
||||
if($effect === null){
|
||||
$sender->sendMessage(TextFormat::RED . "Effect {$args[1]} not found");
|
||||
return true;
|
||||
}
|
||||
|
||||
$duration = 300;
|
||||
$amplification = 0;
|
||||
|
||||
if(count($args) >= 3){
|
||||
$duration = (int) $args[2];
|
||||
if(!($effect instanceof InstantEffect)){
|
||||
$duration *= 20;
|
||||
}
|
||||
}elseif($effect instanceof InstantEffect){
|
||||
$duration = 1;
|
||||
}
|
||||
|
||||
if(count($args) >= 4){
|
||||
$amplification = (int) $args[3];
|
||||
}
|
||||
|
||||
if($duration === 0){
|
||||
if(!$player->hasEffect($effect->getId())){
|
||||
$sender->sendMessage("Couldn't take ". $effect->getName() ." from ". $player->getDisplayName());
|
||||
return true;
|
||||
}
|
||||
|
||||
$player->removeEffect($effect->getId());
|
||||
$sender->sendMessage("Took ". $effect->getName() ." from ". $player->getDisplayName());
|
||||
}else{
|
||||
$effect->setDuration($duration)->setAmplifier($amplification);
|
||||
|
||||
$player->addEffect($effect);
|
||||
self::broadcastCommandMessage($sender, "Given ". $effect->getName() ." (ID ". $effect->getId().") * ". $effect->getAmplifier()." to ". $player->getDisplayName() ." for ". ($effect->getDuration() / 20) ." seconds");
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -46,7 +46,7 @@ class GiveCommand extends VanillaCommand{
|
||||
if(count($args) < 2){
|
||||
$sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage);
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
$player = $sender->getServer()->getPlayer($args[0]);
|
||||
|
191
src/pocketmine/command/defaults/ParticleCommand.php
Normal file
191
src/pocketmine/command/defaults/ParticleCommand.php
Normal file
@ -0,0 +1,191 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\command\defaults;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\command\CommandSender;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\particle\BubbleParticle;
|
||||
use pocketmine\level\particle\CriticalParticle;
|
||||
use pocketmine\level\particle\DustParticle;
|
||||
use pocketmine\level\particle\EnchantParticle;
|
||||
use pocketmine\level\particle\ExplodeParticle;
|
||||
use pocketmine\level\particle\FlameParticle;
|
||||
use pocketmine\level\particle\HeartParticle;
|
||||
use pocketmine\level\particle\InkParticle;
|
||||
use pocketmine\level\particle\ItemBreakParticle;
|
||||
use pocketmine\level\particle\LavaDripParticle;
|
||||
use pocketmine\level\particle\LavaParticle;
|
||||
use pocketmine\level\particle\Particle;
|
||||
use pocketmine\level\particle\PortalParticle;
|
||||
use pocketmine\level\particle\RedstoneParticle;
|
||||
use pocketmine\level\particle\SmokeParticle;
|
||||
use pocketmine\level\particle\SplashParticle;
|
||||
use pocketmine\level\particle\SporeParticle;
|
||||
use pocketmine\level\particle\TerrainParticle;
|
||||
use pocketmine\level\particle\WaterDripParticle;
|
||||
use pocketmine\level\particle\WaterParticle;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\utils\TextFormat;
|
||||
|
||||
class ParticleCommand extends VanillaCommand{
|
||||
|
||||
public function __construct($name){
|
||||
parent::__construct(
|
||||
$name,
|
||||
"Adds particles to a world",
|
||||
"/particle <name> <x> <y> <z> <xd> <yd> <zd> [count] [data]"
|
||||
);
|
||||
$this->setPermission("pocketmine.command.particle");
|
||||
}
|
||||
|
||||
public function execute(CommandSender $sender, $currentAlias, array $args){
|
||||
if(!$this->testPermission($sender)){
|
||||
return true;
|
||||
}
|
||||
|
||||
if(count($args) < 7){
|
||||
$sender->sendMessage(TextFormat::RED . "Usage: " . $this->usageMessage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($sender instanceof Player){
|
||||
$level = $sender->getLevel();
|
||||
}else{
|
||||
$level = $sender->getServer()->getDefaultLevel();
|
||||
}
|
||||
|
||||
$name = strtolower($args[0]);
|
||||
|
||||
$pos = new Vector3((float) $args[1], (float) $args[2], (float) $args[3]);
|
||||
|
||||
$xd = (float) $args[4];
|
||||
$yd = (float) $args[5];
|
||||
$zd = (float) $args[6];
|
||||
|
||||
$count = isset($args[7]) ? max(1, (int) $args[7]) : 1;
|
||||
|
||||
$data = isset($args[8]) ? (int) $args[8] : null;
|
||||
|
||||
$particle = $this->getParticle($name, $pos, $xd, $yd, $zd, $data);
|
||||
|
||||
if($particle === null){
|
||||
$sender->sendMessage(TextFormat::RED . "Unknown particle name (" . $name . ")");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
$sender->sendMessage("Playing particle ". $name ." for ". $count ." times");
|
||||
|
||||
$random = new Random((int) (microtime(true) * 1000) + mt_rand());
|
||||
|
||||
for($i = 0; $i < $count; ++$i){
|
||||
$particle->setComponents(
|
||||
$pos->x + $random->nextSignedFloat() * $xd,
|
||||
$pos->y + $random->nextSignedFloat() * $yd,
|
||||
$pos->z + $random->nextSignedFloat() * $zd
|
||||
);
|
||||
$level->addParticle($particle);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return Particle
|
||||
*/
|
||||
private function getParticle($name, Vector3 $pos, $xd, $yd, $zd, $data){
|
||||
switch($name){
|
||||
case "explode":
|
||||
return new ExplodeParticle($pos);
|
||||
case "bubble":
|
||||
return new BubbleParticle($pos);
|
||||
case "splash":
|
||||
return new SplashParticle($pos);
|
||||
case "wake":
|
||||
case "water":
|
||||
return new WaterParticle($pos);
|
||||
case "crit":
|
||||
return new CriticalParticle($pos);
|
||||
case "smoke":
|
||||
return new SmokeParticle($pos, $data !== null ? $data : 0);
|
||||
case "spell":
|
||||
return new EnchantParticle($pos);
|
||||
case "dripwater":
|
||||
return new WaterDripParticle($pos);
|
||||
case "driplava":
|
||||
return new LavaDripParticle($pos);
|
||||
case "townaura":
|
||||
case "spore":
|
||||
return new SporeParticle($pos);
|
||||
case "portal":
|
||||
return new PortalParticle($pos);
|
||||
case "flame":
|
||||
return new FlameParticle($pos);
|
||||
case "lava":
|
||||
return new LavaParticle($pos);
|
||||
case "reddust":
|
||||
return new RedstoneParticle($pos, $data !== null ? $data : 1);
|
||||
case "snowballpoof":
|
||||
return new ItemBreakParticle($pos, Item::get(Item::SNOWBALL));
|
||||
case "itembreak":
|
||||
if($data !== null and $data !== 0){
|
||||
return new ItemBreakParticle($pos, $data);
|
||||
}
|
||||
break;
|
||||
case "terrain":
|
||||
if($data !== null and $data !== 0){
|
||||
return new TerrainParticle($pos, $data);
|
||||
}
|
||||
break;
|
||||
case "heart":
|
||||
return new HeartParticle($pos, $data !== null ? $data : 0);
|
||||
case "ink":
|
||||
return new InkParticle($pos, $data !== null ? $data : 0);
|
||||
|
||||
}
|
||||
|
||||
if(substr($name, 0, 10) === "iconcrack_"){
|
||||
$d = explode("_", $name);
|
||||
if(count($d) === 3){
|
||||
return new ItemBreakParticle($pos, Item::get((int) $d[1], (int) $d[2]));
|
||||
}
|
||||
}elseif(substr($name, 0, 11) === "blockcrack_"){
|
||||
$d = explode("_", $name);
|
||||
if(count($d) === 2){
|
||||
return new TerrainParticle($pos, Block::get($d[1] & 0xff, $d[1] >> 12));
|
||||
}
|
||||
}elseif(substr($name, 0, 10) === "blockdust_"){
|
||||
$d = explode("_", $name);
|
||||
if(count($d) >= 4){
|
||||
return new DustParticle($pos, $d[1] & 0xff, $d[2] & 0xff, $d[3] & 0xff, isset($d[4]) ? $d[4] & 0xff : 255);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -44,10 +44,10 @@ class StatusCommand extends VanillaCommand{
|
||||
$sender->sendMessage(TextFormat::GREEN . "---- " . TextFormat::WHITE . "Server status" . TextFormat::GREEN . " ----");
|
||||
$sender->sendMessage(TextFormat::GOLD . "TPS: " . TextFormat::WHITE . $server->getTicksPerSecond());
|
||||
$sender->sendMessage(TextFormat::GOLD . "TPS Load: " . TextFormat::WHITE . $server->getTickUsage() . "%");
|
||||
//TODO: implement network speed
|
||||
//$sender->sendMessage(TextFormat::GOLD . "Upload: " . TextFormat::WHITE . round($server->getNetwork()->getUploadSpeed() / 1024, 2) . " kB/s");
|
||||
//$sender->sendMessage(TextFormat::GOLD . "Download: " . TextFormat::WHITE . round($server->getNetwork()->getDownloadSpeed() / 1024, 2) . " kB/s");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Memory: " . TextFormat::WHITE . round((memory_get_usage() / 1024) / 1024, 2) . TextFormat::YELLOW . "/" . TextFormat::WHITE . round((memory_get_usage(true) / 1024) / 1024, 2) . " MB");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Upload: " . TextFormat::WHITE . round($server->getNetwork()->getUploadUsage() / 1024, 2) . " kB/s");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Download: " . TextFormat::WHITE . round($server->getNetwork()->getDownloadUsage() / 1024, 2) . " kB/s");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Memory: " . TextFormat::WHITE . round(($server->getMemoryUsage() / 1024) / 1024, 2) . " MB");
|
||||
$sender->sendMessage(TextFormat::GOLD . "Threads: " . TextFormat::WHITE . $server->getThreadCount());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -23,5 +23,9 @@ namespace pocketmine\entity;
|
||||
|
||||
|
||||
interface Ageable{
|
||||
const DATA_AGEABLE_FLAGS = 14;
|
||||
|
||||
const DATA_FLAG_BABY = 0;
|
||||
|
||||
public function isBaby();
|
||||
}
|
@ -24,4 +24,14 @@ namespace pocketmine\entity;
|
||||
|
||||
abstract class Animal extends Creature implements Ageable{
|
||||
|
||||
public function initEntity(){
|
||||
parent::initEntity();
|
||||
if($this->getDataProperty(self::DATA_AGEABLE_FLAGS) === null){
|
||||
$this->setDataProperty(self::DATA_AGEABLE_FLAGS, self::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function isBaby(){
|
||||
return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY);
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\particle\CriticalParticle;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\Player;
|
||||
@ -36,11 +37,13 @@ class Arrow extends Projectile{
|
||||
protected $gravity = 0.05;
|
||||
protected $drag = 0.01;
|
||||
|
||||
protected $damage = 6;
|
||||
protected $damage = 2;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){
|
||||
$this->shootingEntity = $shootingEntity;
|
||||
parent::__construct($chunk, $nbt);
|
||||
protected $isCritical;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null, $critical = false){
|
||||
$this->isCritical = (bool) $critical;
|
||||
parent::__construct($chunk, $nbt, $shootingEntity);
|
||||
}
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
@ -52,6 +55,15 @@ class Arrow extends Projectile{
|
||||
|
||||
$hasUpdate = parent::onUpdate($currentTick);
|
||||
|
||||
if(!$this->hadCollision and $this->isCritical){
|
||||
$this->level->addParticle(new CriticalParticle($this->add(
|
||||
$this->width / 2 + mt_rand(-100, 100) / 500,
|
||||
$this->height / 2 + mt_rand(-100, 100) / 500,
|
||||
$this->width / 2 + mt_rand(-100, 100) / 500)));
|
||||
}elseif($this->onGround){
|
||||
$this->isCritical = false;
|
||||
}
|
||||
|
||||
if($this->age > 1200){
|
||||
$this->kill();
|
||||
$hasUpdate = true;
|
||||
@ -69,11 +81,12 @@ class Arrow extends Projectile{
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->did = 0; //TODO: send motion here
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
271
src/pocketmine/entity/Effect.php
Normal file
271
src/pocketmine/entity/Effect.php
Normal file
@ -0,0 +1,271 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\network\protocol\MobEffectPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Effect{
|
||||
const SPEED = 1;
|
||||
const SLOWNESS = 2;
|
||||
const SWIFTNESS = 3;
|
||||
const FATIGUE = 4;
|
||||
const MINING_FATIGUE = 4;
|
||||
const STRENGTH = 5;
|
||||
//TODO: const HEALING = 6;
|
||||
//TODO: const HARMING = 7;
|
||||
const JUMP = 8;
|
||||
const NAUSEA = 9;
|
||||
const CONFUSION = 9;
|
||||
const REGENERATION = 10;
|
||||
const DAMAGE_RESISTANCE = 11;
|
||||
const FIRE_RESISTANCE = 12;
|
||||
const WATER_BREATHING = 13;
|
||||
const INVISIBILITY = 14;
|
||||
//const BLINDNESS = 15;
|
||||
//const NIGHT_VISION = 16;
|
||||
//const HUNGER = 17;
|
||||
const WEAKNESS = 18;
|
||||
const POISON = 19;
|
||||
const WITHER = 20;
|
||||
//const HEALTH_BOOST = 21;
|
||||
//const ABSORPTION = 22;
|
||||
//const SATURATION = 23;
|
||||
|
||||
/** @var Effect[] */
|
||||
protected static $effects;
|
||||
|
||||
public static function init(){
|
||||
self::$effects = new \SplFixedArray(256);
|
||||
|
||||
self::$effects[Effect::SPEED] = new Effect(Effect::SPEED, "Speed", 124, 175, 198);
|
||||
self::$effects[Effect::SLOWNESS] = new Effect(Effect::SLOWNESS, "Slowness", 90, 108, 129, true);
|
||||
self::$effects[Effect::SWIFTNESS] = new Effect(Effect::SWIFTNESS, "Swiftness", 217, 192, 67);
|
||||
self::$effects[Effect::FATIGUE] = new Effect(Effect::FATIGUE, "Mining Fatigue", 74, 66, 23, true);
|
||||
self::$effects[Effect::STRENGTH] = new Effect(Effect::STRENGTH, "Strength", 147, 36, 35);
|
||||
//self::$effects[Effect::HEALING] = new InstantEffect(Effect::HEALING, "Healing", 248, 36, 35);
|
||||
//self::$effects[Effect::HARMING] = new InstantEffect(Effect::HARMING, "Harming", 67, 10, 9, true);
|
||||
self::$effects[Effect::NAUSEA] = new Effect(Effect::NAUSEA, "Nausea", 85, 29, 74, true);
|
||||
self::$effects[Effect::JUMP] = new Effect(Effect::JUMP, "Jump", 34, 255, 76);
|
||||
self::$effects[Effect::REGENERATION] = new Effect(Effect::REGENERATION, "Regeneration", 205, 92, 171);
|
||||
self::$effects[Effect::DAMAGE_RESISTANCE] = new Effect(Effect::DAMAGE_RESISTANCE, "Damage Resistance", 153, 69, 58);
|
||||
self::$effects[Effect::FIRE_RESISTANCE] = new Effect(Effect::FIRE_RESISTANCE, "Fire Resistance", 228, 154, 58);
|
||||
self::$effects[Effect::WATER_BREATHING] = new Effect(Effect::WATER_BREATHING, "Water Breathing", 46, 82, 153);
|
||||
self::$effects[Effect::INVISIBILITY] = new Effect(Effect::INVISIBILITY, "Invisibility", 127, 131, 146);
|
||||
self::$effects[Effect::WEAKNESS] = new Effect(Effect::WEAKNESS, "Weakness", 72, 77, 72 , true);
|
||||
self::$effects[Effect::POISON] = new Effect(Effect::POISON, "Poison", 78, 147, 49, true);
|
||||
self::$effects[Effect::WITHER] = new Effect(Effect::WITHER, "Wither", 53, 42, 39, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
* @return $this
|
||||
*/
|
||||
public static function getEffect($id){
|
||||
if(isset(self::$effects[$id])){
|
||||
return clone self::$effects[(int) $id];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static function getEffectByName($name){
|
||||
if(defined(Effect::class . "::" . strtoupper($name))){
|
||||
return self::getEffect(constant(Effect::class . "::" . strtoupper($name)));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @var int */
|
||||
protected $id;
|
||||
|
||||
protected $name;
|
||||
|
||||
protected $duration;
|
||||
|
||||
protected $amplifier;
|
||||
|
||||
protected $color;
|
||||
|
||||
protected $show = true;
|
||||
|
||||
protected $ambient = false;
|
||||
|
||||
protected $bad;
|
||||
|
||||
public function __construct($id, $name, $r, $g, $b, $isBad = false){
|
||||
$this->id = $id;
|
||||
$this->name = $name;
|
||||
$this->bad = (bool) $isBad;
|
||||
$this->setColor($r, $g, $b);
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setDuration($ticks){
|
||||
$this->duration = $ticks;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDuration(){
|
||||
return $this->duration;
|
||||
}
|
||||
|
||||
public function isVisible(){
|
||||
return $this->show;
|
||||
}
|
||||
|
||||
public function setVisible($bool){
|
||||
$this->show = (bool) $bool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAmplifier(){
|
||||
return $this->amplifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $amplifier
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setAmplifier($amplifier){
|
||||
$this->amplifier = (int) $amplifier;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isAmbient(){
|
||||
return $this->ambient;
|
||||
}
|
||||
|
||||
public function setAmbient($ambient = true){
|
||||
$this->ambient = (bool) $ambient;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isBad(){
|
||||
return $this->bad;
|
||||
}
|
||||
|
||||
public function canTick(){
|
||||
switch($this->id){
|
||||
case Effect::POISON:
|
||||
if(($interval = (25 >> $this->amplifier)) > 0){
|
||||
return ($this->duration % $interval) === 0;
|
||||
}
|
||||
return true;
|
||||
case Effect::WITHER:
|
||||
if(($interval = (50 >> $this->amplifier)) > 0){
|
||||
return ($this->duration % $interval) === 0;
|
||||
}
|
||||
return true;
|
||||
case Effect::REGENERATION:
|
||||
if(($interval = (40 >> $this->amplifier)) > 0){
|
||||
return ($this->duration % $interval) === 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public function applyEffect(Entity $entity){
|
||||
switch($this->id){
|
||||
case Effect::POISON:
|
||||
if($entity->getHealth() > 1){
|
||||
$ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
break;
|
||||
|
||||
case Effect::WITHER:
|
||||
$ev = new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
break;
|
||||
|
||||
case Effect::REGENERATION:
|
||||
if($entity->getHealth() < $entity->getMaxHealth()){
|
||||
$ev = new EntityRegainHealthEvent($entity, 1, EntityRegainHealthEvent::CAUSE_MAGIC);
|
||||
$entity->heal($ev->getAmount(), $ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return [$this->color >> 16, ($this->color >> 8) & 0xff, $this->color & 0xff];
|
||||
}
|
||||
|
||||
public function setColor($r, $g, $b){
|
||||
$this->color = (($r & 0xff) << 16) + (($g & 0xff) << 8) + ($b & 0xff);
|
||||
}
|
||||
|
||||
public function add(Entity $entity, $modify = false){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->eid = $entity->getId();
|
||||
$pk->effectId = $this->getId();
|
||||
$pk->amplifier = $this->getAmplifier();
|
||||
$pk->particles = $this->isVisible();
|
||||
$pk->duration = $this->getDuration();
|
||||
if($modify){
|
||||
$pk->eventId = MobEffectPacket::EVENT_MODIFY;
|
||||
}else{
|
||||
$pk->eventId = MobEffectPacket::EVENT_ADD;
|
||||
}
|
||||
|
||||
Server::broadcastPacket($entity->getViewers(), $pk);
|
||||
if($entity instanceof Player){
|
||||
$entity->dataPacket($pk);
|
||||
}
|
||||
|
||||
if($this->id === Effect::INVISIBILITY){
|
||||
$entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, true);
|
||||
$entity->setDataProperty(Entity::DATA_SHOW_NAMETAG, Entity::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function remove(Entity $entity){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->eid = $entity->getId();
|
||||
$pk->eventId = MobEffectPacket::EVENT_REMOVE;
|
||||
$pk->effectId = $this->getId();
|
||||
Server::broadcastPacket($entity->getViewers(), $pk);
|
||||
if($entity instanceof Player){
|
||||
$entity->dataPacket($pk);
|
||||
}
|
||||
|
||||
if($this->id === Effect::INVISIBILITY){
|
||||
$entity->setDataFlag(Entity::DATA_FLAGS, Entity::DATA_FLAG_INVISIBLE, false);
|
||||
$entity->setDataProperty(Entity::DATA_SHOW_NAMETAG, Entity::DATA_TYPE_BYTE, 1);
|
||||
}
|
||||
}
|
||||
}
|
@ -48,10 +48,11 @@ use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Double;
|
||||
use pocketmine\nbt\tag\Enum;
|
||||
use pocketmine\nbt\tag\Float;
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\nbt\tag\Short;
|
||||
use pocketmine\nbt\tag\String;
|
||||
use pocketmine\Network;
|
||||
use pocketmine\network\protocol\MovePlayerPacket;
|
||||
use pocketmine\network\protocol\MobEffectPacket;
|
||||
use pocketmine\network\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\protocol\SetEntityDataPacket;
|
||||
use pocketmine\network\protocol\SetTimePacket;
|
||||
@ -65,6 +66,32 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
const NETWORK_ID = -1;
|
||||
|
||||
|
||||
const DATA_TYPE_BYTE = 0;
|
||||
const DATA_TYPE_SHORT = 1;
|
||||
const DATA_TYPE_INT = 2;
|
||||
const DATA_TYPE_FLOAT = 3;
|
||||
const DATA_TYPE_STRING = 4;
|
||||
const DATA_TYPE_SLOT = 5;
|
||||
const DATA_TYPE_POS = 6;
|
||||
const DATA_TYPE_ROTATION = 7;
|
||||
const DATA_TYPE_LONG = 8;
|
||||
|
||||
const DATA_FLAGS = 0;
|
||||
const DATA_AIR = 1;
|
||||
const DATA_SHOW_NAMETAG = 3;
|
||||
const DATA_POTION_COLOR = 7;
|
||||
const DATA_POTION_VISIBLE = 8;
|
||||
const DATA_NO_AI = 15;
|
||||
|
||||
|
||||
const DATA_FLAG_ONFIRE = 0;
|
||||
const DATA_FLAG_SNEAKING = 1;
|
||||
const DATA_FLAG_RIDING = 2;
|
||||
const DATA_FLAG_ACTION = 4;
|
||||
const DATA_FLAG_INVISIBLE = 5;
|
||||
|
||||
|
||||
public static $entityCount = 1;
|
||||
/** @var Entity[] */
|
||||
private static $knownEntities = [];
|
||||
@ -75,8 +102,18 @@ abstract class Entity extends Location implements Metadatable{
|
||||
*/
|
||||
protected $hasSpawned = [];
|
||||
|
||||
/** @var Effect[] */
|
||||
protected $effects = [];
|
||||
|
||||
protected $id;
|
||||
|
||||
protected $dataFlags = 0;
|
||||
protected $dataProperties = [
|
||||
self::DATA_FLAGS => [self::DATA_TYPE_BYTE, 0],
|
||||
self::DATA_SHOW_NAMETAG => [self::DATA_TYPE_BYTE, 1],
|
||||
self::DATA_AIR => [self::DATA_TYPE_SHORT, 300]
|
||||
];
|
||||
|
||||
public $passenger = null;
|
||||
public $vehicle = null;
|
||||
|
||||
@ -134,7 +171,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
public $lastUpdate;
|
||||
public $maxFireTicks;
|
||||
public $fireTicks;
|
||||
public $airTicks;
|
||||
public $namedtag;
|
||||
public $canCollide = true;
|
||||
|
||||
@ -206,7 +242,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
if(!isset($this->namedtag->Air)){
|
||||
$this->namedtag->Air = new Short("Air", 300);
|
||||
}
|
||||
$this->airTicks = $this->namedtag["Air"];
|
||||
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $this->namedtag["Air"]);
|
||||
|
||||
if(!isset($this->namedtag->OnGround)){
|
||||
$this->namedtag->OnGround = new Byte("OnGround", 0);
|
||||
@ -228,6 +264,87 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Effect[]
|
||||
*/
|
||||
public function getEffects(){
|
||||
return $this->effects;
|
||||
}
|
||||
|
||||
public function removeAllEffects(){
|
||||
foreach($this->effects as $effect){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
}
|
||||
|
||||
public function removeEffect($effectId){
|
||||
if(isset($this->effects[$effectId])){
|
||||
$effect = $this->effects[$effectId];
|
||||
unset($this->effects[$effectId]);
|
||||
$effect->remove($this);
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
}
|
||||
|
||||
public function getEffect($effectId){
|
||||
return isset($this->effects[$effectId]) ? $this->effects[$effectId] : null;
|
||||
}
|
||||
|
||||
public function hasEffect($effectId){
|
||||
return isset($this->effects[$effectId]);
|
||||
}
|
||||
|
||||
public function addEffect(Effect $effect){
|
||||
if(isset($this->effects[$effect->getId()])){
|
||||
$oldEffect = $this->effects[$effect->getId()];
|
||||
if(
|
||||
$effect->getAmplifier() <= $oldEffect->getAmplifier()
|
||||
or ($effect->getAmplifier() === $oldEffect->getAmplifier()
|
||||
and $effect->getDuration() < $oldEffect->getDuration())
|
||||
){
|
||||
return;
|
||||
}
|
||||
$effect->add($this, true);
|
||||
}else{
|
||||
$effect->add($this, false);
|
||||
}
|
||||
|
||||
$this->effects[$effect->getId()] = $effect;
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
|
||||
protected function recalculateEffectColor(){
|
||||
$color = [0, 0, 0]; //RGB
|
||||
$count = 0;
|
||||
$ambient = true;
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->isVisible()){
|
||||
$c = $effect->getColor();
|
||||
$color[0] += $c[0] * ($effect->getAmplifier() + 1);
|
||||
$color[1] += $c[1] * ($effect->getAmplifier() + 1);
|
||||
$color[2] += $c[2] * ($effect->getAmplifier() + 1);
|
||||
$count += $effect->getAmplifier() + 1;
|
||||
if(!$effect->isAmbient()){
|
||||
$ambient = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($count > 0){
|
||||
$r = ($color[0] / $count) & 0xff;
|
||||
$g = ($color[1] / $count) & 0xff;
|
||||
$b = ($color[2] / $count) & 0xff;
|
||||
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, ($r << 16) + ($g << 8) + $b);
|
||||
$this->setDataProperty(Entity::DATA_POTION_VISIBLE, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0);
|
||||
}else{
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0);
|
||||
$this->setDataProperty(Entity::DATA_POTION_VISIBLE, Entity::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|string $type
|
||||
* @param FullChunk $chunk
|
||||
@ -295,12 +412,42 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$this->namedtag->FallDistance = new Float("FallDistance", $this->fallDistance);
|
||||
$this->namedtag->Fire = new Short("Fire", $this->fireTicks);
|
||||
$this->namedtag->Air = new Short("Air", $this->airTicks);
|
||||
$this->namedtag->Air = new Short("Air", $this->getDataProperty(self::DATA_AIR));
|
||||
$this->namedtag->OnGround = new Byte("OnGround", $this->onGround == true ? 1 : 0);
|
||||
$this->namedtag->Invulnerable = new Byte("Invulnerable", $this->invulnerable == true ? 1 : 0);
|
||||
|
||||
if(count($this->effects) > 0){
|
||||
$effects = [];
|
||||
foreach($this->effects as $effect){
|
||||
$effects[$effect->getId()] = new Compound($effect->getId(), [
|
||||
"Id" => new Byte("Id", $effect->getId()),
|
||||
"Amplifier" => new Byte("Amplifier", $effect->getAmplifier()),
|
||||
"Duration" => new Int("Duration", $effect->getDuration()),
|
||||
"Ambient" => new Byte("Ambient", 0),
|
||||
"ShowParticles" => new Byte("ShowParticles", $effect->isVisible() ? 1 : 0)
|
||||
]);
|
||||
}
|
||||
|
||||
$this->namedtag->ActiveEffects = new Enum("ActiveEffects", $effects);
|
||||
}else{
|
||||
unset($this->namedtag->ActiveEffects);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract function initEntity();
|
||||
protected function initEntity(){
|
||||
if(isset($this->namedtag->ActiveEffects)){
|
||||
foreach($this->namedtag->ActiveEffects->getValue() as $e){
|
||||
$effect = Effect::getEffect($e["Id"]);
|
||||
if($effect === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$effect->setAmplifier($e["Amplifier"])->setDuration($e["Duration"])->setVisible($e["ShowParticles"] > 0);
|
||||
|
||||
$this->addEffect($effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Player[]
|
||||
@ -318,30 +465,40 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
public function sendPotionEffects(Player $player){
|
||||
foreach($this->effects as $effect){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->effectId = $effect->getId();
|
||||
$pk->amplifier = $effect->getAmplifier();
|
||||
$pk->particles = $effect->isVisible();
|
||||
$pk->duration = $effect->getDuration();
|
||||
$pk->eventId = MobEffectPacket::EVENT_ADD;
|
||||
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[]|Player $player
|
||||
* @deprecated
|
||||
*/
|
||||
public function sendMetadata($player){
|
||||
$this->sendData($player);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[]|Player $player
|
||||
* @param array $data Properly formatted entity data, defaults to everything
|
||||
*/
|
||||
public function sendData($player, array $data = null){
|
||||
if($player instanceof Player){
|
||||
$player = [$player];
|
||||
}
|
||||
|
||||
$pk = new SetEntityDataPacket();
|
||||
$pk->eid = $this->id;
|
||||
$pk->metadata = $this->getData();
|
||||
$pk->encode();
|
||||
$pk->isEncoded = true;
|
||||
foreach($player as $p){
|
||||
if($p === $this){
|
||||
/** @var Player $p */
|
||||
$pk2 = new SetEntityDataPacket();
|
||||
$pk2->eid = 0;
|
||||
$pk2->metadata = $this->getData();
|
||||
$p->dataPacket($pk2);
|
||||
}else{
|
||||
$p->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
$pk->metadata = $data === null ? $this->dataProperties : $data;
|
||||
Server::broadcastPacket($player, $pk);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -357,18 +514,41 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $damage
|
||||
* @param int|EntityDamageEvent $source
|
||||
* @param float $damage
|
||||
* @param EntityDamageEvent $source
|
||||
*
|
||||
*/
|
||||
abstract function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC);
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if($this->hasEffect(Effect::FIRE_RESISTANCE)
|
||||
and $source->getCause() === EntityDamageEvent::CAUSE_FIRE
|
||||
and $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
|
||||
and $source->getCause() === EntityDamageEvent::CAUSE_LAVA){
|
||||
$source->setCancelled();
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setLastDamageCause($source);
|
||||
|
||||
$this->setHealth($this->getHealth() - $source->getFinalDamage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float $amount
|
||||
* @param int|EntityRegainHealthEvent $source
|
||||
* @param float $amount
|
||||
* @param EntityRegainHealthEvent $source
|
||||
*
|
||||
*/
|
||||
abstract function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC);
|
||||
public function heal($amount, EntityRegainHealthEvent $source){
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setHealth($this->getHealth() + $source->getAmount());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
@ -400,10 +580,10 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int|EntityDamageEvent $type
|
||||
* @param EntityDamageEvent $type
|
||||
*/
|
||||
public function setLastDamageCause($type){
|
||||
$this->lastDamageCause = $type instanceof EntityDamageEvent ? $type : $type;
|
||||
public function setLastDamageCause(EntityDamageEvent $type){
|
||||
$this->lastDamageCause = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -433,29 +613,25 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
protected function checkObstruction($x, $y, $z){
|
||||
$i = (int) $x;
|
||||
$j = (int) $y;
|
||||
$k = (int) $z;
|
||||
$i = Math::floorFloat($x);
|
||||
$j = Math::floorFloat($y);
|
||||
$k = Math::floorFloat($z);
|
||||
|
||||
$diffX = $x - $i;
|
||||
$diffY = $y - $j;
|
||||
$diffZ = $z - $k;
|
||||
|
||||
$list = $this->level->getCollisionBlocks($this->boundingBox);
|
||||
|
||||
$v = new Vector3($i, $j, $k);
|
||||
|
||||
if(count($list) === 0 and !$this->level->isFullBlock($v->setComponents($i, $j, $k))){
|
||||
return false;
|
||||
}else{
|
||||
if($this->level->isFullBlock($v)){
|
||||
$flag = !$this->level->isFullBlock($v->setComponents($i - 1, $j, $k));
|
||||
$flag1 = !$this->level->isFullBlock($v->setComponents($i + 1, $j, $k));
|
||||
//$flag2 = !$this->level->isFullBlock($v->setComponents($i, $j - 1, $k));
|
||||
$flag2 = !$this->level->isFullBlock($v->setComponents($i, $j - 1, $k));
|
||||
$flag3 = !$this->level->isFullBlock($v->setComponents($i, $j + 1, $k));
|
||||
$flag4 = !$this->level->isFullBlock($v->setComponents($i, $j, $k - 1));
|
||||
$flag5 = !$this->level->isFullBlock($v->setComponents($i, $j, $k + 1));
|
||||
|
||||
$direction = 3; //UP!
|
||||
$direction = -1;
|
||||
$limit = 9999;
|
||||
|
||||
if($flag){
|
||||
@ -468,6 +644,11 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$direction = 1;
|
||||
}
|
||||
|
||||
if($flag2 and $diffY < $limit){
|
||||
$limit = $diffY;
|
||||
$direction = 2;
|
||||
}
|
||||
|
||||
if($flag3 and 1 - $diffY < $limit){
|
||||
$limit = 1 - $diffY;
|
||||
$direction = 3;
|
||||
@ -496,7 +677,11 @@ abstract class Entity extends Location implements Metadatable{
|
||||
return true;
|
||||
}
|
||||
|
||||
//No direction 2
|
||||
if($direction === 2){
|
||||
$this->motionY = -$force;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if($direction === 3){
|
||||
$this->motionY = $force;
|
||||
@ -517,6 +702,8 @@ abstract class Entity extends Location implements Metadatable{
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function entityBaseTick($tickDiff = 1){
|
||||
@ -528,6 +715,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$isPlayer = $this instanceof Player;
|
||||
|
||||
if($this->dead === true){
|
||||
$this->removeAllEffects();
|
||||
$this->despawnFromAll();
|
||||
if(!$isPlayer){
|
||||
$this->close();
|
||||
@ -537,6 +725,18 @@ abstract class Entity extends Location implements Metadatable{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(count($this->effects) > 0){
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->canTick()){
|
||||
$effect->applyEffect($this);
|
||||
}
|
||||
$effect->setDuration($effect->getDuration() - $tickDiff);
|
||||
if($effect->getDuration() <= 0){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$hasUpdate = false;
|
||||
|
||||
$this->checkBlockCollision();
|
||||
@ -554,7 +754,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->fireTicks = 0;
|
||||
}
|
||||
}else{
|
||||
if(($this->fireTicks % 20) === 0 or $tickDiff > 20){
|
||||
if(!$this->hasEffect(Effect::FIRE_RESISTANCE) and ($this->fireTicks % 20) === 0 or $tickDiff > 20){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
@ -564,6 +764,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
if($this->fireTicks <= 0){
|
||||
$this->extinguish();
|
||||
}else{
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, true);
|
||||
$hasUpdate = true;
|
||||
}
|
||||
}
|
||||
@ -592,22 +793,11 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->lastYaw = $this->yaw;
|
||||
$this->lastPitch = $this->pitch;
|
||||
|
||||
if($this instanceof Human){
|
||||
$pk = new MovePlayerPacket();
|
||||
$pk->eid = $this->id;
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->bodyYaw = $this->yaw;
|
||||
Server::broadcastPacket($this->hasSpawned, $pk);
|
||||
}else{
|
||||
if(!($this instanceof Player)){
|
||||
foreach($this->hasSpawned as $player){
|
||||
$player->addEntityMovement($this->id, $this->x, $this->y + $this->getEyeHeight(), $this->z, $this->yaw, $this->pitch);
|
||||
$player->addEntityMovement($this->id, $this->x, $this->y + $this->height, $this->z, $this->yaw, $this->pitch, $this->yaw);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(($this->lastMotionX != $this->motionX or $this->lastMotionY != $this->motionY or $this->lastMotionZ != $this->motionZ)){
|
||||
@ -669,11 +859,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
if($ticks > $this->fireTicks){
|
||||
$this->fireTicks = $ticks;
|
||||
}
|
||||
|
||||
$this->sendMetadata($this->hasSpawned);
|
||||
if($this instanceof Player){
|
||||
$this->sendMetadata($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDirection(){
|
||||
@ -696,10 +881,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
public function extinguish(){
|
||||
$this->fireTicks = 0;
|
||||
$this->sendMetadata($this->hasSpawned);
|
||||
if($this instanceof Player){
|
||||
$this->sendMetadata($this);
|
||||
}
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, false);
|
||||
}
|
||||
|
||||
public function canTriggerWalking(){
|
||||
@ -827,7 +1009,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$newBB = $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz);
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $newBB->expand(-0.01, -0.01, -0.01));
|
||||
$list = $this->level->getCollisionCubes($this, $newBB->grow(-0.01, -0.01, -0.01), false);
|
||||
|
||||
if(count($list) === 0){
|
||||
$this->boundingBox = $newBB;
|
||||
@ -835,7 +1017,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$pos = new Vector3(
|
||||
($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
|
||||
$this->boundingBox->minY + $this->ySize,
|
||||
$this->boundingBox->minY,
|
||||
($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
|
||||
);
|
||||
|
||||
@ -924,8 +1106,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
//TODO: big messy loop
|
||||
}*/
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz));
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
|
||||
foreach($list as $bb){
|
||||
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
|
||||
@ -933,12 +1114,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$this->boundingBox->offset(0, $dy, 0);
|
||||
|
||||
if($movY != $dy){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}
|
||||
|
||||
$fallingFlag = ($this->onGround or ($dy != $movY and $movY < 0));
|
||||
|
||||
foreach($list as $bb){
|
||||
@ -947,24 +1122,12 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$this->boundingBox->offset($dx, 0, 0);
|
||||
|
||||
if($movX != $dx){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}
|
||||
|
||||
foreach($list as $bb){
|
||||
$dz = $bb->calculateZOffset($this->boundingBox, $dz);
|
||||
}
|
||||
|
||||
$this->boundingBox->offset(0, 0, $dz);
|
||||
|
||||
if($movZ != $dz){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}
|
||||
|
||||
|
||||
if($this->stepHeight > 0 and $fallingFlag and $this->ySize < 0.05 and ($movX != $dx or $movZ != $dz)){
|
||||
$cx = $dx;
|
||||
@ -978,7 +1141,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
$this->boundingBox->setBB($axisalignedbb);
|
||||
|
||||
$list = $this->level->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, $dy, $dz), false);
|
||||
$list = $this->level->getCollisionCubes($this, $this->boundingBox->addCoord($dx, $dy, $dz), false);
|
||||
|
||||
foreach($list as $bb){
|
||||
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
|
||||
@ -991,34 +1154,12 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
$this->boundingBox->offset($dx, 0, 0);
|
||||
if($movX != $dx){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}
|
||||
|
||||
foreach($list as $bb){
|
||||
$dz = $bb->calculateZOffset($this->boundingBox, $dz);
|
||||
}
|
||||
|
||||
$this->boundingBox->offset(0, 0, $dz);
|
||||
if($movZ != $dz){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}
|
||||
|
||||
if($dy == 0){
|
||||
$dx = 0;
|
||||
$dy = 0;
|
||||
$dz = 0;
|
||||
}else{
|
||||
$dy = -$this->stepHeight;
|
||||
foreach($list as $bb){
|
||||
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
|
||||
}
|
||||
$this->boundingBox->offset(0, $dy, 0);
|
||||
}
|
||||
|
||||
if(($cx ** 2 + $cz ** 2) >= ($dx ** 2 + $dz ** 2)){
|
||||
$dx = $cx;
|
||||
@ -1026,11 +1167,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$dz = $cz;
|
||||
$this->boundingBox->setBB($axisalignedbb1);
|
||||
}else{
|
||||
$diff = $this->boundingBox->minY - (int) $this->boundingBox->minY;
|
||||
|
||||
if($diff > 0){
|
||||
$this->ySize += $diff + 0.01;
|
||||
}
|
||||
$this->ySize += 0.5;
|
||||
}
|
||||
|
||||
}
|
||||
@ -1204,7 +1341,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
if(!$this->justCreated){
|
||||
if($this instanceof Player){
|
||||
$this->addEntityMotion(0, $this->motionX, $this->motionY, $this->motionZ);
|
||||
$this->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
}
|
||||
$this->updateMovement();
|
||||
}
|
||||
@ -1259,7 +1396,7 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->lastPitch = $this->pitch;
|
||||
|
||||
foreach($this->hasSpawned as $player){
|
||||
$player->addEntityMovement($this->getId(), $this->x, $this->y, $this->z, $this->yaw, $this->pitch);
|
||||
$player->addEntityMovement($this->getId(), $this->x, $this->y + $this->height, $this->z, $this->yaw, $this->pitch, $this->yaw);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1300,7 +1437,67 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function getData();
|
||||
/**
|
||||
* @param int $id
|
||||
* @param int $type
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function setDataProperty($id, $type, $value){
|
||||
if($this->getDataProperty($id) !== $value){
|
||||
$this->dataProperties[$id] = [$type, $value];
|
||||
|
||||
$targets = $this->hasSpawned;
|
||||
if($this instanceof Player){
|
||||
if(!$this->spawned){
|
||||
return;
|
||||
}
|
||||
$targets[] = $this;
|
||||
}
|
||||
|
||||
$this->sendData($targets, [$id => $this->dataProperties[$id]]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getDataProperty($id){
|
||||
return isset($this->dataProperties[$id]) ? $this->dataProperties[$id][1] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getDataPropertyType($id){
|
||||
return isset($this->dataProperties[$id]) ? $this->dataProperties[$id][0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $propertyId;
|
||||
* @param int $id
|
||||
* @param bool $value
|
||||
*/
|
||||
public function setDataFlag($propertyId, $id, $value = true, $type = self::DATA_TYPE_BYTE){
|
||||
if($this->getDataFlag($propertyId, $id) !== $value){
|
||||
$flags = (int) $this->getDataProperty($propertyId);
|
||||
$flags ^= 1 << $id;
|
||||
$this->setDataProperty($propertyId, $type, $flags);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $propertyId
|
||||
* @param int $id
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getDataFlag($propertyId, $id){
|
||||
return (((int) $this->getDataProperty($propertyId)) & (1 << $id)) > 0;
|
||||
}
|
||||
|
||||
public function __destruct(){
|
||||
$this->close();
|
||||
|
@ -38,6 +38,8 @@ use pocketmine\Player;
|
||||
class FallingSand extends Entity{
|
||||
const NETWORK_ID = 66;
|
||||
|
||||
const DATA_BLOCK_INFO = 20;
|
||||
|
||||
public $width = 0.98;
|
||||
public $length = 0.98;
|
||||
public $height = 0.98;
|
||||
@ -50,6 +52,7 @@ class FallingSand extends Entity{
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
if(isset($this->namedtag->TileID)){
|
||||
$this->blockId = $this->namedtag["TileID"];
|
||||
}elseif(isset($this->namedtag->Tile)){
|
||||
@ -63,15 +66,18 @@ class FallingSand extends Entity{
|
||||
|
||||
if($this->blockId === 0){
|
||||
$this->close();
|
||||
return;
|
||||
}
|
||||
|
||||
$this->setDataProperty(self::DATA_BLOCK_INFO, self::DATA_TYPE_INT, ($this->getBlock() << 8) | $this->getDamage());
|
||||
}
|
||||
|
||||
public function canCollideWith(Entity $entity){
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getData(){
|
||||
return [];
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
|
||||
}
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
@ -143,14 +149,6 @@ class FallingSand extends Entity{
|
||||
$this->namedtag->Data = new Byte("Data", $this->damage);
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
$pk = new AddEntityPacket();
|
||||
$pk->type = FallingSand::NETWORK_ID;
|
||||
@ -158,11 +156,12 @@ class FallingSand extends Entity{
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->did = -($this->getBlock() | $this->getDamage() << 0x10);
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,12 @@ use pocketmine\utils\TextFormat;
|
||||
|
||||
class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
const DATA_PLAYER_FLAG_SLEEP = 1;
|
||||
const DATA_PLAYER_FLAG_DEAD = 2;
|
||||
|
||||
const DATA_PLAYER_FLAGS = 16;
|
||||
const DATA_PLAYER_BED_POSITION = 17;
|
||||
|
||||
protected $nameTag = "TESTIFICATE";
|
||||
/** @var PlayerInventory */
|
||||
protected $inventory;
|
||||
@ -46,12 +52,41 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
public $height = 1.8;
|
||||
public $eyeHeight = 1.62;
|
||||
|
||||
protected $skin;
|
||||
protected $isSlim = false;
|
||||
|
||||
/**
|
||||
* @param string $str
|
||||
* @param bool $isSlim
|
||||
*/
|
||||
public function setSkin($str, $isSlim = false){
|
||||
$this->skin = $str;
|
||||
$this->isSlim = (bool) $isSlim;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getNameTag(){
|
||||
return $this->nameTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*/
|
||||
public function setNameTag($name){
|
||||
$this->nameTag = $name;
|
||||
}
|
||||
|
||||
public function getInventory(){
|
||||
return $this->inventory;
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
|
||||
$this->setDataFlag(self::DATA_PLAYER_FLAGS, self::DATA_PLAYER_FLAG_SLEEP, false);
|
||||
$this->setDataProperty(self::DATA_PLAYER_BED_POSITION, self::DATA_TYPE_POS, [0, 0, 0]);
|
||||
|
||||
$this->inventory = new PlayerInventory($this);
|
||||
if($this instanceof Player){
|
||||
$this->addWindow($this->inventory, 0);
|
||||
@ -66,9 +101,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
if($item["Slot"] >= 0 and $item["Slot"] < 9){ //Hotbar
|
||||
$this->inventory->setHotbarSlotIndex($item["Slot"], isset($item["TrueSlot"]) ? $item["TrueSlot"] : -1);
|
||||
}elseif($item["Slot"] >= 100 and $item["Slot"] < 104){ //Armor
|
||||
$this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, ItemItem::get($item["id"], $item["Damage"], $item["Count"]), $this);
|
||||
$this->inventory->setItem($this->inventory->getSize() + $item["Slot"] - 100, ItemItem::get($item["id"], $item["Damage"], $item["Count"]));
|
||||
}else{
|
||||
$this->inventory->setItem($item["Slot"] - 9, ItemItem::get($item["id"], $item["Damage"], $item["Count"]), $this);
|
||||
$this->inventory->setItem($item["Slot"] - 9, ItemItem::get($item["id"], $item["Damage"], $item["Count"]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,11 +188,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
|
||||
$pk = new AddPlayerPacket();
|
||||
$pk->clientID = 0;
|
||||
if($player->getRemoveFormat()){
|
||||
$pk->username = TextFormat::clean($this->nameTag);
|
||||
}else{
|
||||
$pk->username = $this->nameTag;
|
||||
}
|
||||
$pk->username = $this->nameTag;
|
||||
$pk->eid = $this->getId();
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
@ -167,7 +198,9 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
$item = $this->getInventory()->getItemInHand();
|
||||
$pk->item = $item->getId();
|
||||
$pk->meta = $item->getDamage();
|
||||
$pk->metadata = $this->getData();
|
||||
$pk->skin = $this->skin;
|
||||
$pk->slim = $this->isSlim;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
@ -186,21 +219,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
|
||||
}
|
||||
}
|
||||
|
||||
public function getData(){ //TODO
|
||||
$flags = 0;
|
||||
$flags |= $this->fireTicks > 0 ? 1 : 0;
|
||||
//$flags |= ($this->crouched === true ? 0b10:0) << 1;
|
||||
//$flags |= ($this->inAction === true ? 0b10000:0);
|
||||
$d = [
|
||||
0 => ["type" => 0, "value" => $flags],
|
||||
1 => ["type" => 1, "value" => $this->airTicks],
|
||||
16 => ["type" => 0, "value" => 0],
|
||||
17 => ["type" => 6, "value" => [0, 0, 0]],
|
||||
];
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
public function close(){
|
||||
if(!$this->closed){
|
||||
if(!($this instanceof Player) or $this->loggedIn){
|
||||
|
@ -14,19 +14,13 @@
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\scheduler;
|
||||
namespace pocketmine\entity;
|
||||
|
||||
class GarbageCollectionTask extends Task{
|
||||
class InstantEffect extends Effect{
|
||||
|
||||
public function onRun($currentTicks){
|
||||
gc_collect_cycles();
|
||||
memory_get_usage();
|
||||
memory_get_usage(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -52,6 +52,8 @@ class Item extends Entity{
|
||||
public $canCollide = false;
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
|
||||
$this->setMaxHealth(5);
|
||||
$this->setHealth($this->namedtag["Health"]);
|
||||
if(isset($this->namedtag->Age)){
|
||||
@ -72,6 +74,13 @@ class Item extends Entity{
|
||||
$this->server->getPluginManager()->callEvent(new ItemSpawnEvent($this));
|
||||
}
|
||||
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if($source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK){
|
||||
parent::attack($damage, $source);
|
||||
}
|
||||
}
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
if($this->closed !== false){
|
||||
return false;
|
||||
@ -92,7 +101,7 @@ class Item extends Entity{
|
||||
|
||||
$this->motionY -= $this->gravity;
|
||||
|
||||
$this->keepMovement = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
@ -128,22 +137,6 @@ class Item extends Entity{
|
||||
return $hasUpdate or !$this->onGround or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0;
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
if($source instanceof EntityDamageEvent){
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
$damage = $source->getFinalDamage();
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
}
|
||||
$this->setLastDamageCause($source);
|
||||
$this->setHealth($this->getHealth() - $damage);
|
||||
}
|
||||
|
||||
public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Item = new Compound("Item", [
|
||||
@ -162,15 +155,6 @@ class Item extends Entity{
|
||||
}
|
||||
}
|
||||
|
||||
public function getData(){
|
||||
$flags = 0;
|
||||
$flags |= $this->fireTicks > 0 ? 1 : 0;
|
||||
|
||||
return [
|
||||
0 => ["type" => 0, "value" => $flags]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ItemItem
|
||||
*/
|
||||
@ -230,14 +214,12 @@ class Item extends Entity{
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->roll = 0;
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->item = $this->getItem();
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,8 +42,12 @@ abstract class Living extends Entity implements Damageable{
|
||||
protected $drag = 0.02;
|
||||
|
||||
protected $attackTime = 0;
|
||||
|
||||
protected $invisible = false;
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
|
||||
if(isset($this->namedtag->HealF)){
|
||||
$this->namedtag->Health = new Short("Health", (int) $this->namedtag["HealF"]);
|
||||
unset($this->namedtag->HealF);
|
||||
@ -69,32 +73,28 @@ abstract class Living extends Entity implements Damageable{
|
||||
//return $this->getLevel()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null;
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
public function heal($amount, EntityRegainHealthEvent $source){
|
||||
parent::heal($amount, $source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attackTime = 0;
|
||||
}
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if($this->attackTime > 0 or $this->noDamageTicks > 0){
|
||||
$lastCause = $this->getLastDamageCause();
|
||||
if($lastCause instanceof EntityDamageEvent and $lastCause->getDamage() >= $damage){
|
||||
if($source instanceof EntityDamageEvent){
|
||||
$source->setCancelled();
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
$damage = $source->getFinalDamage();
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
return;
|
||||
}
|
||||
}elseif($source instanceof EntityDamageEvent){
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
$damage = $source->getFinalDamage();
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
if($lastCause !== null and $lastCause->getDamage() >= $damage){
|
||||
$source->setCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
$this->setLastDamageCause($source);
|
||||
parent::attack($damage, $source);
|
||||
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$e = $source->getDamager();
|
||||
@ -104,8 +104,6 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->knockBack($e, $damage, sin($yaw), cos($yaw), $source->getKnockBack());
|
||||
}
|
||||
|
||||
$this->setHealth($this->getHealth() - $damage);
|
||||
|
||||
$pk = new EntityEventPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->event = $this->getHealth() <= 0 ? 3 : 2; //Ouch!
|
||||
@ -133,10 +131,6 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->setMotion($motion);
|
||||
}
|
||||
|
||||
public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){
|
||||
$this->setHealth($this->getHealth() + $amount);
|
||||
}
|
||||
|
||||
public function kill(){
|
||||
if($this->dead){
|
||||
return;
|
||||
@ -172,17 +166,34 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
|
||||
if($this->dead !== true and $this->isInsideOfWater()){
|
||||
$hasUpdate = true;
|
||||
$this->airTicks -= $tickDiff;
|
||||
if($this->airTicks <= -20){
|
||||
$this->airTicks = 0;
|
||||
if($this->dead !== true and !$this->hasEffect(Effect::WATER_BREATHING) and $this->isInsideOfWater()){
|
||||
if($this instanceof WaterAnimal){
|
||||
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300);
|
||||
}else{
|
||||
$hasUpdate = true;
|
||||
$airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff;
|
||||
if($airTicks <= -20){
|
||||
$airTicks = 0;
|
||||
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks);
|
||||
}
|
||||
}else{
|
||||
$this->airTicks = 300;
|
||||
if($this instanceof WaterAnimal){
|
||||
$hasUpdate = true;
|
||||
$airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff;
|
||||
if($airTicks <= -20){
|
||||
$airTicks = 0;
|
||||
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks);
|
||||
}else{
|
||||
$this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300);
|
||||
}
|
||||
}
|
||||
|
||||
if($this->attackTime > 0){
|
||||
|
@ -44,7 +44,14 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
|
||||
public $canCollide = false;
|
||||
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
|
||||
if(isset($this->namedtag->Fuse)){
|
||||
$this->fuse = $this->namedtag["Fuse"];
|
||||
}else{
|
||||
@ -57,12 +64,6 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getData(){
|
||||
return [
|
||||
16 => ["type" => 0, "value" => $this->fuse],
|
||||
];
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Fuse = new Byte("Fuse", $this->fuse);
|
||||
@ -106,8 +107,6 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
if($this->fuse <= 0){
|
||||
$this->kill();
|
||||
$this->explode();
|
||||
}else{
|
||||
$this->sendMetadata($this->getViewers());
|
||||
}
|
||||
|
||||
}
|
||||
@ -116,14 +115,6 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
return $hasUpdate or $this->fuse >= 0 or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0;
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function explode(){
|
||||
$this->server->getPluginManager()->callEvent($ev = new ExplosionPrimeEvent($this, 4));
|
||||
|
||||
@ -143,11 +134,12 @@ class PrimedTNT extends Entity implements Explosive{
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->did = 0;
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
@ -28,18 +28,38 @@ use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\entity\ProjectileHitEvent;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\MovingObjectPosition;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\Compound;
|
||||
use pocketmine\nbt\tag\Short;
|
||||
|
||||
abstract class Projectile extends Entity{
|
||||
|
||||
const DATA_SHOOTER_ID = 17;
|
||||
|
||||
/** @var Entity */
|
||||
public $shootingEntity = null;
|
||||
protected $damage = 0;
|
||||
|
||||
private $hadCollision = false;
|
||||
public $hadCollision = false;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){
|
||||
$this->shootingEntity = $shootingEntity;
|
||||
if($shootingEntity !== null){
|
||||
$this->setDataProperty(self::DATA_SHOOTER_ID, self::DATA_TYPE_LONG, $shootingEntity->getId());
|
||||
}
|
||||
parent::__construct($chunk, $nbt);
|
||||
}
|
||||
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
|
||||
$this->setMaxHealth(1);
|
||||
$this->setHealth(1);
|
||||
if(isset($this->namedtag->Age)){
|
||||
@ -52,30 +72,11 @@ abstract class Projectile extends Entity{
|
||||
return $entity instanceof Living and !$this->onGround;
|
||||
}
|
||||
|
||||
public function getData(){
|
||||
$flags = 0;
|
||||
$flags |= $this->fireTicks > 0 ? 1 : 0;
|
||||
|
||||
return [
|
||||
0 => ["type" => 0, "value" => $flags],
|
||||
1 => ["type" => 1, "value" => $this->airTicks],
|
||||
16 => ["type" => 0, "value" => 0] //Is critical
|
||||
];
|
||||
}
|
||||
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Age = new Short("Age", $this->age);
|
||||
}
|
||||
|
||||
public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){
|
||||
|
||||
}
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
if($this->closed){
|
||||
return false;
|
||||
@ -95,8 +96,6 @@ abstract class Projectile extends Entity{
|
||||
$this->motionY -= $this->gravity;
|
||||
}
|
||||
|
||||
$this->keepMovement = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z);
|
||||
|
||||
$moveVector = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ);
|
||||
|
||||
$list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this);
|
||||
@ -138,6 +137,10 @@ abstract class Projectile extends Entity{
|
||||
$motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2);
|
||||
$damage = ceil($motion * $this->damage);
|
||||
|
||||
if($this instanceof Arrow and $this->isCritical){
|
||||
$damage += mt_rand(0, (int) ($damage / 2) + 1);
|
||||
}
|
||||
|
||||
if($this->shootingEntity === null){
|
||||
$ev = new EntityDamageByEntityEvent($this, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage);
|
||||
}else{
|
||||
@ -146,6 +149,7 @@ abstract class Projectile extends Entity{
|
||||
|
||||
$movingObjectPosition->entityHit->attack($ev->getFinalDamage(), $ev);
|
||||
|
||||
$this->hadCollision = true;
|
||||
|
||||
if($this->fireTicks > 0){
|
||||
$ev = new EntityCombustByEntityEvent($this, $movingObjectPosition->entityHit, 5);
|
||||
@ -170,7 +174,7 @@ abstract class Projectile extends Entity{
|
||||
$this->motionZ = 0;
|
||||
|
||||
$this->server->getPluginManager()->callEvent(new ProjectileHitEvent($this));
|
||||
}elseif(!$this->isCollided and !$this->hadCollision){
|
||||
}elseif(!$this->isCollided and $this->hadCollision){
|
||||
$this->hadCollision = false;
|
||||
}
|
||||
|
||||
|
@ -38,8 +38,7 @@ class Snowball extends Projectile{
|
||||
protected $drag = 0.01;
|
||||
|
||||
public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){
|
||||
$this->shootingEntity = $shootingEntity;
|
||||
parent::__construct($chunk, $nbt);
|
||||
parent::__construct($chunk, $nbt, $shootingEntity);
|
||||
}
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
@ -68,11 +67,12 @@ class Snowball extends Projectile{
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->did = 0; //TODO: send motion here
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
}
|
167
src/pocketmine/entity/Squid.php
Normal file
167
src/pocketmine/entity/Squid.php
Normal file
@ -0,0 +1,167 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item as ItemItem;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\network\protocol\EntityEventPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\Server;
|
||||
|
||||
class Squid extends WaterAnimal implements Ageable{
|
||||
const NETWORK_ID = 17;
|
||||
|
||||
public $width = 0.95;
|
||||
public $length = 0.95;
|
||||
public $height = 0.95;
|
||||
|
||||
/** @var Vector3 */
|
||||
public $swimDirection = null;
|
||||
public $swimSpeed = 0.1;
|
||||
|
||||
public function initEntity(){
|
||||
$this->setMaxHealth(5);
|
||||
parent::initEntity();
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Squid";
|
||||
}
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
parent::attack($damage, $source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
}
|
||||
|
||||
if($source instanceof EntityDamageByEntityEvent){
|
||||
$this->swimSpeed = mt_rand(150, 350) / 2000;
|
||||
$e = $source->getDamager();
|
||||
$this->swimDirection = (new Vector3($this->x - $e->x, $this->y - $e->y, $this->z - $e->z))->normalize();
|
||||
|
||||
$pk = new EntityEventPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->event = 15;
|
||||
Server::broadcastPacket($this->hasSpawned, $pk);
|
||||
}
|
||||
}
|
||||
|
||||
private function generateRandomDirection(){
|
||||
return new Vector3(mt_rand(-1000, 1000) / 1000, mt_rand(-500, 500) / 1000, mt_rand(-1000, 1000) / 1000);
|
||||
}
|
||||
|
||||
|
||||
public function onUpdate($currentTick){
|
||||
if($this->closed !== false){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($currentTick % 20 === 0 and mt_rand(0, 100) < 5){
|
||||
$this->swimDirection = null;
|
||||
}
|
||||
|
||||
$this->lastUpdate = $currentTick;
|
||||
|
||||
$this->timings->startTiming();
|
||||
|
||||
$hasUpdate = parent::onUpdate($currentTick);
|
||||
|
||||
if(!$this->dead){
|
||||
|
||||
if($this->y > 62 and $this->swimDirection !== null){
|
||||
$this->swimDirection->y = -0.5;
|
||||
}
|
||||
|
||||
$inWater = $this->isInsideOfWater();
|
||||
if(!$inWater){
|
||||
$this->motionY -= $this->gravity;
|
||||
$this->swimDirection = null;
|
||||
}elseif($this->swimDirection !== null){
|
||||
if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){
|
||||
$this->motionX = $this->swimDirection->x * $this->swimSpeed;
|
||||
$this->motionY = $this->swimDirection->y * $this->swimSpeed;
|
||||
$this->motionZ = $this->swimDirection->z * $this->swimSpeed;
|
||||
}
|
||||
}else{
|
||||
$this->swimDirection = $this->generateRandomDirection();
|
||||
$this->swimSpeed = mt_rand(50, 100) / 2000;
|
||||
}
|
||||
|
||||
$expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ);
|
||||
|
||||
$this->move($this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
if($expectedPos->distanceSquared($this) > 0){
|
||||
$this->swimDirection = $this->generateRandomDirection();
|
||||
$this->swimSpeed = mt_rand(50, 100) / 2000;
|
||||
}
|
||||
|
||||
$friction = 1 - $this->drag;
|
||||
|
||||
$this->motionX *= $friction;
|
||||
$this->motionY *= 1 - $this->drag;
|
||||
$this->motionZ *= $friction;
|
||||
|
||||
$f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2));
|
||||
$this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI);
|
||||
$this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI);
|
||||
|
||||
if($this->onGround){
|
||||
$this->motionY *= -0.5;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->timings->stopTiming();
|
||||
|
||||
return $hasUpdate or !$this->onGround or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0;
|
||||
}
|
||||
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
$pk = new AddEntityPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->type = Squid::NETWORK_ID;
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
|
||||
public function getDrops(){
|
||||
return [
|
||||
ItemItem::get(ItemItem::DYE, 0, mt_rand(1, 3))
|
||||
];
|
||||
}
|
||||
}
|
@ -23,7 +23,7 @@ namespace pocketmine\entity;
|
||||
|
||||
|
||||
use pocketmine\nbt\tag\Int;
|
||||
use pocketmine\network\protocol\AddMobPacket;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Villager extends Creature implements NPC, Ageable{
|
||||
@ -52,37 +52,23 @@ class Villager extends Creature implements NPC, Ageable{
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
$pk = new AddMobPacket();
|
||||
$pk = new AddEntityPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->type = Villager::NETWORK_ID;
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->metadata = $this->getData();
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
|
||||
public function getData(){ //TODO
|
||||
$flags = 0;
|
||||
$flags |= $this->fireTicks > 0 ? 1 : 0;
|
||||
//$flags |= ($this->crouched === true ? 0b10:0) << 1;
|
||||
//$flags |= ($this->inAction === true ? 0b10000:0);
|
||||
$d = [
|
||||
0 => ["type" => 0, "value" => $flags],
|
||||
1 => ["type" => 1, "value" => $this->airTicks],
|
||||
16 => ["type" => 0, "value" => 0],
|
||||
17 => ["type" => 6, "value" => [0, 0, 0]],
|
||||
];
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the villager profession
|
||||
*
|
||||
@ -95,4 +81,8 @@ class Villager extends Creature implements NPC, Ageable{
|
||||
public function getProfession(){
|
||||
return $this->namedtag["Profession"];
|
||||
}
|
||||
}
|
||||
|
||||
public function isBaby(){
|
||||
return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY);
|
||||
}
|
||||
}
|
||||
|
35
src/pocketmine/entity/WaterAnimal.php
Normal file
35
src/pocketmine/entity/WaterAnimal.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\entity;
|
||||
|
||||
abstract class WaterAnimal extends Creature implements Ageable{
|
||||
public function initEntity(){
|
||||
parent::initEntity();
|
||||
if($this->getDataProperty(self::DATA_AGEABLE_FLAGS) === null){
|
||||
$this->setDataProperty(self::DATA_AGEABLE_FLAGS, self::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public function isBaby(){
|
||||
return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY);
|
||||
}
|
||||
}
|
@ -24,4 +24,9 @@ namespace pocketmine\entity;
|
||||
|
||||
class Wolf extends Animal implements Tameable{
|
||||
|
||||
const NETWORK_ID = 14;
|
||||
|
||||
public function getName(){
|
||||
return "Wolf";
|
||||
}
|
||||
}
|
@ -24,7 +24,7 @@ namespace pocketmine\entity;
|
||||
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\item\Item as ItemItem;
|
||||
use pocketmine\network\protocol\AddMobPacket;
|
||||
use pocketmine\network\protocol\AddEntityPacket;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Zombie extends Monster{
|
||||
@ -39,38 +39,23 @@ class Zombie extends Monster{
|
||||
}
|
||||
|
||||
public function spawnTo(Player $player){
|
||||
|
||||
$pk = new AddMobPacket();
|
||||
$pk = new AddEntityPacket();
|
||||
$pk->eid = $this->getId();
|
||||
$pk->type = Zombie::NETWORK_ID;
|
||||
$pk->x = $this->x;
|
||||
$pk->y = $this->y;
|
||||
$pk->z = $this->z;
|
||||
$pk->speedX = $this->motionX;
|
||||
$pk->speedY = $this->motionY;
|
||||
$pk->speedZ = $this->motionZ;
|
||||
$pk->yaw = $this->yaw;
|
||||
$pk->pitch = $this->pitch;
|
||||
$pk->metadata = $this->getData();
|
||||
$pk->metadata = $this->dataProperties;
|
||||
$player->dataPacket($pk);
|
||||
|
||||
$player->addEntityMotion($this->getId(), $this->motionX, $this->motionY, $this->motionZ);
|
||||
|
||||
parent::spawnTo($player);
|
||||
}
|
||||
|
||||
public function getData(){ //TODO
|
||||
$flags = 0;
|
||||
$flags |= $this->fireTicks > 0 ? 1 : 0;
|
||||
//$flags |= ($this->crouched === true ? 0b10:0) << 1;
|
||||
//$flags |= ($this->inAction === true ? 0b10000:0);
|
||||
$d = [
|
||||
0 => ["type" => 0, "value" => $flags],
|
||||
1 => ["type" => 1, "value" => $this->airTicks],
|
||||
16 => ["type" => 0, "value" => 0],
|
||||
17 => ["type" => 6, "value" => [0, 0, 0]],
|
||||
];
|
||||
|
||||
return $d;
|
||||
}
|
||||
|
||||
public function getDrops(){
|
||||
$drops = [
|
||||
ItemItem::get(ItemItem::FEATHER, 0, 1)
|
||||
@ -93,4 +78,4 @@ class Zombie extends Monster{
|
||||
|
||||
return $drops;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
|
||||
class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
@ -41,6 +42,17 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
$this->damager = $damager;
|
||||
$this->knockBack = $knockBack;
|
||||
parent::__construct($entity, $cause, $damage);
|
||||
$this->addAttackerModifiers($damager);
|
||||
}
|
||||
|
||||
protected function addAttackerModifiers(Entity $damager){
|
||||
if($damager->hasEffect(Effect::STRENGTH)){
|
||||
$this->setDamage($this->getDamage(self::MODIFIER_BASE) * 0.3 * ($damager->getEffect(Effect::STRENGTH)->getAmplifier() + 1), self::MODIFIER_STRENGTH);
|
||||
}
|
||||
|
||||
if($damager->hasEffect(Effect::WEAKNESS)){
|
||||
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.2 * ($damager->getEffect(Effect::WEAKNESS)->getAmplifier() + 1)), self::MODIFIER_WEAKNESS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
|
||||
@ -29,6 +30,9 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
|
||||
const MODIFIER_BASE = 0;
|
||||
const MODIFIER_ARMOR = 1;
|
||||
const MODIFIER_STRENGTH = 2;
|
||||
const MODIFIER_WEAKNESS = 3;
|
||||
const MODIFIER_RESISTANCE = 4;
|
||||
|
||||
const CAUSE_CONTACT = 0;
|
||||
const CAUSE_ENTITY_ATTACK = 1;
|
||||
@ -76,6 +80,10 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
if(!isset($this->modifiers[self::MODIFIER_BASE])){
|
||||
throw new \InvalidArgumentException("BASE Damage modifier missing");
|
||||
}
|
||||
|
||||
if($entity->hasEffect(Effect::DAMAGE_RESISTANCE)){
|
||||
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.20 * ($entity->getEffect(Effect::DAMAGE_RESISTANCE)->getAmplifier() + 1)), self::MODIFIER_RESISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -118,9 +126,6 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
* @throws \UnexpectedValueException
|
||||
*/
|
||||
public function setDamage($damage, $type = self::MODIFIER_BASE){
|
||||
if(!isset($this->modifiers[$type])){
|
||||
throw new \UnexpectedValueException($type . " is not applicable to " . $this->getEntity());
|
||||
}
|
||||
$this->modifiers[$type] = $damage;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,8 @@ namespace pocketmine\event\player;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\Position;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
|
||||
/**
|
||||
@ -32,33 +34,73 @@ use pocketmine\Player;
|
||||
class PlayerInteractEvent extends PlayerEvent implements Cancellable{
|
||||
public static $handlerList = null;
|
||||
|
||||
const LEFT_CLICK_BLOCK = 0;
|
||||
const RIGHT_CLICK_BLOCK = 1;
|
||||
const LEFT_CLICK_AIR = 2;
|
||||
const RIGHT_CLICK_AIR = 3;
|
||||
const PHYSICAL = 4;
|
||||
|
||||
/**
|
||||
* @var \pocketmine\block\Block;
|
||||
*/
|
||||
protected $blockTouched;
|
||||
|
||||
protected $touchVector;
|
||||
|
||||
/** @var int */
|
||||
protected $blockFace;
|
||||
|
||||
/** @var \pocketmine\item\Item */
|
||||
protected $item;
|
||||
|
||||
protected $action;
|
||||
|
||||
public function __construct(Player $player, Item $item, Block $block, $face){
|
||||
$this->blockTouched = $block;
|
||||
public function __construct(Player $player, Item $item, Vector3 $block, $face, $action = PlayerInteractEvent::RIGHT_CLICK_BLOCK){
|
||||
if($block instanceof Block){
|
||||
$this->blockTouched = $block;
|
||||
$this->touchVector = new Vector3(0, 0, 0);
|
||||
}else{
|
||||
$this->touchVector = $block;
|
||||
Block::get(0, 0, new Position(0, 0, 0, $player->level));
|
||||
}
|
||||
$this->player = $player;
|
||||
$this->item = $item;
|
||||
$this->blockFace = (int) $face;
|
||||
$this->action = (int) $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getAction(){
|
||||
return $this->action;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getItem(){
|
||||
return $this->item;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block
|
||||
*/
|
||||
public function getBlock(){
|
||||
return $this->blockTouched;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Vector3
|
||||
*/
|
||||
public function getTouchVector(){
|
||||
return $this->touchVector;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getFace(){
|
||||
return $this->blockFace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -122,12 +122,12 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
}
|
||||
|
||||
public function setItem($index, Item $item, $source = null){
|
||||
public function setItem($index, Item $item){
|
||||
$item = clone $item;
|
||||
if($index < 0 or $index >= $this->size){
|
||||
return false;
|
||||
}elseif($item->getId() === 0 or $item->getCount() <= 0){
|
||||
return $this->clear($index, $source);
|
||||
return $this->clear($index);
|
||||
}
|
||||
|
||||
$holder = $this->getHolder();
|
||||
@ -142,7 +142,7 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
$old = $this->getItem($index);
|
||||
$this->slots[$index] = clone $item;
|
||||
$this->onSlotChange($index, $old, $source);
|
||||
$this->onSlotChange($index, $old);
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -227,14 +227,6 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function addItem(...$slots){
|
||||
if(isset($slots[$end = count($slots) - 1]) and $slots[$end] instanceof Player){
|
||||
/** @var Player $source */
|
||||
$source = $slots[$end];
|
||||
unset($slots[$end]);
|
||||
}else{
|
||||
$source = null;
|
||||
}
|
||||
|
||||
/** @var Item[] $itemSlots */
|
||||
/** @var Item[] $slots */
|
||||
$itemSlots = [];
|
||||
@ -258,7 +250,7 @@ abstract class BaseInventory implements Inventory{
|
||||
if($amount > 0){
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item->setCount($item->getCount() + $amount);
|
||||
$this->setItem($i, $item, $source);
|
||||
$this->setItem($i, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
unset($itemSlots[$index]);
|
||||
}
|
||||
@ -273,12 +265,13 @@ abstract class BaseInventory implements Inventory{
|
||||
|
||||
if(count($itemSlots) > 0 and count($emptySlots) > 0){
|
||||
foreach($emptySlots as $slotIndex){
|
||||
//This loop only gets the first item, then goes to the next empty slot
|
||||
foreach($itemSlots as $index => $slot){
|
||||
$amount = min($slot->getMaxStackSize(), $slot->getCount(), $this->getMaxStackSize());
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item = clone $slot;
|
||||
$item->setCount($amount);
|
||||
$this->setItem($slotIndex, $item, $source);
|
||||
$this->setItem($slotIndex, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
unset($itemSlots[$index]);
|
||||
}
|
||||
@ -291,14 +284,6 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
public function removeItem(...$slots){
|
||||
if(isset($slots[$end = count($slots) - 1]) and $slots[$end] instanceof Player){
|
||||
/** @var Player $source */
|
||||
$source = $slots[$end];
|
||||
unset($slots[$end]);
|
||||
}else{
|
||||
$source = null;
|
||||
}
|
||||
|
||||
/** @var Item[] $itemSlots */
|
||||
/** @var Item[] $slots */
|
||||
$itemSlots = [];
|
||||
@ -319,7 +304,7 @@ abstract class BaseInventory implements Inventory{
|
||||
$amount = min($item->getCount(), $slot->getCount());
|
||||
$slot->setCount($slot->getCount() - $amount);
|
||||
$item->setCount($item->getCount() - $amount);
|
||||
$this->setItem($i, $item, $source);
|
||||
$this->setItem($i, $item);
|
||||
if($slot->getCount() <= 0){
|
||||
unset($itemSlots[$index]);
|
||||
}
|
||||
@ -334,7 +319,7 @@ abstract class BaseInventory implements Inventory{
|
||||
return $itemSlots;
|
||||
}
|
||||
|
||||
public function clear($index, $source = null){
|
||||
public function clear($index){
|
||||
if(isset($this->slots[$index])){
|
||||
$item = Item::get(Item::AIR, null, 0);
|
||||
$old = $this->slots[$index];
|
||||
@ -353,7 +338,7 @@ abstract class BaseInventory implements Inventory{
|
||||
unset($this->slots[$index]);
|
||||
}
|
||||
|
||||
$this->onSlotChange($index, $old, $source);
|
||||
$this->onSlotChange($index, $old);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -366,20 +351,10 @@ abstract class BaseInventory implements Inventory{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player $source
|
||||
*
|
||||
* @return Player[]
|
||||
*/
|
||||
public function getViewers($source = null){
|
||||
$viewers = [];
|
||||
foreach($this->viewers as $viewer){
|
||||
if($viewer === $source){
|
||||
continue;
|
||||
}
|
||||
$viewers[] = $viewer;
|
||||
}
|
||||
|
||||
return $viewers;
|
||||
public function getViewers(){
|
||||
return $this->viewers;
|
||||
}
|
||||
|
||||
public function getHolder(){
|
||||
@ -412,8 +387,8 @@ abstract class BaseInventory implements Inventory{
|
||||
unset($this->viewers[spl_object_hash($who)]);
|
||||
}
|
||||
|
||||
public function onSlotChange($index, $before, $source = null){
|
||||
$this->sendSlot($index, $this->getViewers($source));
|
||||
public function onSlotChange($index, $before){
|
||||
$this->sendSlot($index, $this->getViewers());
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,7 +128,8 @@ class CraftingManager{
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::NETHER_BRICK, 0, 1), Item::get(Item::NETHERRACK, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::COOKED_PORKCHOP, 0, 1), Item::get(Item::RAW_PORKCHOP, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::BRICK, 0, 1), Item::get(Item::CLAY, 0, 1)));
|
||||
//$this->registerRecipe(new FurnaceRecipe(Item::get(Item::COOKED_FISH, 0, 1), Item::get(Item::RAW_FISH, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::COOKED_FISH, 0, 1), Item::get(Item::RAW_FISH, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::COOKED_FISH, 1, 1), Item::get(Item::RAW_FISH, 1, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::DYE, 2, 1), Item::get(Item::CACTUS, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::DYE, 1, 1), Item::get(Item::RED_MUSHROOM, 0, 1)));
|
||||
$this->registerRecipe(new FurnaceRecipe(Item::get(Item::STEAK, 0, 1), Item::get(Item::RAW_BEEF, 0, 1)));
|
||||
@ -272,7 +273,7 @@ class CraftingManager{
|
||||
Item::IRON_BLOCK => Item::IRON_INGOT,
|
||||
Item::DIAMOND_BLOCK => Item::DIAMOND,
|
||||
Item::EMERALD_BLOCK => Item::EMERALD,
|
||||
//Item::REDSTONE_BLOCK => Item::REDSTONE_DUST,
|
||||
Item::REDSTONE_BLOCK => Item::REDSTONE_DUST,
|
||||
Item::COAL_BLOCK => Item::COAL,
|
||||
Item::HAY_BALE => Item::WHEAT,
|
||||
];
|
||||
|
@ -53,12 +53,12 @@ class DoubleChestInventory extends ChestInventory implements InventoryHolder{
|
||||
return $index < $this->left->getSize() ? $this->left->getItem($index) : $this->right->getItem($index - $this->right->getSize());
|
||||
}
|
||||
|
||||
public function setItem($index, Item $item, $source = null){
|
||||
return $index < $this->left->getSize() ? $this->left->setItem($index, $item, $source) : $this->right->setItem($index - $this->right->getSize(), $item, $source);
|
||||
public function setItem($index, Item $item){
|
||||
return $index < $this->left->getSize() ? $this->left->setItem($index, $item) : $this->right->setItem($index - $this->right->getSize(), $item);
|
||||
}
|
||||
|
||||
public function clear($index, $source = null){
|
||||
return $index < $this->left->getSize() ? $this->left->clear($index, $source) : $this->right->clear($index - $this->right->getSize(), $source);
|
||||
public function clear($index){
|
||||
return $index < $this->left->getSize() ? $this->left->clear($index) : $this->right->clear($index - $this->right->getSize());
|
||||
}
|
||||
|
||||
public function getContents(){
|
||||
|
@ -85,8 +85,8 @@ class FurnaceInventory extends ContainerInventory{
|
||||
return $this->setItem(0, $item);
|
||||
}
|
||||
|
||||
public function onSlotChange($index, $before, $source = null){
|
||||
parent::onSlotChange($index, $before, $source);
|
||||
public function onSlotChange($index, $before){
|
||||
parent::onSlotChange($index, $before);
|
||||
|
||||
$this->getHolder()->scheduleUpdate();
|
||||
}
|
||||
|
@ -57,17 +57,16 @@ interface Inventory{
|
||||
*
|
||||
* @param int $index
|
||||
* @param Item $item
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setItem($index, Item $item, $source = null);
|
||||
public function setItem($index, Item $item);
|
||||
|
||||
/**
|
||||
* Stores the given Items in the inventory. This will try to fill
|
||||
* existing stacks and empty slots as well as it can.
|
||||
*
|
||||
* Returns the Items that did not fit. A Player source can be set at the end
|
||||
* Returns the Items that did not fit.
|
||||
*
|
||||
* @param Item ...$item
|
||||
*
|
||||
@ -86,7 +85,7 @@ interface Inventory{
|
||||
|
||||
/**
|
||||
* Removes the given Item from the inventory.
|
||||
* It will return the Items that couldn't be removed. A Player source can be set at the end
|
||||
* It will return the Items that couldn't be removed.
|
||||
*
|
||||
* @param Item ...$item
|
||||
*
|
||||
@ -163,11 +162,10 @@ interface Inventory{
|
||||
* Will clear a specific slot
|
||||
*
|
||||
* @param int $index
|
||||
* @param Player $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function clear($index, $source = null);
|
||||
public function clear($index);
|
||||
|
||||
/**
|
||||
* Clears all the slots
|
||||
@ -176,13 +174,11 @@ interface Inventory{
|
||||
|
||||
/**
|
||||
* Gets all the Players viewing the inventory
|
||||
* Players will be viewing their inventory at all times, even when not open.
|
||||
*
|
||||
* @param Player $source
|
||||
* Players will view their inventory at all times, even when not open.
|
||||
*
|
||||
* @return Player[]
|
||||
*/
|
||||
public function getViewers($source = null);
|
||||
public function getViewers();
|
||||
|
||||
/**
|
||||
* @return InventoryType
|
||||
@ -218,7 +214,6 @@ interface Inventory{
|
||||
/**
|
||||
* @param int $index
|
||||
* @param Item $before
|
||||
* @param Player $source
|
||||
*/
|
||||
public function onSlotChange($index, $before, $source = null);
|
||||
public function onSlotChange($index, $before);
|
||||
}
|
||||
|
@ -88,12 +88,11 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
/**
|
||||
* @param Item $item
|
||||
* @param $source
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function setItemInHand(Item $item, $source = null){
|
||||
return $this->setItem($this->getHeldItemSlot(), $item, $source);
|
||||
public function setItemInHand(Item $item){
|
||||
return $this->setItem($this->getHeldItemSlot(), $item);
|
||||
}
|
||||
|
||||
public function getHeldItemSlot(){
|
||||
@ -146,20 +145,23 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
foreach($target as $player){
|
||||
if($player === $this->getHolder()){
|
||||
//TODO: Check if Mojang enabled sending a single slot this
|
||||
//$this->sendSlot($this->getHeldItemSlot());
|
||||
$this->sendContents($player);
|
||||
$this->sendSlot($this->getHeldItemSlot(), $player);
|
||||
}else{
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onSlotChange($index, $before, $source = null){
|
||||
parent::onSlotChange($index, $before, $source);
|
||||
public function onSlotChange($index, $before){
|
||||
$holder = $this->getHolder();
|
||||
if($holder instanceof Player and !$holder->spawned){
|
||||
return;
|
||||
}
|
||||
|
||||
parent::onSlotChange($index, $before);
|
||||
|
||||
if($index >= $this->getSize()){
|
||||
$this->sendArmorSlot($index, $this->getViewers($source));
|
||||
$this->sendArmorSlot($index, $this->getViewers());
|
||||
$this->sendArmorSlot($index, $this->getHolder()->getViewers());
|
||||
}
|
||||
}
|
||||
@ -172,8 +174,8 @@ class PlayerInventory extends BaseInventory{
|
||||
return $this->getItem($this->getSize() + $index);
|
||||
}
|
||||
|
||||
public function setArmorItem($index, Item $item, $source = null){
|
||||
return $this->setItem($this->getSize() + $index, $item, $source);
|
||||
public function setArmorItem($index, Item $item){
|
||||
return $this->setItem($this->getSize() + $index, $item);
|
||||
}
|
||||
|
||||
public function getHelmet(){
|
||||
@ -208,11 +210,11 @@ class PlayerInventory extends BaseInventory{
|
||||
return $this->setItem($this->getSize() + 3, $boots);
|
||||
}
|
||||
|
||||
public function setItem($index, Item $item, $source = null){
|
||||
public function setItem($index, Item $item){
|
||||
if($index < 0 or $index >= $this->size){
|
||||
return false;
|
||||
}elseif($item->getId() === 0 or $item->getCount() <= 0){
|
||||
return $this->clear($index, $source);
|
||||
return $this->clear($index);
|
||||
}
|
||||
|
||||
if($index >= $this->getSize()){ //Armor change
|
||||
@ -242,12 +244,12 @@ class PlayerInventory extends BaseInventory{
|
||||
|
||||
$old = $this->getItem($index);
|
||||
$this->slots[$index] = clone $item;
|
||||
$this->onSlotChange($index, $old, $source);
|
||||
$this->onSlotChange($index, $old);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function clear($index, $source = null){
|
||||
public function clear($index){
|
||||
if(isset($this->slots[$index])){
|
||||
$item = Item::get(Item::AIR, null, 0);
|
||||
$old = $this->slots[$index];
|
||||
@ -280,7 +282,7 @@ class PlayerInventory extends BaseInventory{
|
||||
unset($this->slots[$index]);
|
||||
}
|
||||
|
||||
$this->onSlotChange($index, $old, $source);
|
||||
$this->onSlotChange($index, $old);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -393,9 +395,6 @@ class PlayerInventory extends BaseInventory{
|
||||
foreach($target as $player){
|
||||
if($player === $this->getHolder()){
|
||||
/** @var Player $player */
|
||||
//$pk2 = clone $pk;
|
||||
//$pk2->eid = 0;
|
||||
|
||||
$pk2 = new ContainerSetSlotPacket();
|
||||
$pk2->windowid = 0x78; //Armor window id constant
|
||||
$pk2->slot = $index;
|
||||
@ -454,7 +453,8 @@ class PlayerInventory extends BaseInventory{
|
||||
foreach($target as $player){
|
||||
if($player === $this->getHolder()){
|
||||
/** @var Player $player */
|
||||
$this->sendContents($player); //#blamemojang
|
||||
$pk->windowid = 0;
|
||||
$player->dataPacket(clone $pk);
|
||||
}else{
|
||||
if(($id = $player->getWindowId($this)) === -1){
|
||||
$this->close($player);
|
||||
|
@ -151,7 +151,7 @@ class SimpleTransactionGroup implements TransactionGroup{
|
||||
}
|
||||
|
||||
foreach($this->transactions as $transaction){
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem(), $this->getSource());
|
||||
$transaction->getInventory()->setItem($transaction->getSlot(), $transaction->getTargetItem());
|
||||
}
|
||||
|
||||
$this->hasExecuted = true;
|
||||
|
33
src/pocketmine/item/CookedFish.php
Normal file
33
src/pocketmine/item/CookedFish.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
|
||||
class CookedFish extends Item{
|
||||
public function __construct($meta = 0, $count = 1){
|
||||
parent::__construct(self::RAW_FISH, $meta, $count, "Cooked Fish");
|
||||
if($this->meta === 1){
|
||||
$this->name = "Cooked Salmon";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
37
src/pocketmine/item/Fish.php
Normal file
37
src/pocketmine/item/Fish.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\item;
|
||||
|
||||
|
||||
class Fish extends Item{
|
||||
public function __construct($meta = 0, $count = 1){
|
||||
parent::__construct(self::RAW_FISH, $meta, $count, "Raw Fish");
|
||||
if($this->meta === 1){
|
||||
$this->name = "Raw Salmon";
|
||||
}elseif($this->meta === 2){
|
||||
$this->name = "Clownfish";
|
||||
}elseif($this->meta === 3){
|
||||
$this->name = "Pufferfish";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -192,6 +192,8 @@ class Item{
|
||||
const CARROT_BLOCK = 141;
|
||||
const POTATO_BLOCK = 142;
|
||||
|
||||
const REDSTONE_BLOCK = 152;
|
||||
|
||||
const QUARTZ_BLOCK = 155;
|
||||
const QUARTZ_STAIRS = 156;
|
||||
const DOUBLE_WOOD_SLAB = 157;
|
||||
@ -339,8 +341,8 @@ class Item{
|
||||
|
||||
const CLOCK = 347;
|
||||
const GLOWSTONE_DUST = 348;
|
||||
//const RAW_FISH = 349;
|
||||
//const COOKED_FISH = 350;
|
||||
const RAW_FISH = 349;
|
||||
const COOKED_FISH = 350;
|
||||
const DYE = 351;
|
||||
const BONE = 352;
|
||||
const SUGAR = 353;
|
||||
@ -348,7 +350,7 @@ class Item{
|
||||
const BED = 355;
|
||||
|
||||
|
||||
//const COOKIE = 357;
|
||||
const COOKIE = 357;
|
||||
|
||||
|
||||
const SHEARS = 359;
|
||||
@ -479,6 +481,9 @@ class Item{
|
||||
self::$list[self::SHEARS] = Shears::class;
|
||||
self::$list[self::BOW] = Bow::class;
|
||||
|
||||
self::$list[self::RAW_FISH] = Fish::class;
|
||||
self::$list[self::COOKED_FISH] = CookedFish::class;
|
||||
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
if(Block::$list[$i] !== null){
|
||||
self::$list[$i] = Block::$list[$i];
|
||||
|
@ -21,6 +21,8 @@
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
|
||||
interface ChunkManager{
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
@ -63,4 +65,26 @@ interface ChunkManager{
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ);
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk = null);
|
||||
|
||||
/**
|
||||
* Gets the level seed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSeed();
|
||||
}
|
@ -66,7 +66,9 @@ use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\format\generic\BaseLevelProvider;
|
||||
use pocketmine\level\format\generic\EmptyChunkSection;
|
||||
use pocketmine\level\format\LevelProvider;
|
||||
use pocketmine\level\generator\GenerationTask;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\PopulationTask;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Math;
|
||||
use pocketmine\math\Vector2;
|
||||
@ -93,8 +95,13 @@ use pocketmine\tile\Tile;
|
||||
use pocketmine\utils\Cache;
|
||||
use pocketmine\utils\LevelException;
|
||||
use pocketmine\utils\MainLogger;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\utils\ReversePriorityQueue;
|
||||
use pocketmine\utils\TextFormat;
|
||||
use pocketmine\level\particle\Particle;
|
||||
use pocketmine\level\sound\Sound;
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\level\particle\DestroyBlockParticle;
|
||||
|
||||
#include <rules/Level.h>
|
||||
|
||||
@ -156,9 +163,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var FullChunk[]|Chunk[] */
|
||||
private $chunks = [];
|
||||
|
||||
/** @var Block[][] */
|
||||
/** @var Vector3[][] */
|
||||
protected $changedBlocks = [];
|
||||
protected $changedCount = [];
|
||||
|
||||
/** @var ReversePriorityQueue */
|
||||
private $updateQueue;
|
||||
@ -168,7 +174,11 @@ class Level implements ChunkManager, Metadatable{
|
||||
private $chunkSendQueue = [];
|
||||
private $chunkSendTasks = [];
|
||||
|
||||
private $chunkPopulationQueue = [];
|
||||
private $chunkPopulationLock = [];
|
||||
private $chunkGenerationQueue = [];
|
||||
private $chunkGenerationQueueSize = 8;
|
||||
private $chunkPopulationQueueSize = 2;
|
||||
|
||||
private $autoSave = true;
|
||||
|
||||
@ -217,7 +227,10 @@ class Level implements ChunkManager, Metadatable{
|
||||
/** @var LevelTimings */
|
||||
public $timings;
|
||||
|
||||
/** @var Generator */
|
||||
protected $generator;
|
||||
/** @var Generator */
|
||||
protected $generatorInstance;
|
||||
|
||||
/**
|
||||
* Returns the chunk unique hash/key
|
||||
@ -295,7 +308,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->time = (int) $this->provider->getTime();
|
||||
|
||||
$this->chunkTickRadius = min($this->server->getViewDistance(), max(1, (int) $this->server->getProperty("chunk-ticking.tick-radius", 4)));
|
||||
$this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 260);
|
||||
$this->chunksPerTick = (int) $this->server->getProperty("chunk-ticking.per-tick", 40);
|
||||
$this->chunkGenerationQueueSize = (int) $this->server->getProperty("chunk-generation.queue-size", 8);
|
||||
$this->chunkPopulationQueueSize = (int) $this->server->getProperty("chunk-generation.population-queue-size", 2);
|
||||
$this->chunkTickList = [];
|
||||
$this->clearChunksOnTick = (bool) $this->server->getProperty("chunk-ticking.clear-tick-list", false);
|
||||
|
||||
@ -305,7 +320,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
public function initLevel(){
|
||||
$this->server->getGenerationManager()->openLevel($this, $this->generator, $this->provider->getGeneratorOptions());
|
||||
$generator = $this->generator;
|
||||
$this->generatorInstance = new $generator($this->provider->getGeneratorOptions());
|
||||
$this->generatorInstance->init($this, new Random($this->getSeed()));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -348,13 +365,44 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->unloadChunk($chunk->getX(), $chunk->getZ(), false);
|
||||
}
|
||||
|
||||
$this->server->getGenerationManager()->closeLevel($this);
|
||||
$this->provider->close();
|
||||
$this->provider = null;
|
||||
$this->blockMetadata = null;
|
||||
$this->blockCache = [];
|
||||
$this->temporalPosition = null;
|
||||
}
|
||||
|
||||
public function addSound(Sound $sound, array $players = null){
|
||||
$pk = $sound->encode();
|
||||
|
||||
if($players === null){
|
||||
$players = $this->getUsingChunk($sound->x >> 4, $sound->z >> 4);
|
||||
}
|
||||
|
||||
if($pk !== null){
|
||||
if(!is_array($pk)){
|
||||
Server::broadcastPacket($players, $pk);
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addParticle(Particle $particle, array $players = null){
|
||||
$pk = $particle->encode();
|
||||
|
||||
if($players === null){
|
||||
$players = $this->getUsingChunk($particle->x >> 4, $particle->z >> 4);
|
||||
}
|
||||
|
||||
if($pk !== null){
|
||||
if(!is_array($pk)){
|
||||
Server::broadcastPacket($players, $pk);
|
||||
}else{
|
||||
$this->server->batchPackets($players, $pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
@ -533,52 +581,22 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->tickChunks();
|
||||
$this->timings->doTickTiles->stopTiming();
|
||||
|
||||
if(count($this->changedCount) > 0){
|
||||
if(count($this->changedBlocks) > 0){
|
||||
if(count($this->players) > 0){
|
||||
foreach($this->changedCount as $index => $mini){
|
||||
for($Y = 0; $Y < 8; ++$Y){
|
||||
if(($mini & (1 << $Y)) === 0){
|
||||
continue;
|
||||
}
|
||||
if(count($this->changedBlocks[$index][$Y]) < 256){
|
||||
continue;
|
||||
}else{
|
||||
$X = null;
|
||||
$Z = null;
|
||||
Level::getXZ($index, $X, $Z);
|
||||
foreach($this->getUsingChunk($X, $Z) as $p){
|
||||
$p->unloadChunk($X, $Z);
|
||||
}
|
||||
unset($this->changedBlocks[$index][$Y]);
|
||||
foreach($this->changedBlocks as $index => $blocks){
|
||||
Level::getXZ($index, $X, $Z);
|
||||
if(count($blocks) > 512){
|
||||
foreach($this->getUsingChunk($X, $Z) as $p){
|
||||
$p->unloadChunk($X, $Z);
|
||||
}
|
||||
}else{
|
||||
$this->sendBlocks($this->getUsingChunk($X, $Z), $blocks, UpdateBlockPacket::FLAG_ALL);
|
||||
}
|
||||
}
|
||||
$this->changedCount = [];
|
||||
if(count($this->changedBlocks) > 0){
|
||||
foreach($this->changedBlocks as $index => $mini){
|
||||
foreach($mini as $blocks){
|
||||
/** @var Block $b */
|
||||
foreach($blocks as $b){
|
||||
if(!($b instanceof Block)){
|
||||
$b = $this->getBlock($b);
|
||||
}
|
||||
$pk = new UpdateBlockPacket();
|
||||
$pk->x = $b->x;
|
||||
$pk->y = $b->y;
|
||||
$pk->z = $b->z;
|
||||
$pk->block = $b->getId();
|
||||
$pk->meta = $b->getDamage();
|
||||
Server::broadcastPacket($this->getUsingChunk($b->x >> 4, $b->z >> 4), $pk);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->changedBlocks = [];
|
||||
}
|
||||
}else{
|
||||
$this->changedCount = [];
|
||||
$this->changedBlocks = [];
|
||||
}
|
||||
|
||||
$this->changedBlocks = [];
|
||||
|
||||
}
|
||||
|
||||
$this->processChunkRequest();
|
||||
@ -586,6 +604,27 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->timings->doTick->stopTiming();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Player[] $target
|
||||
* @param Block[] $blocks
|
||||
* @param int $flags
|
||||
*/
|
||||
public function sendBlocks(array $target, array $blocks, $flags = UpdateBlockPacket::FLAG_NONE){
|
||||
$pk = new UpdateBlockPacket();
|
||||
foreach($blocks as $b){
|
||||
if($b === null){
|
||||
continue;
|
||||
}
|
||||
if($b instanceof Block){
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $b->getId(), $b->getDamage(), $flags];
|
||||
}else{
|
||||
$fullBlock = $this->getFullBlock($b->x, $b->y, $b->z);
|
||||
$pk->records[] = [$b->x, $b->z, $b->y, $fullBlock >> 4, $fullBlock & 0xf, $flags];
|
||||
}
|
||||
}
|
||||
Server::broadcastPacket($target, $pk);
|
||||
}
|
||||
|
||||
public function clearCache(){
|
||||
$this->blockCache = [];
|
||||
}
|
||||
@ -785,17 +824,17 @@ class Level implements ChunkManager, Metadatable{
|
||||
$minX = Math::floorFloat($bb->minX);
|
||||
$minY = Math::floorFloat($bb->minY);
|
||||
$minZ = Math::floorFloat($bb->minZ);
|
||||
$maxX = Math::floorFloat($bb->maxX + 1);
|
||||
$maxY = Math::floorFloat($bb->maxY + 1);
|
||||
$maxZ = Math::floorFloat($bb->maxZ + 1);
|
||||
$maxX = Math::ceilFloat($bb->maxX);
|
||||
$maxY = Math::ceilFloat($bb->maxY);
|
||||
$maxZ = Math::ceilFloat($bb->maxZ);
|
||||
|
||||
$collides = [];
|
||||
|
||||
$v = $this->temporalVector;
|
||||
|
||||
for($v->z = $minZ; $v->z < $maxZ; ++$v->z){
|
||||
for($v->x = $minX; $v->x < $maxX; ++$v->x){
|
||||
for($v->y = $minY - 1; $v->y < $maxY; ++$v->y){
|
||||
for($v->z = $minZ; $v->z <= $maxZ; ++$v->z){
|
||||
for($v->x = $minX; $v->x <= $maxX; ++$v->x){
|
||||
for($v->y = $minY - 1; $v->y <= $maxY; ++$v->y){
|
||||
$block = $this->getBlock($v);
|
||||
if($block->getId() !== 0){
|
||||
$block->collidesWithBB($bb, $collides);
|
||||
@ -833,15 +872,15 @@ class Level implements ChunkManager, Metadatable{
|
||||
$minX = Math::floorFloat($bb->minX);
|
||||
$minY = Math::floorFloat($bb->minY);
|
||||
$minZ = Math::floorFloat($bb->minZ);
|
||||
$maxX = Math::floorFloat($bb->maxX + 1);
|
||||
$maxY = Math::floorFloat($bb->maxY + 1);
|
||||
$maxZ = Math::floorFloat($bb->maxZ + 1);
|
||||
$maxX = Math::ceilFloat($bb->maxX);
|
||||
$maxY = Math::ceilFloat($bb->maxY);
|
||||
$maxZ = Math::ceilFloat($bb->maxZ);
|
||||
|
||||
$collides = [];
|
||||
$v = $this->temporalVector;
|
||||
|
||||
for($v->z = $minZ; $v->z < $maxZ; ++$v->z){
|
||||
for($v->x = $minX; $v->x < $maxX; ++$v->x){
|
||||
for($v->z = $minZ; $v->z <= $maxZ; ++$v->z){
|
||||
for($v->x = $minX; $v->x <= $maxX; ++$v->x){
|
||||
for($v->y = $minY - 1; $v->y < $maxY; ++$v->y){
|
||||
$block = $this->getBlock($v);
|
||||
if($block->getId() !== 0){
|
||||
@ -1094,7 +1133,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
*
|
||||
* @param Vector3 $pos
|
||||
* @param Block $block
|
||||
* @param bool $direct
|
||||
* @param bool $direct @deprecated
|
||||
* @param bool $update
|
||||
*
|
||||
* @return bool Whether the block has been updated or not
|
||||
@ -1112,19 +1151,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
$block->position($pos);
|
||||
$index = Level::chunkHash($pos->x >> 4, $pos->z >> 4);
|
||||
if(ADVANCED_CACHE == true){
|
||||
Cache::remove("world:" . $this->getId() . ":" . $index);
|
||||
}
|
||||
|
||||
if($direct === true){
|
||||
$pk = new UpdateBlockPacket();
|
||||
$pk->x = $pos->x;
|
||||
$pk->y = $pos->y;
|
||||
$pk->z = $pos->z;
|
||||
$pk->block = $block->getId();
|
||||
$pk->meta = $block->getDamage();
|
||||
|
||||
Server::broadcastPacket($this->getUsingChunk($pos->x >> 4, $pos->z >> 4), $pk);
|
||||
$this->sendBlocks($this->getUsingChunk($pos->x >> 4, $pos->z >> 4), [$block], UpdateBlockPacket::FLAG_ALL_PRIORITY);
|
||||
}else{
|
||||
if(!($pos instanceof Position)){
|
||||
$pos = $this->temporalPosition->setComponents($pos->x, $pos->y, $pos->z);
|
||||
@ -1132,14 +1161,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$block->position($pos);
|
||||
if(!isset($this->changedBlocks[$index])){
|
||||
$this->changedBlocks[$index] = [];
|
||||
$this->changedCount[$index] = 0;
|
||||
}
|
||||
$Y = $pos->y >> 4;
|
||||
if(!isset($this->changedBlocks[$index][$Y])){
|
||||
$this->changedBlocks[$index][$Y] = [];
|
||||
$this->changedCount[$index] |= 1 << $Y;
|
||||
}
|
||||
$this->changedBlocks[$index][$Y][Level::blockHash($block->x, $block->y, $block->z)] = clone $block;
|
||||
|
||||
$this->changedBlocks[$index][Level::blockHash($block->x, $block->y, $block->z)] = clone $block;
|
||||
}
|
||||
|
||||
if($update === true){
|
||||
@ -1238,12 +1262,15 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
$breakTime = $player->isCreative() ? 0.15 : $target->getBreakTime($item);
|
||||
if($player->hasEffect(Effect::SWIFTNESS)){
|
||||
$breakTime *= pow(0.80, $player->getEffect(Effect::SWIFTNESS)->getAmplifier() + 1);
|
||||
}
|
||||
|
||||
if(!$ev->getInstaBreak() and ($player->lastBreak + $breakTime) >= microtime(true)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$player->lastBreak = microtime(true);
|
||||
$player->lastBreak = PHP_INT_MAX;
|
||||
}elseif($item instanceof Item and !$target->isBreakable($item)){
|
||||
return false;
|
||||
}
|
||||
@ -1259,7 +1286,15 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
$drops = $target->getDrops($item); //Fixes tile entities being deleted before getting drops
|
||||
|
||||
$players = $this->getUsingChunk($target->x >> 4, $target->z >> 4);
|
||||
if($player !== null){
|
||||
unset($players[$player->getId()]);
|
||||
}
|
||||
$this->addParticle(new DestroyBlockParticle($target->add(0.5, 0.5, 0.5), $target), $players);
|
||||
|
||||
$target->onBreak($item);
|
||||
|
||||
$tile = $this->getTile($target);
|
||||
if($tile instanceof Tile){
|
||||
if($tile instanceof InventoryHolder){
|
||||
@ -1319,7 +1354,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
if($player instanceof Player){
|
||||
$ev = new PlayerInteractEvent($player, $item, $target, $face);
|
||||
$ev = new PlayerInteractEvent($player, $item, $target, $face, $target->getId() === 0 ? PlayerInteractEvent::RIGHT_CLICK_AIR : PlayerInteractEvent::RIGHT_CLICK_BLOCK);
|
||||
if(!$player->isOp() and ($distance = $this->server->getSpawnRadius()) > -1){
|
||||
$t = new Vector2($target->x, $target->z);
|
||||
$s = new Vector2($this->getSpawnLocation()->x, $this->getSpawnLocation()->z);
|
||||
@ -1341,6 +1376,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}elseif($target->canBeActivated() === true and $target->onActivate($item, $player) === true){
|
||||
return true;
|
||||
@ -1456,9 +1493,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if($entity === null or $entity->canCollide){
|
||||
$minX = Math::floorFloat(($bb->minX - 2) / 16);
|
||||
$maxX = Math::floorFloat(($bb->maxX + 2) / 16);
|
||||
$maxX = Math::ceilFloat(($bb->maxX + 2) / 16);
|
||||
$minZ = Math::floorFloat(($bb->minZ - 2) / 16);
|
||||
$maxZ = Math::floorFloat(($bb->maxZ + 2) / 16);
|
||||
$maxZ = Math::ceilFloat(($bb->maxZ + 2) / 16);
|
||||
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
@ -1486,9 +1523,9 @@ class Level implements ChunkManager, Metadatable{
|
||||
$nearby = [];
|
||||
|
||||
$minX = Math::floorFloat(($bb->minX - 2) / 16);
|
||||
$maxX = Math::floorFloat(($bb->maxX + 2) / 16);
|
||||
$maxX = Math::ceilFloat(($bb->maxX + 2) / 16);
|
||||
$minZ = Math::floorFloat(($bb->minZ - 2) / 16);
|
||||
$maxZ = Math::floorFloat(($bb->maxZ + 2) / 16);
|
||||
$maxZ = Math::ceilFloat(($bb->maxZ + 2) / 16);
|
||||
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
@ -1598,13 +1635,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
|
||||
$this->changedBlocks[$index] = [];
|
||||
$this->changedCount[$index] = 0;
|
||||
}
|
||||
if(!isset($this->changedBlocks[$index][$Y = $y >> 4])){
|
||||
$this->changedBlocks[$index][$Y] = [];
|
||||
$this->changedCount[$index] |= 1 << $Y;
|
||||
}
|
||||
$this->changedBlocks[$index][$Y][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1634,13 +1666,8 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
if(!isset($this->changedBlocks[$index = Level::chunkHash($x >> 4, $z >> 4)])){
|
||||
$this->changedBlocks[$index] = [];
|
||||
$this->changedCount[$index] = 0;
|
||||
}
|
||||
if(!isset($this->changedBlocks[$index][$Y = $y >> 4])){
|
||||
$this->changedBlocks[$index][$Y] = [];
|
||||
$this->changedCount[$index] |= 1 << $Y;
|
||||
}
|
||||
$this->changedBlocks[$index][$Y][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
$this->changedBlocks[$index][Level::blockHash($x, $y, $z)] = new Vector3($x, $y, $z);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1792,17 +1819,34 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
public function generateChunkCallback($x, $z, FullChunk $chunk){
|
||||
$oldChunk = $this->getChunk($x, $z, false);
|
||||
unset($this->chunkGenerationQueue[Level::chunkHash($x, $z)]);
|
||||
$chunk->setProvider($this->provider);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated()){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
|
||||
Timings::$generationTimer->startTiming();
|
||||
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)])){
|
||||
$oldChunk = $this->getChunk($x, $z, false);
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
unset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)]);
|
||||
}
|
||||
}
|
||||
unset($this->chunkPopulationQueue[$index]);
|
||||
$chunk->setProvider($this->provider);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
$chunk = $this->getChunk($x, $z, false);
|
||||
if($chunk !== null and ($oldChunk === null or $oldChunk->isPopulated() === false) and $chunk->isPopulated() and $chunk->getProvider() !== null){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkPopulateEvent($chunk));
|
||||
}
|
||||
}elseif(isset($this->chunkGenerationQueue[$index]) or isset($this->chunkPopulationLock[$index])){
|
||||
unset($this->chunkGenerationQueue[$index]);
|
||||
unset($this->chunkPopulationLock[$index]);
|
||||
$chunk->setProvider($this->provider);
|
||||
$this->setChunk($x, $z, $chunk);
|
||||
}
|
||||
Timings::$generationTimer->stopTiming();
|
||||
}
|
||||
|
||||
public function setChunk($x, $z, FullChunk $chunk, $unload = true){
|
||||
public function setChunk($x, $z, FullChunk $chunk = null, $unload = true){
|
||||
if($chunk === null){
|
||||
return;
|
||||
}
|
||||
$index = Level::chunkHash($x, $z);
|
||||
if($unload){
|
||||
foreach($this->getUsingChunk($x, $z) as $player){
|
||||
@ -1814,9 +1858,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
$this->provider->setChunk($x, $z, $chunk);
|
||||
$this->chunks[$index] = $chunk;
|
||||
}
|
||||
if(ADVANCED_CACHE == true){
|
||||
Cache::remove("world:" . $this->getId() . ":" . Level::chunkHash($x, $z));
|
||||
}
|
||||
|
||||
$chunk->setChanged();
|
||||
}
|
||||
|
||||
@ -1904,23 +1946,13 @@ class Level implements ChunkManager, Metadatable{
|
||||
continue;
|
||||
}
|
||||
Level::getXZ($index, $x, $z);
|
||||
if(ADVANCED_CACHE == true and ($cache = Cache::get("world:" . $this->getId() . ":" . $index)) !== false){
|
||||
/** @var Player[] $players */
|
||||
foreach($players as $player){
|
||||
if($player->isConnected() and isset($player->usedChunks[$index])){
|
||||
$player->sendChunk($x, $z, $cache);
|
||||
}
|
||||
}
|
||||
unset($this->chunkSendQueue[$index]);
|
||||
}else{
|
||||
$this->chunkSendTasks[$index] = true;
|
||||
$this->timings->syncChunkSendPrepareTimer->startTiming();
|
||||
$task = $this->provider->requestChunkTask($x, $z);
|
||||
if($task instanceof AsyncTask){
|
||||
$this->server->getScheduler()->scheduleAsyncTask($task);
|
||||
}
|
||||
$this->timings->syncChunkSendPrepareTimer->stopTiming();
|
||||
$this->chunkSendTasks[$index] = true;
|
||||
$this->timings->syncChunkSendPrepareTimer->startTiming();
|
||||
$task = $this->provider->requestChunkTask($x, $z);
|
||||
if($task instanceof AsyncTask){
|
||||
$this->server->getScheduler()->scheduleAsyncTask($task);
|
||||
}
|
||||
$this->timings->syncChunkSendPrepareTimer->stopTiming();
|
||||
}
|
||||
|
||||
$this->timings->syncChunkSendTimer->stopTiming();
|
||||
@ -1930,10 +1962,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
public function chunkRequestCallback($x, $z, $payload){
|
||||
$index = Level::chunkHash($x, $z);
|
||||
if(isset($this->chunkSendTasks[$index])){
|
||||
|
||||
if(ADVANCED_CACHE == true){
|
||||
Cache::add("world:" . $this->getId() . ":" . $index, $payload, 60);
|
||||
}
|
||||
foreach($this->chunkSendQueue[$index] as $player){
|
||||
/** @var Player $player */
|
||||
if($player->isConnected() and isset($player->usedChunks[$index])){
|
||||
@ -2050,7 +2078,12 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated()));
|
||||
if($chunk->getProvider() !== null){
|
||||
$this->server->getPluginManager()->callEvent(new ChunkLoadEvent($chunk, !$chunk->isGenerated()));
|
||||
}else{
|
||||
$this->unloadChunk($x, $z, false);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2085,7 +2118,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
|
||||
$chunk = $this->getChunk($x, $z);
|
||||
|
||||
if($chunk !== null){
|
||||
if($chunk !== null and $chunk->getProvider() !== null){
|
||||
$this->server->getPluginManager()->callEvent($ev = new ChunkUnloadEvent($chunk));
|
||||
if($ev->isCancelled()){
|
||||
return false;
|
||||
@ -2109,7 +2142,6 @@ class Level implements ChunkManager, Metadatable{
|
||||
unset($this->chunks[$index]);
|
||||
unset($this->usedChunks[$index]);
|
||||
unset($this->chunkTickList[$index]);
|
||||
Cache::remove("world:" . $this->getId() . ":$index");
|
||||
|
||||
$this->timings->doChunkUnload->stopTiming();
|
||||
|
||||
@ -2147,7 +2179,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
* @return bool|Position
|
||||
*/
|
||||
public function getSafeSpawn($spawn = null){
|
||||
if(!($spawn instanceof Vector3)){
|
||||
if(!($spawn instanceof Vector3) or $spawn->y <= 0){
|
||||
$spawn = $this->getSpawnLocation();
|
||||
}
|
||||
if($spawn instanceof Vector3){
|
||||
@ -2165,7 +2197,7 @@ class Level implements ChunkManager, Metadatable{
|
||||
for(; $v->y >= 0 and $v->y < 128; ++$v->y){
|
||||
if(!Block::$solid[$chunk->getBlockId($x, $v->y + 1, $z)]){
|
||||
if(!Block::$solid[$chunk->getBlockId($x, $v->y, $z)]){
|
||||
return new Position($spawn->x, $v->y === Math::floorFloat($spawn->y) ? $spawn->y : $v->y, $spawn->z, $this);
|
||||
return new Position($spawn->x, $v->y === (int) $spawn->y ? $spawn->y : $v->y, $spawn->z, $this);
|
||||
}
|
||||
}else{
|
||||
++$v->y;
|
||||
@ -2262,10 +2294,58 @@ class Level implements ChunkManager, Metadatable{
|
||||
}
|
||||
|
||||
|
||||
public function generateChunk($x, $z){
|
||||
public function populateChunk($x, $z, $force = false){
|
||||
if(isset($this->chunkPopulationQueue[$index = Level::chunkHash($x, $z)]) or (count($this->chunkPopulationQueue) >= $this->chunkPopulationQueueSize and !$force)){
|
||||
return false;
|
||||
}
|
||||
|
||||
Timings::$generationTimer->startTiming();
|
||||
if(!$this->isChunkPopulated($x, $z)){
|
||||
$populate = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
if(isset($this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)])){
|
||||
$populate = false;
|
||||
}elseif(!$this->isChunkGenerated($x + $xx, $z + $zz)){
|
||||
$populate = false;
|
||||
$this->generateChunk($x + $xx, $z + $zz, $force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($populate){
|
||||
if(!isset($this->chunkPopulationQueue[$index])){
|
||||
$this->chunkPopulationQueue[$index] = true;
|
||||
for($xx = -1; $xx <= 1; ++$xx){
|
||||
for($zz = -1; $zz <= 1; ++$zz){
|
||||
$this->chunkPopulationLock[Level::chunkHash($x + $xx, $z + $zz)] = true;
|
||||
}
|
||||
}
|
||||
$task = new PopulationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true));
|
||||
$this->server->getScheduler()->scheduleAsyncTask($task);
|
||||
}
|
||||
Timings::$generationTimer->stopTiming();
|
||||
return false;
|
||||
}
|
||||
|
||||
Timings::$generationTimer->stopTiming();
|
||||
return false;
|
||||
}
|
||||
Timings::$generationTimer->stopTiming();
|
||||
return true;
|
||||
}
|
||||
|
||||
public function generateChunk($x, $z, $force = false){
|
||||
if(count($this->chunkGenerationQueue) >= $this->chunkGenerationQueueSize and !$force){
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isset($this->chunkGenerationQueue[$index = Level::chunkHash($x, $z)])){
|
||||
Timings::$generationTimer->startTiming();
|
||||
$this->chunkGenerationQueue[$index] = true;
|
||||
$this->server->getGenerationManager()->requestChunk($this, $x, $z);
|
||||
$task = new GenerationTask($this, $this->generatorInstance, $this->getChunk($x, $z, true));
|
||||
$this->server->getScheduler()->scheduleAsyncTask($task);
|
||||
Timings::$generationTimer->stopTiming();
|
||||
}
|
||||
}
|
||||
|
||||
|
128
src/pocketmine/level/SimpleChunkManager.php
Normal file
128
src/pocketmine/level/SimpleChunkManager.php
Normal file
@ -0,0 +1,128 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
|
||||
class SimpleChunkManager implements ChunkManager{
|
||||
|
||||
/** @var FullChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
protected $seed;
|
||||
|
||||
public function __construct($seed){
|
||||
$this->seed = $seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z){
|
||||
if($chunk = $this->getChunk($x >> 4, $z >> 4)){
|
||||
return $chunk->getBlockId($x & 0xf, $y & 0x7f, $z & 0xf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id){
|
||||
if($chunk = $this->getChunk($x >> 4, $z >> 4)){
|
||||
$chunk->setBlockId($x & 0xf, $y & 0x7f, $z & 0xf, $id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block metadata
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z){
|
||||
if($chunk = $this->getChunk($x >> 4, $z >> 4)){
|
||||
return $chunk->getBlockData($x & 0xf, $y & 0x7f, $z & 0xf);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block metadata.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data){
|
||||
if($chunk = $this->getChunk($x >> 4, $z >> 4)){
|
||||
$chunk->setBlockData($x & 0xf, $y & 0x7f, $z & 0xf, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ){
|
||||
return isset($this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)]) ? $this->chunks[$index] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk = null){
|
||||
if($chunk === null){
|
||||
unset($this->chunks[Level::chunkHash($chunkX, $chunkZ)]);
|
||||
return;
|
||||
}
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the level seed
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSeed(){
|
||||
return $this->seed;
|
||||
}
|
||||
}
|
@ -310,6 +310,8 @@ interface FullChunk{
|
||||
|
||||
public function toBinary();
|
||||
|
||||
public function toFastBinary();
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
@ -328,4 +330,12 @@ interface FullChunk{
|
||||
*/
|
||||
public static function fromBinary($data, LevelProvider $provider = null);
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null);
|
||||
|
||||
}
|
@ -34,7 +34,6 @@ class ChunkRequestTask extends AsyncTask{
|
||||
protected $levelId;
|
||||
protected $chunkX;
|
||||
protected $chunkZ;
|
||||
protected $compressionLevel;
|
||||
|
||||
/** @var \pocketmine\level\format\ChunkSection[] */
|
||||
protected $sections;
|
||||
@ -69,8 +68,6 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
$this->tiles = $tiles;
|
||||
|
||||
$this->compressionLevel = Level::$COMPRESSION_LEVEL;
|
||||
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
@ -102,9 +99,9 @@ class ChunkRequestTask extends AsyncTask{
|
||||
|
||||
$biomeColors = pack("N*", ...$this->biomeColors);
|
||||
|
||||
$ordered = zlib_encode(Binary::writeLInt($this->chunkX) . Binary::writeLInt($this->chunkZ) . $orderedIds . $orderedData . $orderedSkyLight . $orderedLight . $this->biomeIds . $biomeColors . $this->tiles, ZLIB_ENCODING_DEFLATE, $this->compressionLevel);
|
||||
$ordered = $orderedIds . $orderedData . $orderedSkyLight . $orderedLight . $this->biomeIds . $biomeColors . $this->tiles;
|
||||
|
||||
$this->setResult($ordered);
|
||||
$this->setResult($ordered, false);
|
||||
}
|
||||
|
||||
public function getColumn(&$data, $x, $z){
|
||||
|
@ -357,4 +357,12 @@ abstract class BaseFullChunk implements FullChunk{
|
||||
$this->hasChanged = (bool) $changed;
|
||||
}
|
||||
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
self::fromBinary($data, $provider);
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
return $this->toBinary();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -221,6 +221,10 @@ class Chunk extends BaseFullChunk{
|
||||
$this->isGenerated = (bool) $value;
|
||||
}
|
||||
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
return self::fromBinary($data, $provider);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $data
|
||||
* @param LevelProvider $provider
|
||||
@ -272,6 +276,10 @@ class Chunk extends BaseFullChunk{
|
||||
}
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
return $this->toBinary(false);
|
||||
}
|
||||
|
||||
public function toBinary($saveExtra = false){
|
||||
$chunkIndex = LevelDB::chunkIndex($this->getX(), $this->getZ());
|
||||
|
||||
|
@ -148,16 +148,13 @@ class LevelDB extends BaseLevelProvider{
|
||||
|
||||
$biomeColors = pack("N*", ...$chunk->getBiomeColorArray());
|
||||
|
||||
$ordered = zlib_encode(
|
||||
Binary::writeLInt($x) . Binary::writeLInt($z) .
|
||||
$chunk->getBlockIdArray() .
|
||||
$ordered = $chunk->getBlockIdArray() .
|
||||
$chunk->getBlockDataArray() .
|
||||
$chunk->getBlockSkyLightArray() .
|
||||
$chunk->getBlockLightArray() .
|
||||
$chunk->getBiomeIdArray() .
|
||||
$biomeColors .
|
||||
$tiles
|
||||
, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL);
|
||||
$tiles;
|
||||
|
||||
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);
|
||||
|
||||
|
@ -38,7 +38,12 @@ class Chunk extends BaseFullChunk{
|
||||
/** @var Compound */
|
||||
protected $nbt;
|
||||
|
||||
public function __construct($level, Compound $nbt){
|
||||
public function __construct($level, Compound $nbt = null){
|
||||
if($nbt === null){
|
||||
$this->nbt = new Compound("Level", []);
|
||||
return;
|
||||
}
|
||||
|
||||
$this->nbt = $nbt;
|
||||
|
||||
if(isset($this->nbt->Entities) and $this->nbt->Entities instanceof Enum){
|
||||
@ -89,6 +94,9 @@ class Chunk extends BaseFullChunk{
|
||||
unset($this->nbt->Data);
|
||||
unset($this->nbt->SkyLight);
|
||||
unset($this->nbt->BlockLight);
|
||||
unset($this->nbt->BiomeColors);
|
||||
unset($this->nbt->HeightMap);
|
||||
unset($this->nbt->Biomes);
|
||||
}
|
||||
|
||||
public function getBlockId($x, $y, $z){
|
||||
@ -231,28 +239,35 @@ class Chunk extends BaseFullChunk{
|
||||
* @return bool
|
||||
*/
|
||||
public function isPopulated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0;
|
||||
return isset($this->nbt->TerrainPopulated) and $this->nbt->TerrainPopulated->getValue() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setPopulated($value = 1){
|
||||
$this->nbt->TerrainPopulated = new Byte("TerrainPopulated", $value);
|
||||
$this->nbt->TerrainPopulated = new Byte("TerrainPopulated", (int) $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function isGenerated(){
|
||||
return $this->nbt["TerrainPopulated"] > 0 or (isset($this->nbt->TerrainGenerated) and $this->nbt["TerrainGenerated"] > 0);
|
||||
if(isset($this->nbt->TerrainGenerated)){
|
||||
return $this->nbt->TerrainGenerated->getValue() > 0;
|
||||
}elseif(isset($this->nbt->TerrainPopulated)){
|
||||
return $this->nbt->TerrainPopulated->getValue() > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $value
|
||||
*/
|
||||
public function setGenerated($value = 1){
|
||||
$this->nbt->TerrainGenerated = new Byte("TerrainGenerated", $value);
|
||||
$this->nbt->TerrainGenerated = new Byte("TerrainGenerated", (int) $value);
|
||||
$this->hasChanged = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -277,6 +292,70 @@ class Chunk extends BaseFullChunk{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static function fromFastBinary($data, LevelProvider $provider = null){
|
||||
|
||||
try{
|
||||
$offset = 0;
|
||||
|
||||
$chunk = new Chunk($provider instanceof LevelProvider ? $provider : McRegion::class, null);
|
||||
$chunk->provider = $provider;
|
||||
$chunk->x = Binary::readInt(substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunk->z = Binary::readInt(substr($data, $offset, 4));
|
||||
$offset += 4;
|
||||
|
||||
$chunk->blocks = substr($data, $offset, 32768);
|
||||
$offset += 32768;
|
||||
$chunk->data = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
$chunk->skyLight = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
$chunk->blockLight = substr($data, $offset, 16384);
|
||||
$offset += 16384;
|
||||
|
||||
$chunk->biomeIds = substr($data, $offset, 256);
|
||||
$offset += 256;
|
||||
|
||||
$chunk->biomeColors = [];
|
||||
$chunk->heightMap = [];
|
||||
$bc = unpack("N*", substr($data, $offset, 1024));
|
||||
$offset += 1024;
|
||||
$hm = unpack("N*", substr($data, $offset, 1024));
|
||||
$offset += 1024;
|
||||
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$chunk->biomeColors[$i] = $bc[$i + 1];
|
||||
$chunk->heightMap[$i] = $hm[$i + 1];
|
||||
}
|
||||
|
||||
$flags = ord($data{$offset++});
|
||||
|
||||
$chunk->nbt->TerrainGenerated = new Byte("TerrainGenerated", $flags & 0b1);
|
||||
$chunk->nbt->TerrainPopulated = new Byte("TerrainPopulated", $flags >> 1);
|
||||
|
||||
return $chunk;
|
||||
}catch(\Exception $e){
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function toFastBinary(){
|
||||
$biomeColors = pack("N*", ...$this->getBiomeColorArray());
|
||||
$heightMap = pack("N*", ...$this->getHeightMapArray());
|
||||
|
||||
return
|
||||
Binary::writeInt($this->x) .
|
||||
Binary::writeInt($this->z) .
|
||||
$this->getBlockIdArray() .
|
||||
$this->getBlockDataArray() .
|
||||
$this->getBlockSkyLightArray() .
|
||||
$this->getBlockLightArray() .
|
||||
$this->getBiomeIdArray() .
|
||||
$biomeColors .
|
||||
$heightMap .
|
||||
chr(($this->isPopulated() ? 1 << 1 : 0) + ($this->isGenerated() ? 1 : 0));
|
||||
}
|
||||
|
||||
public function toBinary(){
|
||||
$nbt = clone $this->getNBT();
|
||||
|
@ -129,16 +129,13 @@ class McRegion extends BaseLevelProvider{
|
||||
|
||||
$biomeColors = pack("N*", ...$chunk->getBiomeColorArray());
|
||||
|
||||
$ordered = zlib_encode(
|
||||
Binary::writeLInt($x) . Binary::writeLInt($z) .
|
||||
$chunk->getBlockIdArray() .
|
||||
$ordered = $chunk->getBlockIdArray() .
|
||||
$chunk->getBlockDataArray() .
|
||||
$chunk->getBlockSkyLightArray() .
|
||||
$chunk->getBlockLightArray() .
|
||||
$chunk->getBiomeIdArray() .
|
||||
$biomeColors .
|
||||
$tiles
|
||||
, ZLIB_ENCODING_DEFLATE, Level::$COMPRESSION_LEVEL);
|
||||
$tiles;
|
||||
|
||||
$this->getLevel()->chunkRequestCallback($x, $z, $ordered);
|
||||
|
||||
@ -295,13 +292,9 @@ class McRegion extends BaseLevelProvider{
|
||||
}
|
||||
|
||||
protected function loadRegion($x, $z){
|
||||
if(isset($this->regions[$index = Level::chunkHash($x, $z)])){
|
||||
return true;
|
||||
if(!isset($this->regions[$index = Level::chunkHash($x, $z)])){
|
||||
$this->regions[$index] = new RegionLoader($this, $x, $z);
|
||||
}
|
||||
|
||||
$this->regions[$index] = new RegionLoader($this, $x, $z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function close(){
|
||||
|
@ -30,6 +30,7 @@ use pocketmine\block\IronOre;
|
||||
use pocketmine\block\LapisOre;
|
||||
use pocketmine\block\RedstoneOre;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\generator\populator\Ore;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
@ -37,7 +38,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Flat extends Generator{
|
||||
/** @var GenerationChunkManager */
|
||||
/** @var ChunkManager */
|
||||
private $level;
|
||||
/** @var FullChunk */
|
||||
private $chunk;
|
||||
@ -88,12 +89,12 @@ class Flat extends Generator{
|
||||
$blocks = isset($preset[1]) ? $preset[1] : "";
|
||||
$biome = isset($preset[2]) ? $preset[2] : 1;
|
||||
$options = isset($preset[3]) ? $preset[3] : "";
|
||||
preg_match_all('#(([0-9]{0,})x?([0-9]{1,3}:?[0-9]{0,2})),?#', $blocks, $matches);
|
||||
preg_match_all('#^(([0-9]*x|)([0-9]{1,3})(|:[0-9]{0,2}))$#m', str_replace(",", "\n", $blocks), $matches);
|
||||
$y = 0;
|
||||
$this->structure = [];
|
||||
$this->chunks = [];
|
||||
foreach($matches[3] as $i => $b){
|
||||
$b = Item::fromString($b);
|
||||
$b = Item::fromString($b . $matches[4][$i]);
|
||||
$cnt = $matches[2][$i] === "" ? 1 : intval($matches[2][$i]);
|
||||
for($cY = $y, $y += $cnt; $cY < $y; ++$cY){
|
||||
$this->structure[$cY] = [$b->getId(), $b->getDamage()];
|
||||
@ -112,6 +113,7 @@ class Flat extends Generator{
|
||||
|
||||
for($Z = 0; $Z < 16; ++$Z){
|
||||
for($X = 0; $X < 16; ++$X){
|
||||
$this->chunk->setBiomeId($X, $Z, $biome);
|
||||
for($y = 0; $y < 128; ++$y){
|
||||
$this->chunk->setBlock($X, $y, $Z, ...$this->structure[$y]);
|
||||
}
|
||||
@ -136,7 +138,7 @@ class Flat extends Generator{
|
||||
}
|
||||
}
|
||||
|
||||
public function init(GenerationChunkManager $level, Random $random){
|
||||
public function init(ChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
|
||||
|
@ -1,271 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\ChunkException;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class GenerationChunkManager implements ChunkManager{
|
||||
|
||||
protected $levelID;
|
||||
|
||||
/** @var FullChunk[] */
|
||||
protected $chunks = [];
|
||||
|
||||
/** @var Generator */
|
||||
protected $generator;
|
||||
|
||||
/** @var GenerationManager */
|
||||
protected $manager;
|
||||
|
||||
protected $seed;
|
||||
|
||||
protected $changes = [];
|
||||
|
||||
public function __construct(GenerationManager $manager, $levelID, $seed, $class, array $options){
|
||||
if(!class_exists($class, true) or !is_subclass_of($class, Generator::class)){
|
||||
throw new \InvalidStateException("Class $class does not exists or is not a subclass of Generator");
|
||||
}
|
||||
|
||||
$this->levelID = $levelID;
|
||||
$this->seed = $seed;
|
||||
$this->manager = $manager;
|
||||
$this->generator = new $class($options);
|
||||
$this->generator->init($this, new Random($seed));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getSeed(){
|
||||
return $this->seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
public function getId(){
|
||||
return $this->levelID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*
|
||||
* @throws ChunkException
|
||||
*/
|
||||
public function getChunk($chunkX, $chunkZ){
|
||||
$index = Level::chunkHash($chunkX, $chunkZ);
|
||||
$chunk = !isset($this->chunks[$index]) ? $this->requestChunk($chunkX, $chunkZ) : $this->chunks[$index];
|
||||
if($chunk === null){
|
||||
throw new ChunkException("null Chunk received");
|
||||
}
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FullChunk[]
|
||||
*/
|
||||
public function getChangedChunks(){
|
||||
return $this->changes;
|
||||
}
|
||||
|
||||
public function cleanChangedChunks(){
|
||||
$this->changes = [];
|
||||
}
|
||||
|
||||
public function cleanChangedChunk($index){
|
||||
unset($this->changes[$index]);
|
||||
}
|
||||
|
||||
public function doGarbageCollection(){
|
||||
$count = 0;
|
||||
|
||||
foreach($this->chunks as $index => $chunk){
|
||||
if(!isset($this->changes[$index]) or $chunk->isPopulated()){
|
||||
unset($this->chunks[$index]);
|
||||
unset($this->changes[$index]);
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
try{
|
||||
$this->getChunk($chunkX, $chunkZ);
|
||||
$this->generator->generateChunk($chunkX, $chunkZ);
|
||||
$this->setChunkGenerated($chunkX, $chunkZ);
|
||||
}catch(\Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
if(!$this->isChunkGenerated($chunkX, $chunkZ)){
|
||||
$this->generateChunk($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
for($z = $chunkZ - 1; $z <= $chunkZ + 1; ++$z){
|
||||
for($x = $chunkX - 1; $x <= $chunkX + 1; ++$x){
|
||||
if(!$this->isChunkGenerated($x, $z)){
|
||||
$this->generateChunk($x, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->generator->populateChunk($chunkX, $chunkZ);
|
||||
$this->setChunkPopulated($chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function isChunkGenerated($chunkX, $chunkZ){
|
||||
try{
|
||||
return $this->getChunk($chunkX, $chunkZ)->isGenerated();
|
||||
}catch(\Exception $e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isChunkPopulated($chunkX, $chunkZ){
|
||||
try{
|
||||
return $this->getChunk($chunkX, $chunkZ)->isPopulated();
|
||||
}catch(\Exception $e){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunkGenerated($chunkX, $chunkZ){
|
||||
try{
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setGenerated(true);
|
||||
$this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}catch(\Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
public function setChunkPopulated($chunkX, $chunkZ){
|
||||
try{
|
||||
$chunk = $this->getChunk($chunkX, $chunkZ);
|
||||
$chunk->setPopulated(true);
|
||||
$this->changes[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
}catch(\Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
protected function requestChunk($chunkX, $chunkZ){
|
||||
$chunk = $this->manager->requestChunk($this->levelID, $chunkX, $chunkZ);
|
||||
$this->chunks[Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
|
||||
return $chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $chunkX
|
||||
* @param int $chunkZ
|
||||
* @param FullChunk $chunk
|
||||
*/
|
||||
public function setChunk($chunkX, $chunkZ, FullChunk $chunk){
|
||||
$this->chunks[$index = Level::chunkHash($chunkX, $chunkZ)] = $chunk;
|
||||
$this->changes[$index] = $chunk;
|
||||
if($chunk->isPopulated()){
|
||||
//TODO: Queue to be sent
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-255
|
||||
*/
|
||||
public function getBlockIdAt($x, $y, $z){
|
||||
try{
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block id.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $id 0-255
|
||||
*/
|
||||
public function setBlockIdAt($x, $y, $z, $id){
|
||||
try{
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockId($x & 0x0f, $y & 0x7f, $z & 0x0f, $id & 0xff);
|
||||
}catch(\Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw block metadata
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return int 0-15
|
||||
*/
|
||||
public function getBlockDataAt($x, $y, $z){
|
||||
try{
|
||||
return $this->getChunk($x >> 4, $z >> 4)->getBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the raw block metadata.
|
||||
*
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
* @param int $data 0-15
|
||||
*/
|
||||
public function setBlockDataAt($x, $y, $z, $data){
|
||||
try{
|
||||
$this->getChunk($x >> 4, $z >> 4)->setBlockData($x & 0x0f, $y & 0x7f, $z & 0x0f, $data & 0x0f);
|
||||
}catch(\Exception $e){
|
||||
}
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
foreach($this->chunks as $chunk){
|
||||
//TODO: send generated chunks to be saved
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,99 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
class GenerationInstanceManager extends GenerationRequestManager{
|
||||
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
/** @var GenerationManager */
|
||||
protected $generationManager;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->generationManager = new GenerationLevelManager($this->server, $this);
|
||||
}
|
||||
|
||||
public function process(){
|
||||
$this->generationManager->process();
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
$this->generationManager->shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param string $generator
|
||||
* @param array $options
|
||||
*/
|
||||
public function openLevel(Level $level, $generator, array $options = []){
|
||||
$this->generationManager->openLevel($level->getId(), $level->getSeed(), $generator, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
*/
|
||||
public function closeLevel(Level $level){
|
||||
$this->generationManager->closeLevel($level->getId());
|
||||
}
|
||||
|
||||
public function addNamespace($namespace, $path){
|
||||
|
||||
}
|
||||
|
||||
public function requestChunk(Level $level, $chunkX, $chunkZ){
|
||||
$this->generationManager->enqueueChunk($level->getId(), $chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function getChunk($levelID, $chunkX, $chunkZ){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, true);
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
throw new ChunkException("Invalid Chunk given");
|
||||
}
|
||||
}else{
|
||||
$this->generationManager->closeLevel($levelID);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function receiveChunk($levelID, FullChunk $chunk){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}else{
|
||||
$this->generationManager->closeLevel($levelID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,144 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
|
||||
class GenerationLevelManager extends GenerationManager{
|
||||
|
||||
/** @var GenerationChunkManager[] */
|
||||
protected $levels = [];
|
||||
|
||||
/** @var array */
|
||||
protected $requestQueue = [];
|
||||
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
|
||||
/** @var GenerationInstanceManager */
|
||||
protected $manager;
|
||||
|
||||
protected $maxCount;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
* @param GenerationInstanceManager $manager
|
||||
*/
|
||||
public function __construct(Server $server, GenerationInstanceManager $manager){
|
||||
$this->server = $server;
|
||||
$this->manager = $manager;
|
||||
$this->maxCount = $this->server->getProperty("chunk-generation.per-tick", 1);
|
||||
}
|
||||
|
||||
public function openLevel($levelID, $seed, $class, array $options){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
|
||||
}
|
||||
}
|
||||
|
||||
public function generateChunk($levelID, $chunkX, $chunkZ){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
|
||||
if(isset($this->levels[$levelID])){
|
||||
foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
$this->levels[$levelID]->cleanChangedChunk($index);
|
||||
}
|
||||
|
||||
$this->levels[$levelID]->doGarbageCollection();
|
||||
$this->levels[$levelID]->cleanChangedChunks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function process(){
|
||||
if(count($this->requestQueue) > 0){
|
||||
$count = 0;
|
||||
foreach($this->requestQueue as $levelID => $chunks){
|
||||
if($count >= $this->maxCount){
|
||||
break;
|
||||
}
|
||||
|
||||
if(count($chunks) === 0){
|
||||
unset($this->requestQueue[$levelID]);
|
||||
}else{
|
||||
$key = key($chunks);
|
||||
Level::getXZ($key, $chunkX, $chunkZ);
|
||||
unset($this->requestQueue[$levelID][$key]);
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
++$count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
}
|
||||
$this->levels = [];
|
||||
}
|
||||
|
||||
public function closeLevel($levelID){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->shutdown();
|
||||
unset($this->levels[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
public function enqueueChunk($levelID, $chunkX, $chunkZ){
|
||||
if(!isset($this->requestQueue[$levelID])){
|
||||
$this->requestQueue[$levelID] = [];
|
||||
}
|
||||
if(!isset($this->requestQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){
|
||||
$this->requestQueue[$levelID][$index] = 1;
|
||||
}else{
|
||||
$this->requestQueue[$levelID][$index]++;
|
||||
arsort($this->requestQueue[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $levelID
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
return $this->manager->getChunk($levelID, $chunkX, $chunkZ);
|
||||
}
|
||||
|
||||
public function sendChunk($levelID, FullChunk $chunk){
|
||||
$this->manager->receiveChunk($levelID, $chunk);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Logger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->server->getLogger();
|
||||
}
|
||||
|
||||
}
|
@ -1,274 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
class GenerationManager{
|
||||
|
||||
|
||||
/*
|
||||
* IPC protocol:
|
||||
* int32 (total length)
|
||||
* byte (packet id)
|
||||
* byte[] (length - 1 bytes)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Direction: Both
|
||||
* If Server->Thread, request chunk generation
|
||||
* If Thread->Server, request chunk contents / loading
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 chunkX
|
||||
* int32 chunkZ
|
||||
*/
|
||||
const PACKET_REQUEST_CHUNK = 0x00;
|
||||
|
||||
/*
|
||||
* Direction: Both
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 chunkX
|
||||
* int32 chunkZ
|
||||
* byte className length
|
||||
* byte[] className
|
||||
* byte[] chunk (none if generated flag is not set)
|
||||
*/
|
||||
const PACKET_SEND_CHUNK = 0x01;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
* int32 seed
|
||||
* string class that extends pocketmine\level\generator\Generator
|
||||
* byte[] serialized options array
|
||||
*/
|
||||
const PACKET_OPEN_LEVEL = 0x02;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* byte[] payload:
|
||||
* int32 levelID
|
||||
*/
|
||||
const PACKET_CLOSE_LEVEL = 0x03;
|
||||
|
||||
/*
|
||||
* Direction: Server->Thread
|
||||
* no payload
|
||||
*/
|
||||
const PACKET_SHUTDOWN = 0xff;
|
||||
|
||||
/** @var GenerationThread */
|
||||
protected $thread;
|
||||
|
||||
/** @var \Logger */
|
||||
protected $logger;
|
||||
/** @var \ClassLoader */
|
||||
protected $loader;
|
||||
|
||||
/** @var GenerationChunkManager[] */
|
||||
protected $levels = [];
|
||||
|
||||
/** @var array */
|
||||
protected $requestQueue = [];
|
||||
|
||||
/** @var array */
|
||||
protected $needsChunk = [];
|
||||
|
||||
protected $shutdown = false;
|
||||
|
||||
/**
|
||||
* @param GenerationThread $thread
|
||||
* @param \Logger $logger
|
||||
* @param \ClassLoader $loader
|
||||
*/
|
||||
public function __construct(GenerationThread $thread, \Logger $logger, \ClassLoader $loader){
|
||||
$this->thread = $thread;
|
||||
$this->logger = $logger;
|
||||
$this->loader = $loader;
|
||||
$chunkX = $chunkZ = null;
|
||||
|
||||
while($this->shutdown !== true){
|
||||
try{
|
||||
if(count($this->requestQueue) > 0){
|
||||
foreach($this->requestQueue as $levelID => $chunks){
|
||||
if(count($chunks) === 0){
|
||||
unset($this->requestQueue[$levelID]);
|
||||
}else{
|
||||
$key = key($chunks);
|
||||
Level::getXZ($key, $chunkX, $chunkZ);
|
||||
unset($this->requestQueue[$levelID][$key]);
|
||||
$this->generateChunk($levelID, $chunkX, $chunkZ);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$this->readPacket();
|
||||
}
|
||||
}catch(\Exception $e){
|
||||
$this->logger->warning("[Generator Thread] Exception: " . $e->getMessage() . " on file \"" . $e->getFile() . "\" line " . $e->getLine());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function openLevel($levelID, $seed, $class, array $options){
|
||||
if(!isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID] = new GenerationChunkManager($this, $levelID, $seed, $class, $options);
|
||||
}
|
||||
}
|
||||
|
||||
protected function generateChunk($levelID, $chunkX, $chunkZ){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->populateChunk($chunkX, $chunkZ); //Request population directly
|
||||
if(isset($this->levels[$levelID])){
|
||||
foreach($this->levels[$levelID]->getChangedChunks() as $index => $chunk){
|
||||
if($chunk->isPopulated()){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
$this->levels[$levelID]->cleanChangedChunk($index);
|
||||
}
|
||||
}
|
||||
|
||||
$this->levels[$levelID]->doGarbageCollection();
|
||||
$this->levels[$levelID]->cleanChangedChunks();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function closeLevel($levelID){
|
||||
if(isset($this->levels[$levelID])){
|
||||
$this->levels[$levelID]->shutdown();
|
||||
unset($this->levels[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function enqueueChunk($levelID, $chunkX, $chunkZ){
|
||||
if(!isset($this->requestQueue[$levelID])){
|
||||
$this->requestQueue[$levelID] = [];
|
||||
}
|
||||
if(!isset($this->requestQueue[$levelID][$index = Level::chunkHash($chunkX, $chunkZ)])){
|
||||
$this->requestQueue[$levelID][$index] = 1;
|
||||
}else{
|
||||
$this->requestQueue[$levelID][$index]++;
|
||||
arsort($this->requestQueue[$levelID]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function receiveChunk($levelID, FullChunk $chunk){
|
||||
if($this->needsChunk[$levelID] !== null){
|
||||
if($this->needsChunk[$levelID][0] === $chunk->getX() and $this->needsChunk[$levelID][1] === $chunk->getZ()){
|
||||
$this->needsChunk[$levelID] = $chunk;
|
||||
}
|
||||
}
|
||||
//TODO: set new received chunks
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $levelID
|
||||
* @param $chunkX
|
||||
* @param $chunkZ
|
||||
*
|
||||
* @return FullChunk
|
||||
*/
|
||||
public function requestChunk($levelID, $chunkX, $chunkZ){
|
||||
$this->needsChunk[$levelID] = [$chunkX, $chunkZ];
|
||||
$binary = chr(self::PACKET_REQUEST_CHUNK) . Binary::writeInt($levelID) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
|
||||
$this->thread->pushThreadToMainPacket($binary);
|
||||
|
||||
do{
|
||||
$this->readPacket();
|
||||
}while($this->shutdown !== true and !($this->needsChunk[$levelID] instanceof FullChunk));
|
||||
|
||||
$chunk = $this->needsChunk[$levelID];
|
||||
$this->needsChunk[$levelID] = null;
|
||||
if($chunk instanceof FullChunk){
|
||||
return $chunk;
|
||||
}else{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function sendChunk($levelID, FullChunk $chunk){
|
||||
$binary = chr(self::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
$this->thread->pushThreadToMainPacket($binary);
|
||||
}
|
||||
|
||||
protected function readPacket(){
|
||||
if(strlen($packet = $this->thread->readMainToThreadPacket()) > 0){
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
if($pid === self::PACKET_REQUEST_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkX = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkZ = Binary::readInt(substr($packet, $offset, 4));
|
||||
$this->enqueueChunk($levelID, $chunkX, $chunkZ);
|
||||
}elseif($pid === self::PACKET_SEND_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$chunk = $class::fromBinary(substr($packet, $offset));
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}elseif($pid === self::PACKET_OPEN_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$seed = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = Binary::readShort(substr($packet, $offset, 2));
|
||||
$offset += 2;
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$options = unserialize(substr($packet, $offset));
|
||||
$this->openLevel($levelID, $seed, $class, $options);
|
||||
}elseif($pid === self::PACKET_CLOSE_LEVEL){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$this->closeLevel($levelID);
|
||||
}elseif($pid === self::PACKET_SHUTDOWN){
|
||||
foreach($this->levels as $level){
|
||||
$level->shutdown();
|
||||
}
|
||||
$this->levels = [];
|
||||
|
||||
$this->shutdown = true;
|
||||
}
|
||||
}elseif(count($this->thread->getInternalQueue()) === 0){
|
||||
$this->thread->synchronized(function (){
|
||||
$this->thread->wait(50000);
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Logger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
}
|
@ -1,143 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\ChunkException;
|
||||
|
||||
class GenerationRequestManager{
|
||||
|
||||
/** @var Server */
|
||||
protected $server;
|
||||
/** @var GenerationThread */
|
||||
protected $generationThread;
|
||||
|
||||
/**
|
||||
* @param Server $server
|
||||
*/
|
||||
public function __construct(Server $server){
|
||||
$this->server = $server;
|
||||
$this->generationThread = new GenerationThread($server->getLogger(), $server->getLoader());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
* @param string $generator
|
||||
* @param array $options
|
||||
*/
|
||||
public function openLevel(Level $level, $generator, array $options = []){
|
||||
$buffer = chr(GenerationManager::PACKET_OPEN_LEVEL) . Binary::writeInt($level->getId()) . Binary::writeInt($level->getSeed()) .
|
||||
Binary::writeShort(strlen($generator)) . $generator . serialize($options);
|
||||
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Level $level
|
||||
*/
|
||||
public function closeLevel(Level $level){
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($level->getId());
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function addNamespace($namespace, $path){
|
||||
|
||||
}
|
||||
|
||||
protected function sendChunk($levelID, FullChunk $chunk){
|
||||
$buffer = chr(GenerationManager::PACKET_SEND_CHUNK) . Binary::writeInt($levelID) . chr(strlen($class = get_class($chunk))) . $class . $chunk->toBinary();
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
public function requestChunk(Level $level, $chunkX, $chunkZ){
|
||||
$buffer = chr(GenerationManager::PACKET_REQUEST_CHUNK) . Binary::writeInt($level->getId()) . Binary::writeInt($chunkX) . Binary::writeInt($chunkZ);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
protected function handleRequest($levelID, $chunkX, $chunkZ){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$chunk = $level->getChunk($chunkX, $chunkZ, true);
|
||||
if($chunk instanceof FullChunk){
|
||||
$this->sendChunk($levelID, $chunk);
|
||||
}else{
|
||||
throw new ChunkException("Invalid Chunk given");
|
||||
}
|
||||
}else{
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
}
|
||||
|
||||
protected function receiveChunk($levelID, FullChunk $chunk){
|
||||
if(($level = $this->server->getLevel($levelID)) instanceof Level){
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}else{
|
||||
$buffer = chr(GenerationManager::PACKET_CLOSE_LEVEL) . Binary::writeInt($levelID);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public function process(){
|
||||
$this->handlePackets();
|
||||
}
|
||||
|
||||
public function handlePackets(){
|
||||
while(strlen($packet = $this->generationThread->readThreadToMainPacket()) > 0){
|
||||
$pid = ord($packet{0});
|
||||
$offset = 1;
|
||||
|
||||
if($pid === GenerationManager::PACKET_REQUEST_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkX = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$chunkZ = Binary::readInt(substr($packet, $offset, 4));
|
||||
$this->handleRequest($levelID, $chunkX, $chunkZ);
|
||||
}elseif($pid === GenerationManager::PACKET_SEND_CHUNK){
|
||||
$levelID = Binary::readInt(substr($packet, $offset, 4));
|
||||
$offset += 4;
|
||||
$len = ord($packet{$offset++});
|
||||
/** @var FullChunk $class */
|
||||
$class = substr($packet, $offset, $len);
|
||||
$offset += $len;
|
||||
$level = $this->server->getLevel($levelID);
|
||||
if($level instanceof Level){
|
||||
$chunk = $class::fromBinary(substr($packet, $offset), $level->getProvider());
|
||||
$this->receiveChunk($levelID, $chunk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function shutdown(){
|
||||
$buffer = chr(GenerationManager::PACKET_SHUTDOWN);
|
||||
$this->generationThread->pushMainToThreadPacket($buffer);
|
||||
}
|
||||
|
||||
|
||||
}
|
100
src/pocketmine/level/generator/GenerationTask.php
Normal file
100
src/pocketmine/level/generator/GenerationTask.php
Normal file
@ -0,0 +1,100 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\SimpleChunkManager;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class GenerationTask extends AsyncTask{
|
||||
|
||||
public $generator;
|
||||
public $settings;
|
||||
public $seed;
|
||||
public $levelId;
|
||||
public $chunk;
|
||||
public $chunkClass;
|
||||
|
||||
public function __construct(Level $level, Generator $generator, FullChunk $chunk){
|
||||
$this->generator = get_class($generator);
|
||||
$this->settings = $generator->getSettings();
|
||||
$this->seed = $level->getSeed();
|
||||
$this->levelId = $level->getId();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunkClass = get_class($chunk);
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
/** @var SimpleChunkManager $manager */
|
||||
$manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager");
|
||||
/** @var Generator $generator */
|
||||
$generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator");
|
||||
if($manager === null or $generator === null){
|
||||
Block::init();
|
||||
Biome::init();
|
||||
$manager = new SimpleChunkManager($this->seed);
|
||||
$this->saveToThreadStore($key, $manager);
|
||||
/** @var Generator $generator */
|
||||
$generator = $this->generator;
|
||||
$generator = new $generator($this->settings);
|
||||
$generator->init($manager, new Random($manager->getSeed()));
|
||||
$this->saveToThreadStore($gKey, $generator);
|
||||
}
|
||||
|
||||
/** @var FullChunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk);
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
}
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
|
||||
$generator->generateChunk($chunk->getX(), $chunk->getZ());
|
||||
|
||||
$chunk = $manager->getChunk($chunk->getX(), $chunk->getZ());
|
||||
$chunk->setGenerated(true);
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$level = $server->getLevel($this->levelId);
|
||||
if($level !== null){
|
||||
/** @var FullChunk $chunk */
|
||||
$chunk = $this->chunkClass;
|
||||
$chunk = $chunk::fromFastBinary($this->chunk, $level->getProvider());
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
}
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,120 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
|
||||
use pocketmine\Thread;
|
||||
|
||||
class GenerationThread extends Thread{
|
||||
|
||||
protected $loadPaths;
|
||||
/** @var \ClassLoader */
|
||||
protected $loader;
|
||||
/** @var \ThreadedLogger */
|
||||
protected $logger;
|
||||
|
||||
/** @var \Threaded */
|
||||
protected $externalQueue;
|
||||
/** @var \Threaded */
|
||||
protected $internalQueue;
|
||||
|
||||
/**
|
||||
* @return \Threaded
|
||||
*/
|
||||
public function getInternalQueue(){
|
||||
return $this->internalQueue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Threaded
|
||||
*/
|
||||
public function getExternalQueue(){
|
||||
return $this->externalQueue;
|
||||
}
|
||||
|
||||
public function pushMainToThreadPacket($str){
|
||||
$this->internalQueue[] = $str;
|
||||
$this->synchronized(function (){
|
||||
$this->notify();
|
||||
});
|
||||
}
|
||||
|
||||
public function readMainToThreadPacket(){
|
||||
return $this->internalQueue->shift();
|
||||
}
|
||||
|
||||
public function pushThreadToMainPacket($str){
|
||||
$this->externalQueue[] = $str;
|
||||
}
|
||||
|
||||
public function readThreadToMainPacket(){
|
||||
return $this->externalQueue->shift();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \ThreadedLogger
|
||||
*/
|
||||
public function getLogger(){
|
||||
return $this->logger;
|
||||
}
|
||||
|
||||
public function __construct(\ThreadedLogger $logger, \ClassLoader $loader){
|
||||
$this->loader = $loader;
|
||||
$this->logger = $logger;
|
||||
$loadPaths = [];
|
||||
$this->addDependency($loadPaths, new \ReflectionClass($this->loader));
|
||||
$this->loadPaths = array_reverse($loadPaths);
|
||||
|
||||
$this->externalQueue = \ThreadedFactory::create();
|
||||
$this->internalQueue = \ThreadedFactory::create();
|
||||
|
||||
$this->start();
|
||||
}
|
||||
|
||||
protected function addDependency(array &$loadPaths, \ReflectionClass $dep){
|
||||
if($dep->getFileName() !== false){
|
||||
$loadPaths[$dep->getName()] = $dep->getFileName();
|
||||
}
|
||||
|
||||
if($dep->getParentClass() instanceof \ReflectionClass){
|
||||
$this->addDependency($loadPaths, $dep->getParentClass());
|
||||
}
|
||||
|
||||
foreach($dep->getInterfaces() as $interface){
|
||||
$this->addDependency($loadPaths, $interface);
|
||||
}
|
||||
}
|
||||
|
||||
public function run(){
|
||||
error_reporting(-1);
|
||||
gc_enable();
|
||||
//Load removed dependencies, can't use require_once()
|
||||
foreach($this->loadPaths as $name => $path){
|
||||
if(!class_exists($name, false) and !interface_exists($name, false)){
|
||||
require($path);
|
||||
}
|
||||
}
|
||||
$this->loader->register();
|
||||
|
||||
$generationManager = new GenerationManager($this, $this->getLogger(), $this->loader);
|
||||
}
|
||||
}
|
@ -20,10 +20,13 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generator classes used in Levels
|
||||
* Noise classes used in Levels
|
||||
*/
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\generator\noise\Noise;
|
||||
use pocketmine\level\generator\normal\Normal;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
abstract class Generator{
|
||||
@ -39,6 +42,11 @@ abstract class Generator{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*
|
||||
* @return Generator
|
||||
*/
|
||||
public static function getGenerator($name){
|
||||
if(isset(Generator::$list[$name = strtolower($name)])){
|
||||
return Generator::$list[$name];
|
||||
@ -57,9 +65,177 @@ abstract class Generator{
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Noise $noise
|
||||
* @param int $xSize
|
||||
* @param int $samplingRate
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return \SplFixedArray
|
||||
*/
|
||||
public static function getFastNoise1D(Noise $noise, $xSize, $samplingRate, $x, $y, $z){
|
||||
if($samplingRate === 0){
|
||||
throw new \InvalidArgumentException("samplingRate cannot be 0");
|
||||
}
|
||||
if ($xSize % $samplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("xSize % samplingRate must return 0");
|
||||
}
|
||||
|
||||
$noiseArray = new \SplFixedArray($xSize + 1);
|
||||
|
||||
for($xx = 0; $xx <= $xSize; $xx += $samplingRate){
|
||||
$noiseArray[$xx] = $noise->noise3D($xx + $x, $y, $z);
|
||||
}
|
||||
|
||||
for($xx = 0; $xx < $xSize; ++$xx){
|
||||
if($xx % $samplingRate !== 0){
|
||||
$nx = (int) ($xx / $samplingRate) * $samplingRate;
|
||||
$noiseArray[$xx] = Noise::linearLerp($xx, $nx, $nx + $samplingRate, $noiseArray[$nx], $noiseArray[$nx + $samplingRate]);
|
||||
}
|
||||
}
|
||||
|
||||
return $noiseArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Noise $noise
|
||||
* @param int $xSize
|
||||
* @param int $zSize
|
||||
* @param int $samplingRate
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return \SplFixedArray
|
||||
*/
|
||||
public static function getFastNoise2D(Noise $noise, $xSize, $zSize, $samplingRate, $x, $y, $z){
|
||||
if($samplingRate === 0){
|
||||
throw new \InvalidArgumentException("samplingRate cannot be 0");
|
||||
}
|
||||
if ($xSize % $samplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("xSize % samplingRate must return 0");
|
||||
}
|
||||
if ($zSize % $samplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("zSize % samplingRate must return 0");
|
||||
}
|
||||
|
||||
$noiseArray = new \SplFixedArray($xSize + 1);
|
||||
|
||||
for($xx = 0; $xx <= $xSize; $xx += $samplingRate){
|
||||
$noiseArray[$xx] = new \SplFixedArray($zSize + 1);
|
||||
for($zz = 0; $zz <= $zSize; $zz += $samplingRate){
|
||||
$noiseArray[$xx][$zz] = $noise->noise3D($x + $xx, $y, $z + $zz);
|
||||
}
|
||||
}
|
||||
|
||||
for($xx = 0; $xx < $xSize; ++$xx){
|
||||
if($xx % $samplingRate !== 0){
|
||||
$noiseArray[$xx] = new \SplFixedArray($zSize + 1);
|
||||
}
|
||||
|
||||
for($zz = 0; $zz < $zSize; ++$zz){
|
||||
if($xx % $samplingRate !== 0 or $zz % $samplingRate !== 0){
|
||||
$nx = (int) ($xx / $samplingRate) * $samplingRate;
|
||||
$nz = (int) ($zz / $samplingRate) * $samplingRate;
|
||||
$noiseArray[$xx][$zz] = Noise::bilinearLerp(
|
||||
$xx, $zz, $noiseArray[$nx][$nz], $noiseArray[$nx][$nz + $samplingRate],
|
||||
$noiseArray[$nx + $samplingRate][$nz], $noiseArray[$nx + $samplingRate][$nz + $samplingRate],
|
||||
$nx, $nx + $samplingRate, $nz, $nz + $samplingRate
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $noiseArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Noise $noise
|
||||
* @param int $xSize
|
||||
* @param int $ySize
|
||||
* @param int $zSize
|
||||
* @param int $xSamplingRate
|
||||
* @param int $ySamplingRate
|
||||
* @param int $zSamplingRate
|
||||
* @param int $x
|
||||
* @param int $y
|
||||
* @param int $z
|
||||
*
|
||||
* @return \SplFixedArray
|
||||
*/
|
||||
public static function getFastNoise3D(Noise $noise, $xSize, $ySize, $zSize, $xSamplingRate, $ySamplingRate, $zSamplingRate, $x, $y, $z){
|
||||
if($xSamplingRate === 0){
|
||||
throw new \InvalidArgumentException("xSamplingRate cannot be 0");
|
||||
}
|
||||
if($zSamplingRate === 0){
|
||||
throw new \InvalidArgumentException("zSamplingRate cannot be 0");
|
||||
}
|
||||
if($ySamplingRate === 0){
|
||||
throw new \InvalidArgumentException("ySamplingRate cannot be 0");
|
||||
}
|
||||
if ($xSize % $xSamplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("xSize % xSamplingRate must return 0");
|
||||
}
|
||||
if ($zSize % $zSamplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("zSize % zSamplingRate must return 0");
|
||||
}
|
||||
if ($ySize % $ySamplingRate !== 0) {
|
||||
throw new \InvalidArgumentCountException("ySize % ySamplingRate must return 0");
|
||||
}
|
||||
|
||||
$noiseArray = array_fill(0, $xSize + 1, array_fill(0, $zSize + 1, []));
|
||||
|
||||
for($xx = 0; $xx <= $xSize; $xx += $xSamplingRate){
|
||||
for($zz = 0; $zz <= $zSize; $zz += $zSamplingRate){
|
||||
for($yy = 0; $yy <= $ySize; $yy += $ySamplingRate){
|
||||
$noiseArray[$xx][$zz][$yy] = $noise->noise3D($x + $xx, $y + $yy, $z + $zz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for($xx = 0; $xx < $xSize; ++$xx){
|
||||
for($zz = 0; $zz < $zSize; ++$zz){
|
||||
for($yy = 0; $yy < $ySize; ++$yy){
|
||||
if($xx % $xSamplingRate !== 0 or $zz % $zSamplingRate !== 0 or $yy % $ySamplingRate !== 0){
|
||||
$nx = (int) ($xx / $xSamplingRate) * $xSamplingRate;
|
||||
$ny = (int) ($yy / $ySamplingRate) * $ySamplingRate;
|
||||
$nz = (int) ($zz / $zSamplingRate) * $zSamplingRate;
|
||||
|
||||
$nnx = $nx + $xSamplingRate;
|
||||
$nny = $ny + $ySamplingRate;
|
||||
$nnz = $nz + $zSamplingRate;
|
||||
|
||||
$dx1 = (($nnx - $xx) / ($nnx - $nx));
|
||||
$dx2 = (($xx - $nx) / ($nnx - $nx));
|
||||
$dy1 = (($nny - $yy) / ($nny - $ny));
|
||||
$dy2 = (($yy - $ny) / ($nny - $ny));
|
||||
|
||||
$noiseArray[$xx][$zz][$yy] = (($nnz - $zz) / ($nnz - $nz)) * (
|
||||
$dy1 * (
|
||||
$dx1 * $noiseArray[$nx][$nz][$ny] + $dx2 * $noiseArray[$nnx][$nz][$ny]
|
||||
) + $dy2 * (
|
||||
$dx1 * $noiseArray[$nx][$nz][$nny] + $dx2 * $noiseArray[$nnx][$nz][$nny]
|
||||
)
|
||||
) + (($zz - $nz) / ($nnz - $nz)) * (
|
||||
$dy1 * (
|
||||
$dx1 * $noiseArray[$nx][$nnz][$ny] + $dx2 * $noiseArray[$nnx][$nnz][$ny]
|
||||
) + $dy2 * (
|
||||
$dx1 * $noiseArray[$nx][$nnz][$nny] + $dx2 * $noiseArray[$nnx][$nnz][$nny]
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $noiseArray;
|
||||
}
|
||||
|
||||
public abstract function __construct(array $settings = []);
|
||||
|
||||
public abstract function init(GenerationChunkManager $level, Random $random);
|
||||
public abstract function init(ChunkManager $level, Random $random);
|
||||
|
||||
public abstract function generateChunk($chunkX, $chunkZ);
|
||||
|
||||
|
@ -1,163 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\CoalOre;
|
||||
use pocketmine\block\DiamondOre;
|
||||
use pocketmine\block\Dirt;
|
||||
use pocketmine\block\GoldOre;
|
||||
use pocketmine\block\Gravel;
|
||||
use pocketmine\block\IronOre;
|
||||
use pocketmine\block\LapisOre;
|
||||
use pocketmine\block\RedstoneOre;
|
||||
use pocketmine\level\generator\noise\Simplex;
|
||||
use pocketmine\level\generator\object\OreType;
|
||||
use pocketmine\level\generator\populator\Ore;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Normal extends Generator{
|
||||
|
||||
/** @var Populator[] */
|
||||
private $populators = [];
|
||||
/** @var GenerationChunkManager */
|
||||
private $level;
|
||||
/** @var Random */
|
||||
private $random;
|
||||
private $worldHeight = 65;
|
||||
private $waterHeight = 63;
|
||||
/** @var Simplex */
|
||||
private $noiseHills;
|
||||
/** @var Simplex */
|
||||
private $noiseBase;
|
||||
|
||||
public function __construct(array $options = []){
|
||||
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "normal";
|
||||
}
|
||||
|
||||
public function getSettings(){
|
||||
return [];
|
||||
}
|
||||
|
||||
public function init(GenerationChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
$this->noiseHills = new Simplex($this->random, 3, 0.1, 12);
|
||||
$this->noiseBase = new Simplex($this->random, 16, 0.6, 16);
|
||||
|
||||
|
||||
$ores = new Ore();
|
||||
$ores->setOreTypes([
|
||||
new OreType(new CoalOre(), 20, 16, 0, 128),
|
||||
new OreType(New IronOre(), 20, 8, 0, 64),
|
||||
new OreType(new RedstoneOre(), 8, 7, 0, 16),
|
||||
new OreType(new LapisOre(), 1, 6, 0, 32),
|
||||
new OreType(new GoldOre(), 2, 8, 0, 32),
|
||||
new OreType(new DiamondOre(), 1, 7, 0, 16),
|
||||
new OreType(new Dirt(), 20, 32, 0, 128),
|
||||
new OreType(new Gravel(), 10, 16, 0, 128),
|
||||
]);
|
||||
$this->populators[] = $ores;
|
||||
|
||||
$trees = new Tree();
|
||||
$trees->setBaseAmount(1);
|
||||
$trees->setRandomAmount(1);
|
||||
$this->populators[] = $trees;
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(5);
|
||||
$tallGrass->setRandomAmount(0);
|
||||
$this->populators[] = $tallGrass;
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
|
||||
$hills = [];
|
||||
$base = [];
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
$i = ($z << 4) + $x;
|
||||
$hills[$i] = $this->noiseHills->noise2D($x + ($chunkX << 4), $z + ($chunkZ << 4), true);
|
||||
$base[$i] = $this->noiseBase->noise2D($x + ($chunkX << 4), $z + ($chunkZ << 4), true);
|
||||
|
||||
if($base[$i] < 0){
|
||||
$base[$i] *= 0.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$chunk = $this->level->getChunk($chunkX, $chunkZ);
|
||||
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
$i = ($z << 4) + $x;
|
||||
$height = $this->worldHeight + $hills[$i] * 14 + $base[$i] * 7;
|
||||
$height = (int) $height;
|
||||
|
||||
for($y = 0; $y < 128; ++$y){
|
||||
$diff = $height - $y;
|
||||
if($y <= 4 and ($y === 0 or $this->random->nextFloat() < 0.75)){
|
||||
$chunk->setBlockId($x, $y, $z, Block::BEDROCK);
|
||||
}elseif($diff > 2){
|
||||
$chunk->setBlockId($x, $y, $z, Block::STONE);
|
||||
}elseif($diff > 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::DIRT);
|
||||
}elseif($y <= $this->waterHeight){
|
||||
if(($this->waterHeight - $y) <= 1 and $diff === 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::SAND);
|
||||
}elseif($diff === 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::DIRT);
|
||||
}else{
|
||||
$chunk->setBlockId($x, $y, $z, Block::STILL_WATER);
|
||||
}
|
||||
}elseif($diff === 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::GRASS);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
|
||||
foreach($this->populators as $populator){
|
||||
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
|
||||
$populator->populate($this->level, $chunkX, $chunkZ, $this->random);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSpawn(){
|
||||
return $this->level->getSafeSpawn(new Vector3(127.5, 128, 127.5));
|
||||
}
|
||||
|
||||
}
|
181
src/pocketmine/level/generator/PopulationTask.php
Normal file
181
src/pocketmine/level/generator/PopulationTask.php
Normal file
@ -0,0 +1,181 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\format\FullChunk;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\level\SimpleChunkManager;
|
||||
use pocketmine\scheduler\AsyncTask;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class PopulationTask extends AsyncTask{
|
||||
|
||||
public $generator;
|
||||
public $settings;
|
||||
public $seed;
|
||||
public $levelId;
|
||||
public $chunk;
|
||||
public $chunkClass;
|
||||
|
||||
public $chunk00;
|
||||
public $chunk01;
|
||||
public $chunk02;
|
||||
public $chunk10;
|
||||
//center chunk
|
||||
public $chunk12;
|
||||
public $chunk20;
|
||||
public $chunk21;
|
||||
public $chunk22;
|
||||
|
||||
public function __construct(Level $level, Generator $generator, FullChunk $chunk){
|
||||
$this->generator = get_class($generator);
|
||||
$this->settings = $generator->getSettings();
|
||||
$this->seed = $level->getSeed();
|
||||
$this->levelId = $level->getId();
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
$this->chunkClass = get_class($chunk);
|
||||
|
||||
$this->chunk00 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() - 1, true)->toFastBinary();
|
||||
$this->chunk01 = $level->getChunk($chunk->getX() - 1, $chunk->getZ(), true)->toFastBinary();
|
||||
$this->chunk02 = $level->getChunk($chunk->getX() - 1, $chunk->getZ() + 1, true)->toFastBinary();
|
||||
$this->chunk10 = $level->getChunk($chunk->getX(), $chunk->getZ() - 1, true)->toFastBinary();
|
||||
|
||||
$this->chunk12 = $level->getChunk($chunk->getX(), $chunk->getZ() + 1, true)->toFastBinary();
|
||||
$this->chunk20 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() - 1, true)->toFastBinary();
|
||||
$this->chunk21 = $level->getChunk($chunk->getX() + 1, $chunk->getZ(), true)->toFastBinary();
|
||||
$this->chunk22 = $level->getChunk($chunk->getX() + 1, $chunk->getZ() + 1, true)->toFastBinary();
|
||||
}
|
||||
|
||||
public function onRun(){
|
||||
/** @var SimpleChunkManager $manager */
|
||||
$manager = $this->getFromThreadStore($key = "generation.level{$this->levelId}.manager");
|
||||
/** @var Generator $generator */
|
||||
$generator = $this->getFromThreadStore($gKey = "generation.level{$this->levelId}.generator");
|
||||
if($manager === null or $generator === null){
|
||||
Block::init();
|
||||
Biome::init();
|
||||
$manager = new SimpleChunkManager($this->seed);
|
||||
$this->saveToThreadStore($key, $manager);
|
||||
/** @var Generator $generator */
|
||||
$generator = $this->generator;
|
||||
$generator = new $generator($this->settings);
|
||||
$generator->init($manager, new Random($manager->getSeed()));
|
||||
$this->saveToThreadStore($gKey, $generator);
|
||||
}
|
||||
|
||||
/** @var FullChunk[] $chunks */
|
||||
$chunks = [];
|
||||
/** @var FullChunk $chunkC */
|
||||
$chunkC = $this->chunkClass;
|
||||
|
||||
$chunks[0] = $chunkC::fromFastBinary($this->chunk00);
|
||||
$chunks[1] = $chunkC::fromFastBinary($this->chunk01);
|
||||
$chunks[2] = $chunkC::fromFastBinary($this->chunk02);
|
||||
$chunks[3] = $chunkC::fromFastBinary($this->chunk10);
|
||||
$chunk = $chunkC::fromFastBinary($this->chunk);
|
||||
$chunks[5] = $chunkC::fromFastBinary($this->chunk12);
|
||||
$chunks[6] = $chunkC::fromFastBinary($this->chunk20);
|
||||
$chunks[7] = $chunkC::fromFastBinary($this->chunk21);
|
||||
$chunks[8] = $chunkC::fromFastBinary($this->chunk22);
|
||||
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
}
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
|
||||
foreach($chunks as $c){
|
||||
if($c !== null){
|
||||
$manager->setChunk($c->getX(), $c->getZ(), $c);
|
||||
}
|
||||
}
|
||||
|
||||
$generator->populateChunk($chunk->getX(), $chunk->getZ());
|
||||
|
||||
$chunk = $manager->getChunk($chunk->getX(), $chunk->getZ());
|
||||
$chunk->setPopulated(true);
|
||||
$this->chunk = $chunk->toFastBinary();
|
||||
|
||||
$manager->setChunk($chunk->getX(), $chunk->getZ(), null);
|
||||
|
||||
foreach($chunks as $i => $c){
|
||||
if($c !== null){
|
||||
$c = $chunks[$i] = $manager->getChunk($c->getX(), $c->getZ());
|
||||
if(!$c->hasChanged()){
|
||||
$chunks[$i] = null;
|
||||
}
|
||||
$manager->setChunk($c->getX(), $c->getZ(), null);
|
||||
}else{
|
||||
//This way non-changed chunks are not set
|
||||
$chunks[$i] = null;
|
||||
}
|
||||
}
|
||||
|
||||
$this->chunk00 = $chunks[0] !== null ? $chunks[0]->toFastBinary() : null;
|
||||
$this->chunk01 = $chunks[1] !== null ? $chunks[1]->toFastBinary() : null;
|
||||
$this->chunk02 = $chunks[2] !== null ? $chunks[2]->toFastBinary() : null;
|
||||
$this->chunk10 = $chunks[3] !== null ? $chunks[3]->toFastBinary() : null;
|
||||
|
||||
$this->chunk12 = $chunks[5] !== null ? $chunks[5]->toFastBinary() : null;
|
||||
$this->chunk20 = $chunks[6] !== null ? $chunks[6]->toFastBinary() : null;
|
||||
$this->chunk21 = $chunks[7] !== null ? $chunks[7]->toFastBinary() : null;
|
||||
$this->chunk22 = $chunks[8] !== null ? $chunks[8]->toFastBinary() : null;
|
||||
}
|
||||
|
||||
public function onCompletion(Server $server){
|
||||
$level = $server->getLevel($this->levelId);
|
||||
if($level !== null){
|
||||
/** @var FullChunk[] $chunks */
|
||||
$chunks = [];
|
||||
/** @var FullChunk $chunkC */
|
||||
$chunkC = $this->chunkClass;
|
||||
|
||||
$chunks[0] = $chunkC::fromFastBinary($this->chunk00, $level->getProvider());
|
||||
$chunks[1] = $chunkC::fromFastBinary($this->chunk01, $level->getProvider());
|
||||
$chunks[2] = $chunkC::fromFastBinary($this->chunk02, $level->getProvider());
|
||||
$chunks[3] = $chunkC::fromFastBinary($this->chunk10, $level->getProvider());
|
||||
$chunk = $chunkC::fromFastBinary($this->chunk, $level->getProvider());
|
||||
$chunks[5] = $chunkC::fromFastBinary($this->chunk12, $level->getProvider());
|
||||
$chunks[6] = $chunkC::fromFastBinary($this->chunk20, $level->getProvider());
|
||||
$chunks[7] = $chunkC::fromFastBinary($this->chunk21, $level->getProvider());
|
||||
$chunks[8] = $chunkC::fromFastBinary($this->chunk22, $level->getProvider());
|
||||
|
||||
foreach($chunks as $c){
|
||||
if($c !== null){
|
||||
$level->generateChunkCallback($c->getX(), $c->getZ(), $c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if($chunk === null){
|
||||
//TODO error
|
||||
return;
|
||||
}
|
||||
|
||||
$level->generateChunkCallback($chunk->getX(), $chunk->getZ(), $chunk);
|
||||
}
|
||||
}
|
||||
}
|
181
src/pocketmine/level/generator/biome/Biome.php
Normal file
181
src/pocketmine/level/generator/biome/Biome.php
Normal file
@ -0,0 +1,181 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\biome;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\generator\normal\biome\SwampBiome;
|
||||
use pocketmine\level\generator\normal\biome\DesertBiome;
|
||||
use pocketmine\level\generator\normal\biome\ForestBiome;
|
||||
use pocketmine\level\generator\normal\biome\IcePlainsBiome;
|
||||
use pocketmine\level\generator\normal\biome\MountainsBiome;
|
||||
use pocketmine\level\generator\normal\biome\OceanBiome;
|
||||
use pocketmine\level\generator\normal\biome\PlainBiome;
|
||||
use pocketmine\level\generator\normal\biome\RiverBiome;
|
||||
use pocketmine\level\generator\normal\biome\SmallMountainsBiome;
|
||||
use pocketmine\level\generator\normal\biome\TaigaBiome;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
abstract class Biome{
|
||||
|
||||
const OCEAN = 0;
|
||||
const PLAINS = 1;
|
||||
const DESERT = 2;
|
||||
const MOUNTAINS = 3;
|
||||
const FOREST = 4;
|
||||
const TAIGA = 5;
|
||||
const SWAMP = 6;
|
||||
const RIVER = 7;
|
||||
|
||||
const ICE_PLAINS = 12;
|
||||
|
||||
|
||||
const SMALL_MOUNTAINS = 20;
|
||||
|
||||
|
||||
const BIRCH_FOREST = 27;
|
||||
|
||||
|
||||
const MAX_BIOMES = 256;
|
||||
|
||||
/** @var Biome[] */
|
||||
private static $biomes = [];
|
||||
|
||||
private $id;
|
||||
private $registered = false;
|
||||
/** @var Populator[] */
|
||||
private $populators = [];
|
||||
|
||||
private $minElevation;
|
||||
private $maxElevation;
|
||||
|
||||
private $groundCover = [];
|
||||
|
||||
protected $rainfall = 0.5;
|
||||
protected $temperature = 0.5;
|
||||
|
||||
protected static function register($id, Biome $biome){
|
||||
self::$biomes[(int) $id] = $biome;
|
||||
$biome->setId((int) $id);
|
||||
}
|
||||
|
||||
public static function init(){
|
||||
self::register(self::OCEAN, new OceanBiome());
|
||||
self::register(self::PLAINS, new PlainBiome());
|
||||
self::register(self::DESERT, new DesertBiome());
|
||||
self::register(self::MOUNTAINS, new MountainsBiome());
|
||||
self::register(self::FOREST, new ForestBiome());
|
||||
self::register(self::TAIGA, new TaigaBiome());
|
||||
|
||||
self::register(self::RIVER, new RiverBiome());
|
||||
|
||||
self::register(self::ICE_PLAINS, new IcePlainsBiome());
|
||||
|
||||
self::register(self::SWAMP, new SwampBiome());
|
||||
|
||||
self::register(self::SMALL_MOUNTAINS, new SmallMountainsBiome());
|
||||
|
||||
self::register(self::BIRCH_FOREST, new ForestBiome(ForestBiome::TYPE_BIRCH));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
*
|
||||
* @return Biome
|
||||
*/
|
||||
public static function getBiome($id){
|
||||
return isset(self::$biomes[$id]) ? self::$biomes[$id] : self::$biomes[self::OCEAN];
|
||||
}
|
||||
|
||||
public function clearPopulators(){
|
||||
$this->populators = [];
|
||||
}
|
||||
|
||||
public function addPopulator(Populator $populator){
|
||||
$this->populators[] = $populator;
|
||||
}
|
||||
|
||||
public function populateChunk(ChunkManager $level, $chunkX, $chunkZ, Random $random){
|
||||
foreach($this->populators as $populator){
|
||||
$populator->populate($level, $chunkX, $chunkZ, $random);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPopulators(){
|
||||
return $this->populators;
|
||||
}
|
||||
|
||||
public function setId($id){
|
||||
if(!$this->registered){
|
||||
$this->registered = true;
|
||||
$this->id = $id;
|
||||
}
|
||||
}
|
||||
|
||||
public function getId(){
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public abstract function getName();
|
||||
|
||||
public function getMinElevation(){
|
||||
return $this->minElevation;
|
||||
}
|
||||
|
||||
public function getMaxElevation(){
|
||||
return $this->maxElevation;
|
||||
}
|
||||
|
||||
public function setElevation($min, $max){
|
||||
$this->minElevation = $min;
|
||||
$this->maxElevation = $max;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block[]
|
||||
*/
|
||||
public function getGroundCover(){
|
||||
return $this->groundCover;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block[] $covers
|
||||
*/
|
||||
public function setGroundCover(array $covers){
|
||||
$this->groundCover = $covers;
|
||||
}
|
||||
|
||||
public function getTemperature(){
|
||||
return $this->temperature;
|
||||
}
|
||||
|
||||
public function getRainfall(){
|
||||
return $this->rainfall;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return int (randomness|Red|Green|Blue)
|
||||
*/
|
||||
abstract public function getColor();
|
||||
}
|
86
src/pocketmine/level/generator/biome/BiomeSelector.php
Normal file
86
src/pocketmine/level/generator/biome/BiomeSelector.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\biome;
|
||||
|
||||
use pocketmine\level\generator\noise\Simplex;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class BiomeSelector{
|
||||
|
||||
/** @var Biome */
|
||||
private $fallback;
|
||||
|
||||
/** @var Simplex */
|
||||
private $temperature;
|
||||
/** @var Simplex */
|
||||
private $rainfall;
|
||||
|
||||
/** @var Biome[] */
|
||||
private $biomes = [];
|
||||
|
||||
private $map = [];
|
||||
|
||||
private $lookup;
|
||||
|
||||
public function __construct(Random $random, callable $lookup, Biome $fallback){
|
||||
$this->fallback = $fallback;
|
||||
$this->lookup = $lookup;
|
||||
$this->temperature = new Simplex($random, 1, 0.001, 1, 1);
|
||||
$this->rainfall = new Simplex($random, 1, 0.001, 1, 1);
|
||||
}
|
||||
|
||||
public function recalculate(){
|
||||
$this->map = new \SplFixedArray(64 * 64);
|
||||
|
||||
for($i = 0; $i < 64; ++$i){
|
||||
for($j = 0; $j < 64; ++$j){
|
||||
$this->map[$i + ($j << 6)] = call_user_func($this->lookup, $i / 63, $j / 63);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addBiome(Biome $biome){
|
||||
$this->biomes[$biome->getId()] = $biome;
|
||||
}
|
||||
|
||||
public function getTemperature($x, $z){
|
||||
return ($this->temperature->noise2D($x, $z, true) + 1) / 2;
|
||||
}
|
||||
|
||||
public function getRainfall($x, $z){
|
||||
return ($this->rainfall->noise2D($x, $z, true) + 1) / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $x
|
||||
* @param $z
|
||||
*
|
||||
* @return Biome
|
||||
*/
|
||||
public function pickBiome($x, $z){
|
||||
$temperature = (int) ($this->getTemperature($x, $z) * 63);
|
||||
$rainfall = (int) ($this->getRainfall($x, $z) * 63);
|
||||
|
||||
$biomeId = $this->map[$temperature + ($rainfall << 6)];
|
||||
return isset($this->biomes[$biomeId]) ? $this->biomes[$biomeId] : $this->fallback;
|
||||
}
|
||||
}
|
@ -28,13 +28,14 @@
|
||||
namespace pocketmine\level\generator\noise;
|
||||
|
||||
|
||||
abstract class Generator{
|
||||
abstract class Noise{
|
||||
protected $perm = [];
|
||||
protected $offsetX = 0;
|
||||
protected $offsetY = 0;
|
||||
protected $offsetZ = 0;
|
||||
protected $octaves = 8;
|
||||
protected $frequency;
|
||||
protected $lacunarity;
|
||||
protected $amplitude;
|
||||
|
||||
public static function floor($x){
|
||||
@ -49,6 +50,42 @@ abstract class Generator{
|
||||
return $y + $x * ($z - $y);
|
||||
}
|
||||
|
||||
public static function linearLerp($x, $x1, $x2, $q0, $q1){
|
||||
return (($x2 - $x) / ($x2 - $x1)) * $q0 + (($x - $x1) / ($x2 - $x1)) * $q1;
|
||||
}
|
||||
|
||||
public static function bilinearLerp($x, $y, $q00, $q01, $q10, $q11, $x1, $x2, $y1, $y2){
|
||||
$dx1 = (($x2 - $x) / ($x2 - $x1));
|
||||
$dx2 = (($x - $x1) / ($x2 - $x1));
|
||||
|
||||
return (($y2 - $y) / ($y2 - $y1)) * (
|
||||
$dx1 * $q00 + $dx2 * $q10
|
||||
) + (($y - $y1) / ($y2 - $y1)) * (
|
||||
$dx1 * $q01 + $dx2 * $q11
|
||||
);
|
||||
}
|
||||
|
||||
public static function trilinearLerp($x, $y, $z, $q000, $q001, $q010, $q011, $q100, $q101, $q110, $q111, $x1, $x2, $y1, $y2, $z1, $z2) {
|
||||
$dx1 = (($x2 - $x) / ($x2 - $x1));
|
||||
$dx2 = (($x - $x1) / ($x2 - $x1));
|
||||
$dy1 = (($y2 - $y) / ($y2 - $y1));
|
||||
$dy2 = (($y - $y1) / ($y2 - $y1));
|
||||
|
||||
return (($z2 - $z) / ($z2 - $z1)) * (
|
||||
$dy1 * (
|
||||
$dx1 * $q000 + $dx2 * $q100
|
||||
) + $dy2 * (
|
||||
$dx1 * $q001 + $dx2 * $q101
|
||||
)
|
||||
) + (($z - $z1) / ($z2 - $z1)) * (
|
||||
$dy1 * (
|
||||
$dx1 * $q010 + $dx2 * $q110
|
||||
) + $dy2 * (
|
||||
$dx1 * $q011 + $dx2 * $q111
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static function grad($hash, $x, $y, $z){
|
||||
$hash &= 15;
|
||||
$u = $hash < 8 ? $x : $y;
|
||||
@ -64,13 +101,16 @@ abstract class Generator{
|
||||
public function noise2D($x, $z, $normalized = false){
|
||||
$result = 0;
|
||||
$amp = 1;
|
||||
$freq = 1;
|
||||
$laq = 1;
|
||||
$max = 0;
|
||||
|
||||
$x *= $this->frequency;
|
||||
$z *= $this->frequency;
|
||||
|
||||
for($i = 0; $i < $this->octaves; ++$i){
|
||||
$result += $this->getNoise2D($x * $freq, $z * $freq) * $amp;
|
||||
$result += $this->getNoise2D($x * $laq, $z * $laq) * $amp;
|
||||
$max += $amp;
|
||||
$freq *= $this->frequency;
|
||||
$laq *= $this->lacunarity;
|
||||
$amp *= $this->amplitude;
|
||||
}
|
||||
if($normalized === true){
|
||||
@ -83,13 +123,17 @@ abstract class Generator{
|
||||
public function noise3D($x, $y, $z, $normalized = false){
|
||||
$result = 0;
|
||||
$amp = 1;
|
||||
$freq = 1;
|
||||
$laq = 1;
|
||||
$max = 0;
|
||||
|
||||
$x *= $this->frequency;
|
||||
$y *= $this->frequency;
|
||||
$z *= $this->frequency;
|
||||
|
||||
for($i = 0; $i < $this->octaves; ++$i){
|
||||
$result += $this->getNoise3D($x * $freq, $y * $freq, $z * $freq) * $amp;
|
||||
$result += $this->getNoise3D($x * $laq, $y * $laq, $z * $laq) * $amp;
|
||||
$max += $amp;
|
||||
$freq *= $this->frequency;
|
||||
$laq *= $this->lacunarity;
|
||||
$amp *= $this->amplitude;
|
||||
}
|
||||
if($normalized === true){
|
@ -23,7 +23,7 @@ namespace pocketmine\level\generator\noise;
|
||||
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Perlin extends Generator{
|
||||
class Perlin extends Noise{
|
||||
public static $grad3 = [
|
||||
[1, 1, 0], [-1, 1, 0], [1, -1, 0], [-1, -1, 0],
|
||||
[1, 0, 1], [-1, 0, 1], [1, 0, -1], [-1, 0, -1],
|
||||
@ -31,9 +31,10 @@ class Perlin extends Generator{
|
||||
];
|
||||
|
||||
|
||||
public function __construct(Random $random, $octaves, $frequency, $amplitude){
|
||||
public function __construct(Random $random, $octaves, $frequency, $amplitude, $lacunarity){
|
||||
$this->octaves = $octaves;
|
||||
$this->frequency = $frequency;
|
||||
$this->lacunarity = $lacunarity;
|
||||
$this->amplitude = $amplitude;
|
||||
$this->offsetX = $random->nextFloat() * 256;
|
||||
$this->offsetY = $random->nextFloat() * 256;
|
||||
@ -44,11 +45,11 @@ class Perlin extends Generator{
|
||||
}
|
||||
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$this->perm[$i] = $random->nextRange(0, 255);
|
||||
$this->perm[$i] = $random->nextBoundedInt(256);
|
||||
}
|
||||
|
||||
for($i = 0; $i < 256; ++$i){
|
||||
$pos = $random->nextRange(0, 255 - $i) + $i;
|
||||
$pos = $random->nextBoundedInt(256 - $i) + $i;
|
||||
$old = $this->perm[$i];
|
||||
|
||||
$this->perm[$i] = $this->perm[$pos];
|
||||
@ -63,9 +64,9 @@ class Perlin extends Generator{
|
||||
$y += $this->offsetY;
|
||||
$z += $this->offsetZ;
|
||||
|
||||
$floorX = (int) floor($x);
|
||||
$floorY = (int) floor($y);
|
||||
$floorZ = (int) floor($z);
|
||||
$floorX = (int) $x;
|
||||
$floorY = (int) $y;
|
||||
$floorZ = (int) $z;
|
||||
|
||||
$X = $floorX & 0xFF;
|
||||
$Y = $floorY & 0xFF;
|
||||
@ -76,26 +77,70 @@ class Perlin extends Generator{
|
||||
$z -= $floorZ;
|
||||
|
||||
//Fade curves
|
||||
$fX = self::fade($x);
|
||||
$fY = self::fade($y);
|
||||
$fZ = self::fade($z);
|
||||
//$fX = self::fade($x);
|
||||
//$fY = self::fade($y);
|
||||
//$fZ = self::fade($z);
|
||||
$fX = $x ** 3 * ($x * ($x * 6 - 15) + 10);
|
||||
$fY = $y ** 3 * ($y * ($y * 6 - 15) + 10);
|
||||
$fZ = $z ** 3 * ($z * ($z * 6 - 15) + 10);
|
||||
|
||||
//Cube corners
|
||||
$A = $this->perm[$X] + $Y;
|
||||
$B = $this->perm[$X + 1] + $Y;
|
||||
|
||||
$AA = $this->perm[$A] + $Z;
|
||||
$AB = $this->perm[$A + 1] + $Z;
|
||||
$B = $this->perm[$X + 1] + $Y;
|
||||
$BA = $this->perm[$B] + $Z;
|
||||
$BB = $this->perm[$B + 1] + $Z;
|
||||
|
||||
return self::lerp($fZ, self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA], $x, $y, $z),
|
||||
self::grad($this->perm[$BA], $x - 1, $y, $z)),
|
||||
self::lerp($fX, self::grad($this->perm[$AB], $x, $y - 1, $z),
|
||||
self::grad($this->perm[$BB], $x - 1, $y - 1, $z))),
|
||||
self::lerp($fY, self::lerp($fX, self::grad($this->perm[$AA + 1], $x, $y, $z - 1),
|
||||
self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)),
|
||||
self::lerp($fX, self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1),
|
||||
self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1))));
|
||||
$AA1 = self::grad($this->perm[$AA], $x, $y, $z);
|
||||
$BA1 = self::grad($this->perm[$BA], $x - 1, $y, $z);
|
||||
$AB1 = self::grad($this->perm[$AB], $x, $y - 1, $z);
|
||||
$BB1 = self::grad($this->perm[$BB], $x - 1, $y - 1, $z);
|
||||
$AA2 = self::grad($this->perm[$AA + 1], $x, $y, $z - 1);
|
||||
$BA2 = self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1);
|
||||
$AB2 = self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1);
|
||||
$BB2 = self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1);
|
||||
|
||||
$xLerp11 = $AA1 + $fX * ($BA1 - $AA1);
|
||||
|
||||
$zLerp1 = $xLerp11 + $fY * ($AB1 + $fX * ($BB1 - $AB1) - $xLerp11);
|
||||
|
||||
$xLerp21 = $AA2 + $fX * ($BA2 - $AA2);
|
||||
|
||||
return $zLerp1 + $fZ * ($xLerp21 + $fY * ($AB2 + $fX * ($BB2 - $AB2) - $xLerp21) - $zLerp1);
|
||||
|
||||
/*
|
||||
return self::lerp(
|
||||
$fZ,
|
||||
self::lerp(
|
||||
$fY,
|
||||
self::lerp(
|
||||
$fX,
|
||||
self::grad($this->perm[$AA], $x, $y, $z),
|
||||
self::grad($this->perm[$BA], $x - 1, $y, $z)
|
||||
),
|
||||
self::lerp(
|
||||
$fX,
|
||||
self::grad($this->perm[$AB], $x, $y - 1, $z),
|
||||
self::grad($this->perm[$BB], $x - 1, $y - 1, $z)
|
||||
)
|
||||
),
|
||||
self::lerp(
|
||||
$fY,
|
||||
self::lerp(
|
||||
$fX,
|
||||
self::grad($this->perm[$AA + 1], $x, $y, $z - 1),
|
||||
self::grad($this->perm[$BA + 1], $x - 1, $y, $z - 1)
|
||||
),
|
||||
self::lerp(
|
||||
$fX,
|
||||
self::grad($this->perm[$AB + 1], $x, $y - 1, $z - 1),
|
||||
self::grad($this->perm[$BB + 1], $x - 1, $y - 1, $z - 1)
|
||||
)
|
||||
)
|
||||
);
|
||||
*/
|
||||
}
|
||||
|
||||
public function getNoise2D($x, $y){
|
||||
|
@ -63,8 +63,8 @@ class Simplex extends Perlin{
|
||||
protected $offsetW;
|
||||
|
||||
|
||||
public function __construct(Random $random, $octaves, $frequency, $amplitude){
|
||||
parent::__construct($random, $octaves, $frequency, $amplitude);
|
||||
public function __construct(Random $random, $octaves, $frequency, $amplitude, $lacunarity){
|
||||
parent::__construct($random, $octaves, $frequency, $amplitude, $lacunarity);
|
||||
$this->offsetW = $random->nextFloat() * 256;
|
||||
self::$SQRT_3 = sqrt(3);
|
||||
self::$SQRT_5 = sqrt(5);
|
||||
@ -99,16 +99,14 @@ class Simplex extends Perlin{
|
||||
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
$s = ($x + $y + $z) * self::$F3; // Very nice and simple skew factor for 3D
|
||||
$i = (int) floor($x + $s);
|
||||
$j = (int) floor($y + $s);
|
||||
$k = (int) floor($z + $s);
|
||||
$i = (int) ($x + $s);
|
||||
$j = (int) ($y + $s);
|
||||
$k = (int) ($z + $s);
|
||||
$t = ($i + $j + $k) * self::$G3;
|
||||
$X0 = $i - $t; // Unskew the cell origin back to (x,y,z) space
|
||||
$Y0 = $j - $t;
|
||||
$Z0 = $k - $t;
|
||||
$x0 = $x - $X0; // The x,y,z distances from the cell origin
|
||||
$y0 = $y - $Y0;
|
||||
$z0 = $z - $Z0;
|
||||
// Unskew the cell origin back to (x,y,z) space
|
||||
$x0 = $x - ($i - $t); // The x,y,z distances from the cell origin
|
||||
$y0 = $y - ($j - $t);
|
||||
$z0 = $z - ($k - $t);
|
||||
|
||||
// For the 3D case, the simplex shape is a slightly irregular tetrahedron.
|
||||
|
||||
@ -185,47 +183,37 @@ class Simplex extends Perlin{
|
||||
$ii = $i & 255;
|
||||
$jj = $j & 255;
|
||||
$kk = $k & 255;
|
||||
$gi0 = $this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12;
|
||||
$gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12;
|
||||
$gi2 = $this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12;
|
||||
$gi3 = $this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12;
|
||||
|
||||
$n = 0;
|
||||
|
||||
// Calculate the contribution from the four corners
|
||||
$t0 = 0.6 - $x0 ** 2 - $y0 ** 2 - $z0 ** 2;
|
||||
if($t0 < 0){
|
||||
$n0 = 0.0;
|
||||
}else{
|
||||
$t0 **= 2;
|
||||
$n0 = $t0 ** 2 * self::dot3D(self::$grad3[$gi0], $x0, $y0, $z0);
|
||||
if($t0 > 0){
|
||||
$gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj + $this->perm[$kk]]] % 12];
|
||||
$n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0 + $gi0[2] * $z0);
|
||||
}
|
||||
|
||||
$t1 = 0.6 - $x1 ** 2 - $y1 ** 2 - $z1 ** 2;
|
||||
if($t1 < 0){
|
||||
$n1 = 0.0;
|
||||
}else{
|
||||
$t1 **= 2;
|
||||
$n1 = $t1 ** 2 * self::dot3D(self::$grad3[$gi1], $x1, $y1, $z1);
|
||||
if($t1 > 0){
|
||||
$gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1 + $this->perm[$kk + $k1]]] % 12];
|
||||
$n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1 + $gi1[2] * $z1);
|
||||
}
|
||||
|
||||
$t2 = 0.6 - $x2 ** 2 - $y2 ** 2 - $z2 ** 2;
|
||||
if($t2 < 0){
|
||||
$n2 = 0.0;
|
||||
}else{
|
||||
$t2 **= 2;
|
||||
$n2 = $t2 ** 2 * self::dot3D(self::$grad3[$gi2], $x2, $y2, $z2);
|
||||
if($t2 > 0){
|
||||
$gi2 = self::$grad3[$this->perm[$ii + $i2 + $this->perm[$jj + $j2 + $this->perm[$kk + $k2]]] % 12];
|
||||
$n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2 + $gi2[2] * $z2);
|
||||
}
|
||||
|
||||
$t3 = 0.6 - $x3 ** 2 - $y3 ** 2 - $z3 ** 2;
|
||||
if($t3 < 0){
|
||||
$n3 = 0.0;
|
||||
}else{
|
||||
$t3 **= 2;
|
||||
$n3 = $t3 ** 2 * self::dot3D(self::$grad3[$gi3], $x3, $y3, $z3);
|
||||
if($t3 > 0){
|
||||
$gi3 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1 + $this->perm[$kk + 1]]] % 12];
|
||||
$n += $t3 ** 4 * ($gi3[0] * $x3 + $gi3[1] * $y3 + $gi3[2] * $z3);
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the noise value.
|
||||
// The result is scaled to stay just inside [-1,1]
|
||||
return 32.0 * ($n0 + $n1 + $n2 + $n3);
|
||||
return 32.0 * $n;
|
||||
}
|
||||
|
||||
public function getNoise2D($x, $y){
|
||||
@ -234,13 +222,12 @@ class Simplex extends Perlin{
|
||||
|
||||
// Skew the input space to determine which simplex cell we're in
|
||||
$s = ($x + $y) * self::$F2; // Hairy factor for 2D
|
||||
$i = (int) floor($x + $s);
|
||||
$j = (int) floor($y + $s);
|
||||
$i = (int) ($x + $s);
|
||||
$j = (int) ($y + $s);
|
||||
$t = ($i + $j) * self::$G2;
|
||||
$X0 = $i - $t; // Unskew the cell origin back to (x,y) space
|
||||
$Y0 = $j - $t;
|
||||
$x0 = $x - $X0; // The x,y distances from the cell origin
|
||||
$y0 = $y - $Y0;
|
||||
// Unskew the cell origin back to (x,y) space
|
||||
$x0 = $x - ($i - $t); // The x,y distances from the cell origin
|
||||
$y0 = $y - ($j - $t);
|
||||
|
||||
// For the 2D case, the simplex shape is an equilateral triangle.
|
||||
|
||||
@ -267,26 +254,26 @@ class Simplex extends Perlin{
|
||||
// Work out the hashed gradient indices of the three simplex corners
|
||||
$ii = $i & 255;
|
||||
$jj = $j & 255;
|
||||
$gi0 = $this->perm[$ii + $this->perm[$jj]] % 12;
|
||||
$gi1 = $this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12;
|
||||
$gi2 = $this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12;
|
||||
|
||||
$n = 0;
|
||||
|
||||
// Calculate the contribution from the three corners
|
||||
$t = 0.5 - $x0 ** 2 - $y0 ** 2;
|
||||
if($t > 0){
|
||||
$n += $t ** 4 * self::dot2D(self::$grad3[$gi0], $x0, $y0); // (x,y) of grad3 used for 2D gradient
|
||||
$t0 = 0.5 - $x0 ** 2 - $y0 ** 2;
|
||||
if($t0 > 0){
|
||||
$gi0 = self::$grad3[$this->perm[$ii + $this->perm[$jj]] % 12];
|
||||
$n += $t0 ** 4 * ($gi0[0] * $x0 + $gi0[1] * $y0); // (x,y) of grad3 used for 2D gradient
|
||||
}
|
||||
|
||||
$t = 0.5 - $x1 ** 2 - $y1 ** 2;
|
||||
if($t > 0){
|
||||
$n += $t ** 4 * self::dot2D(self::$grad3[$gi1], $x1, $y1);
|
||||
$t1 = 0.5 - $x1 ** 2 - $y1 ** 2;
|
||||
if($t1 > 0){
|
||||
$gi1 = self::$grad3[$this->perm[$ii + $i1 + $this->perm[$jj + $j1]] % 12];
|
||||
$n += $t1 ** 4 * ($gi1[0] * $x1 + $gi1[1] * $y1);
|
||||
}
|
||||
|
||||
$t = 0.5 - $x2 ** 2 - $y2 ** 2;
|
||||
if($t > 0){
|
||||
$n += $t ** 4 * self::dot2D(self::$grad3[$gi2], $x2, $y2);
|
||||
$t2 = 0.5 - $x2 ** 2 - $y2 ** 2;
|
||||
if($t2 > 0){
|
||||
$gi2 = self::$grad3[$this->perm[$ii + 1 + $this->perm[$jj + 1]] % 12];
|
||||
$n += $t2 ** 4 * ($gi2[0] * $x2 + $gi2[1] * $y2);
|
||||
}
|
||||
|
||||
// Add contributions from each corner to get the noise value.
|
||||
|
289
src/pocketmine/level/generator/normal/Normal.php
Normal file
289
src/pocketmine/level/generator/normal/Normal.php
Normal file
@ -0,0 +1,289 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\CoalOre;
|
||||
use pocketmine\block\DiamondOre;
|
||||
use pocketmine\block\Dirt;
|
||||
use pocketmine\block\GoldOre;
|
||||
use pocketmine\block\Gravel;
|
||||
use pocketmine\block\IronOre;
|
||||
use pocketmine\block\LapisOre;
|
||||
use pocketmine\block\RedstoneOre;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
use pocketmine\level\generator\biome\BiomeSelector;
|
||||
use pocketmine\level\generator\GenerationChunkManager;
|
||||
use pocketmine\level\generator\GenerationManager;
|
||||
use pocketmine\level\generator\Generator;
|
||||
use pocketmine\level\generator\noise\Perlin;
|
||||
use pocketmine\level\generator\noise\Simplex;
|
||||
use pocketmine\level\generator\normal\biome\NormalBiome;
|
||||
use pocketmine\level\generator\object\OreType;
|
||||
use pocketmine\level\generator\populator\GroundCover;
|
||||
use pocketmine\level\generator\populator\Ore;
|
||||
use pocketmine\level\generator\populator\Populator;
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3 as Vector3;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Normal extends Generator{
|
||||
|
||||
/** @var Populator[] */
|
||||
private $populators = [];
|
||||
/** @var ChunkManager */
|
||||
private $level;
|
||||
/** @var Random */
|
||||
private $random;
|
||||
private $waterHeight = 62;
|
||||
private $bedrockDepth = 5;
|
||||
|
||||
/** @var Populator[] */
|
||||
private $generationPopulators = [];
|
||||
/** @var Simplex */
|
||||
private $noiseBase;
|
||||
|
||||
/** @var BiomeSelector */
|
||||
private $selector;
|
||||
|
||||
private static $GAUSSIAN_KERNEL = null;
|
||||
private static $SMOOTH_SIZE = 2;
|
||||
|
||||
public function __construct(array $options = []){
|
||||
if(self::$GAUSSIAN_KERNEL === null){
|
||||
self::generateKernel();
|
||||
}
|
||||
}
|
||||
|
||||
private static function generateKernel(){
|
||||
self::$GAUSSIAN_KERNEL = [];
|
||||
|
||||
$bellSize = 1 / self::$SMOOTH_SIZE;
|
||||
$bellHeight = 2 * self::$SMOOTH_SIZE;
|
||||
|
||||
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
|
||||
self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE] = [];
|
||||
|
||||
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
|
||||
$bx = $bellSize * $sx;
|
||||
$bz = $bellSize * $sz;
|
||||
self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE] = $bellHeight * exp(-($bx * $bx + $bz * $bz) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "normal";
|
||||
}
|
||||
|
||||
public function getSettings(){
|
||||
return [];
|
||||
}
|
||||
|
||||
public function pickBiome($x, $z){
|
||||
$hash = $x * 2345803 ^ $z * 9236449 ^ $this->level->getSeed();
|
||||
$hash *= $hash + 223;
|
||||
$xNoise = $hash >> 20 & 3;
|
||||
$zNoise = $hash >> 22 & 3;
|
||||
if ($xNoise == 3) {
|
||||
$xNoise = 1;
|
||||
}
|
||||
if($zNoise == 3) {
|
||||
$zNoise = 1;
|
||||
}
|
||||
|
||||
return $this->selector->pickBiome($x + $xNoise - 1, $z + $zNoise - 1);
|
||||
}
|
||||
|
||||
public function init(ChunkManager $level, Random $random){
|
||||
$this->level = $level;
|
||||
$this->random = $random;
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
$this->noiseBase = new Simplex($this->random, 16, 0.01, 0.5, 2);
|
||||
$this->random->setSeed($this->level->getSeed());
|
||||
$this->selector = new BiomeSelector($this->random, function($temperature, $rainfall){
|
||||
$rainfall *= $temperature;
|
||||
if($temperature < 0.10){
|
||||
return Biome::ICE_PLAINS;
|
||||
}elseif($rainfall < 0.20){
|
||||
if($temperature < 0.50){
|
||||
return Biome::ICE_PLAINS;
|
||||
}elseif($temperature < 0.95){
|
||||
return Biome::PLAINS;
|
||||
}else{
|
||||
return Biome::DESERT;
|
||||
}
|
||||
}elseif($rainfall > 0.5 and $temperature < 0.7){
|
||||
if($rainfall < 0.7){
|
||||
return Biome::OCEAN;
|
||||
}elseif($rainfall < 0.85){
|
||||
return Biome::RIVER;
|
||||
}else{
|
||||
return Biome::SWAMP;
|
||||
}
|
||||
}elseif($temperature < 0.50){
|
||||
return Biome::TAIGA;
|
||||
}elseif($temperature < 0.97){
|
||||
if($rainfall < 0.25){
|
||||
return Biome::MOUNTAINS;
|
||||
}elseif($rainfall < 0.35){
|
||||
return Biome::SMALL_MOUNTAINS;
|
||||
}else{
|
||||
return Biome::PLAINS;
|
||||
}
|
||||
}else{
|
||||
if($rainfall < 0.45){
|
||||
return Biome::PLAINS;
|
||||
}elseif($rainfall < 0.90){
|
||||
return Biome::FOREST;
|
||||
}else{
|
||||
return Biome::BIRCH_FOREST;
|
||||
}
|
||||
}
|
||||
}, Biome::getBiome(Biome::OCEAN));
|
||||
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::OCEAN));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::PLAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::DESERT));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::MOUNTAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::FOREST));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::TAIGA));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::SWAMP));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::RIVER));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::ICE_PLAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::SMALL_MOUNTAINS));
|
||||
$this->selector->addBiome(Biome::getBiome(Biome::BIRCH_FOREST));
|
||||
|
||||
$this->selector->recalculate();
|
||||
|
||||
$cover = new GroundCover();
|
||||
$this->generationPopulators[] = $cover;
|
||||
|
||||
$ores = new Ore();
|
||||
$ores->setOreTypes([
|
||||
new OreType(new CoalOre(), 20, 16, 0, 128),
|
||||
new OreType(New IronOre(), 20, 8, 0, 64),
|
||||
new OreType(new RedstoneOre(), 8, 7, 0, 16),
|
||||
new OreType(new LapisOre(), 1, 6, 0, 32),
|
||||
new OreType(new GoldOre(), 2, 8, 0, 32),
|
||||
new OreType(new DiamondOre(), 1, 7, 0, 16),
|
||||
new OreType(new Dirt(), 20, 32, 0, 128),
|
||||
new OreType(new Gravel(), 10, 16, 0, 128)
|
||||
]);
|
||||
$this->populators[] = $ores;
|
||||
}
|
||||
|
||||
public function generateChunk($chunkX, $chunkZ){
|
||||
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
|
||||
|
||||
$noise = Generator::getFastNoise3D($this->noiseBase, 16, 128, 16, 4, 8, 4, $chunkX * 16, 0, $chunkZ * 16);
|
||||
|
||||
$chunk = $this->level->getChunk($chunkX, $chunkZ);
|
||||
|
||||
$biomeCache = [];
|
||||
|
||||
for($x = 0; $x < 16; ++$x){
|
||||
for($z = 0; $z < 16; ++$z){
|
||||
$minSum = 0;
|
||||
$maxSum = 0;
|
||||
$weightSum = 0;
|
||||
|
||||
$biome = $this->pickBiome($chunkX * 16 + $x, $chunkZ * 16 + $z);
|
||||
$chunk->setBiomeId($x, $z, $biome->getId());
|
||||
$color = $biome->getColor();
|
||||
$chunk->setBiomeColor($x, $z, $color >> 16, ($color >> 8) & 0xff, $color & 0xff);
|
||||
|
||||
for($sx = -self::$SMOOTH_SIZE; $sx <= self::$SMOOTH_SIZE; ++$sx){
|
||||
for($sz = -self::$SMOOTH_SIZE; $sz <= self::$SMOOTH_SIZE; ++$sz){
|
||||
|
||||
$weight = self::$GAUSSIAN_KERNEL[$sx + self::$SMOOTH_SIZE][$sz + self::$SMOOTH_SIZE];
|
||||
|
||||
if($sx === 0 and $sz === 0){
|
||||
$adjacent = $biome;
|
||||
}else{
|
||||
$index = Level::chunkHash($chunkX * 16 + $x + $sx, $chunkZ * 16 + $z + $sz);
|
||||
if(isset($biomeCache[$index])){
|
||||
$adjacent = $biomeCache[$index];
|
||||
}else{
|
||||
$biomeCache[$index] = $adjacent = $this->pickBiome($chunkX * 16 + $x + $sx, $chunkZ * 16 + $z + $sz);
|
||||
}
|
||||
}
|
||||
|
||||
$minSum += $adjacent->getMinElevation() * $weight;
|
||||
$maxSum += $adjacent->getMaxElevation() * $weight;
|
||||
$weightSum += $weight;
|
||||
}
|
||||
}
|
||||
|
||||
$minSum /= $weightSum;
|
||||
$maxSum /= $weightSum;
|
||||
|
||||
$smoothHeight = ($maxSum - $minSum) / 2;
|
||||
|
||||
for($y = 0; $y < 128; ++$y){
|
||||
if($y === 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::BEDROCK);
|
||||
continue;
|
||||
}
|
||||
$noiseValue = $noise[$x][$z][$y] - 1 / $smoothHeight * ($y - $smoothHeight - $minSum);
|
||||
|
||||
if($noiseValue >= 0){
|
||||
$chunk->setBlockId($x, $y, $z, Block::STONE);
|
||||
}else{
|
||||
if($y <= $this->waterHeight){
|
||||
$chunk->setBlockId($x, $y, $z, Block::STILL_WATER);
|
||||
$lightValue = 15 - ($this->waterHeight - $y) * 2;
|
||||
if($lightValue > 0){
|
||||
$chunk->setBlockSkyLight($x, $y, $z, $lightValue);
|
||||
}
|
||||
}else{
|
||||
$chunk->setBlockSkyLight($x, $y, $z, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach($this->generationPopulators as $populator){
|
||||
$populator->populate($this->level, $chunkX, $chunkZ, $this->random);
|
||||
}
|
||||
}
|
||||
|
||||
public function populateChunk($chunkX, $chunkZ){
|
||||
$this->random->setSeed(0xdeadbeef ^ ($chunkX << 8) ^ $chunkZ ^ $this->level->getSeed());
|
||||
foreach($this->populators as $populator){
|
||||
$populator->populate($this->level, $chunkX, $chunkZ, $this->random);
|
||||
}
|
||||
|
||||
$chunk = $this->level->getChunk($chunkX, $chunkZ);
|
||||
$biome = Biome::getBiome($chunk->getBiomeId(7, 7));
|
||||
$biome->populateChunk($this->level, $chunkX, $chunkZ, $this->random);
|
||||
}
|
||||
|
||||
public function getSpawn(){
|
||||
return new Vector3(127.5, 128, 127.5);
|
||||
}
|
||||
|
||||
}
|
39
src/pocketmine/level/generator/normal/biome/DesertBiome.php
Normal file
39
src/pocketmine/level/generator/normal/biome/DesertBiome.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
|
||||
class DesertBiome extends SandyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
$this->setElevation(63, 74);
|
||||
|
||||
$this->temperature = 2;
|
||||
$this->rainfall = 0;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Desert";
|
||||
}
|
||||
}
|
67
src/pocketmine/level/generator/normal/biome/ForestBiome.php
Normal file
67
src/pocketmine/level/generator/normal/biome/ForestBiome.php
Normal file
@ -0,0 +1,67 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
|
||||
class ForestBiome extends GrassyBiome{
|
||||
|
||||
const TYPE_NORMAL = 0;
|
||||
const TYPE_BIRCH = 1;
|
||||
|
||||
public $type;
|
||||
|
||||
public function __construct($type = self::TYPE_NORMAL){
|
||||
parent::__construct();
|
||||
|
||||
$this->type = $type;
|
||||
|
||||
$trees = new Tree($type === self::TYPE_BIRCH ? Sapling::BIRCH : Sapling::OAK);
|
||||
$trees->setBaseAmount(5);
|
||||
$this->addPopulator($trees);
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(3);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(63, 81);
|
||||
|
||||
if($type === self::TYPE_BIRCH){
|
||||
$this->temperature = 0.5;
|
||||
$this->rainfall = 0.5;
|
||||
}else{
|
||||
$this->temperature = 0.7;
|
||||
$this->temperature = 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return $this->type === self::TYPE_BIRCH ? "Birch Forest" : "Forest";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x056621;
|
||||
}
|
||||
}
|
37
src/pocketmine/level/generator/normal/biome/GrassyBiome.php
Normal file
37
src/pocketmine/level/generator/normal/biome/GrassyBiome.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
|
||||
abstract class GrassyBiome extends NormalBiome{
|
||||
|
||||
public function __construct(){
|
||||
$this->setGroundCover([
|
||||
Block::get(Block::GRASS, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
|
||||
class IcePlainsBiome extends SnowyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(5);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(63, 74);
|
||||
|
||||
$this->temperature = 0.05;
|
||||
$this->rainfall = 0.8;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Ice Plains";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x163933;
|
||||
}
|
||||
}
|
@ -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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
|
||||
class MountainsBiome extends GrassyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$trees = new Tree();
|
||||
$trees->setBaseAmount(1);
|
||||
$this->addPopulator($trees);
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(1);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
//TODO: add emerald
|
||||
|
||||
$this->setElevation(63, 127);
|
||||
|
||||
$this->temperature = 0.4;
|
||||
$this->rainfall = 0.5;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Mountains";
|
||||
}
|
||||
}
|
30
src/pocketmine/level/generator/normal/biome/NormalBiome.php
Normal file
30
src/pocketmine/level/generator/normal/biome/NormalBiome.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\biome\Biome;
|
||||
|
||||
abstract class NormalBiome extends Biome{
|
||||
public function getColor(){
|
||||
return 0xffb360; //Detect wrong biomes
|
||||
}
|
||||
}
|
49
src/pocketmine/level/generator/normal/biome/OceanBiome.php
Normal file
49
src/pocketmine/level/generator/normal/biome/OceanBiome.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
|
||||
class OceanBiome extends GrassyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(5);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(46, 58);
|
||||
|
||||
$this->temperature = 0.5;
|
||||
$this->rainfall = 0.5;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Ocean";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x8da360;
|
||||
}
|
||||
}
|
49
src/pocketmine/level/generator/normal/biome/PlainBiome.php
Normal file
49
src/pocketmine/level/generator/normal/biome/PlainBiome.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
|
||||
class PlainBiome extends GrassyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(12);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(63, 74);
|
||||
|
||||
$this->temperature = 0.8;
|
||||
$this->rainfall = 0.4;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Plains";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x8db360;
|
||||
}
|
||||
}
|
49
src/pocketmine/level/generator/normal/biome/RiverBiome.php
Normal file
49
src/pocketmine/level/generator/normal/biome/RiverBiome.php
Normal file
@ -0,0 +1,49 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
|
||||
class RiverBiome extends GrassyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(5);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(58, 62);
|
||||
|
||||
$this->temperature = 0.5;
|
||||
$this->rainfall = 0.7;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "River";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x8dc360;
|
||||
}
|
||||
}
|
37
src/pocketmine/level/generator/normal/biome/SandyBiome.php
Normal file
37
src/pocketmine/level/generator/normal/biome/SandyBiome.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
|
||||
abstract class SandyBiome extends NormalBiome{
|
||||
|
||||
public function __construct(){
|
||||
$this->setGroundCover([
|
||||
Block::get(Block::SAND, 0),
|
||||
Block::get(Block::SAND, 0),
|
||||
Block::get(Block::SANDSTONE, 0),
|
||||
Block::get(Block::SANDSTONE, 0),
|
||||
Block::get(Block::SANDSTONE, 0),
|
||||
]);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
|
||||
class SmallMountainsBiome extends MountainsBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$this->setElevation(63, 97);
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Small Mountains";
|
||||
}
|
||||
}
|
37
src/pocketmine/level/generator/normal/biome/SnowyBiome.php
Normal file
37
src/pocketmine/level/generator/normal/biome/SnowyBiome.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
|
||||
abstract class SnowyBiome extends NormalBiome{
|
||||
|
||||
public function __construct(){
|
||||
$this->setGroundCover([
|
||||
Block::get(Block::SNOW_LAYER, 0),
|
||||
Block::get(Block::GRASS, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
Block::get(Block::DIRT, 0),
|
||||
]);
|
||||
}
|
||||
}
|
42
src/pocketmine/level/generator/normal/biome/SwampBiome.php
Normal file
42
src/pocketmine/level/generator/normal/biome/SwampBiome.php
Normal file
@ -0,0 +1,42 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
class SwampBiome extends GrassyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$this->setElevation(62, 63);
|
||||
|
||||
$this->temperature = 0.8;
|
||||
$this->rainfall = 0.9;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Swamp";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x07f9b2;
|
||||
}
|
||||
}
|
55
src/pocketmine/level/generator/normal/biome/TaigaBiome.php
Normal file
55
src/pocketmine/level/generator/normal/biome/TaigaBiome.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\normal\biome;
|
||||
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\level\generator\populator\TallGrass;
|
||||
use pocketmine\level\generator\populator\Tree;
|
||||
|
||||
class TaigaBiome extends SnowyBiome{
|
||||
|
||||
public function __construct(){
|
||||
parent::__construct();
|
||||
|
||||
$trees = new Tree(Sapling::SPRUCE);
|
||||
$trees->setBaseAmount(10);
|
||||
$this->addPopulator($trees);
|
||||
|
||||
$tallGrass = new TallGrass();
|
||||
$tallGrass->setBaseAmount(1);
|
||||
|
||||
$this->addPopulator($tallGrass);
|
||||
|
||||
$this->setElevation(63, 81);
|
||||
|
||||
$this->temperature = 0.05;
|
||||
$this->rainfall = 0.8;
|
||||
}
|
||||
|
||||
public function getName(){
|
||||
return "Taiga";
|
||||
}
|
||||
|
||||
public function getColor(){
|
||||
return 0x0b6659;
|
||||
}
|
||||
}
|
47
src/pocketmine/level/generator/object/BirchTree.php
Normal file
47
src/pocketmine/level/generator/object/BirchTree.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class BirchTree extends Tree{
|
||||
|
||||
protected $superBirch = false;
|
||||
|
||||
public function __construct($superBirch = false){
|
||||
$this->trunkBlock = Block::LOG;
|
||||
$this->leafBlock = Block::LEAVES;
|
||||
$this->type = Wood::BIRCH;
|
||||
$this->superBirch = (bool) $superBirch;
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->treeHeight = $random->nextBoundedInt(3) + 5;
|
||||
if($this->superBirch){
|
||||
$this->treeHeight += 5;
|
||||
}
|
||||
parent::placeObject($level, $x, $y, $z, $random);
|
||||
}
|
||||
}
|
35
src/pocketmine/level/generator/object/JungleTree.php
Normal file
35
src/pocketmine/level/generator/object/JungleTree.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Wood;
|
||||
|
||||
class JungleTree extends Tree{
|
||||
|
||||
public function __construct(){
|
||||
$this->trunkBlock = Block::LOG;
|
||||
$this->leafBlock = Block::LEAVES;
|
||||
$this->type = Wood::JUNGLE;
|
||||
$this->treeHeight = 8;
|
||||
}
|
||||
}
|
41
src/pocketmine/level/generator/object/OakTree.php
Normal file
41
src/pocketmine/level/generator/object/OakTree.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class OakTree extends Tree{
|
||||
|
||||
public function __construct(){
|
||||
$this->trunkBlock = Block::LOG;
|
||||
$this->leafBlock = Block::LEAVES;
|
||||
$this->type = Wood::OAK;
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->treeHeight = $random->nextBoundedInt(3) + 4;
|
||||
parent::placeObject($level, $x, $y, $z, $random);
|
||||
}
|
||||
}
|
@ -50,8 +50,8 @@ class Ore{
|
||||
$x2 = $x + 8 - $offset->x;
|
||||
$z1 = $z + 8 + $offset->y;
|
||||
$z2 = $z + 8 - $offset->y;
|
||||
$y1 = $y + $this->random->nextRange(0, 3) + 2;
|
||||
$y2 = $y + $this->random->nextRange(0, 3) + 2;
|
||||
$y1 = $y + $this->random->nextBoundedInt(3) + 2;
|
||||
$y2 = $y + $this->random->nextBoundedInt(3) + 2;
|
||||
for($count = 0; $count <= $clusterSize; ++$count){
|
||||
$seedX = $x1 + ($x2 - $x1) * $count / $clusterSize;
|
||||
$seedY = $y1 + ($y2 - $y1) * $count / $clusterSize;
|
||||
@ -81,7 +81,9 @@ class Ore{
|
||||
|
||||
if(($sizeX + $sizeY + $sizeZ) < 1 and $level->getBlockIdAt($x, $y, $z) === 1){
|
||||
$level->setBlockIdAt($x, $y, $z, $this->type->material->getId());
|
||||
$level->setBlockDataAt($x, $y, $z, $this->type->material->getDamage());
|
||||
if($this->type->material->getDamage() !== 0){
|
||||
$level->setBlockDataAt($x, $y, $z, $this->type->material->getDamage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,96 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class PineTree extends Tree{
|
||||
var $type = 1;
|
||||
private $totalHeight = 8;
|
||||
private $leavesSizeY = -1;
|
||||
private $leavesAbsoluteMaxRadius = -1;
|
||||
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->findRandomLeavesSize($random);
|
||||
$checkRadius = 0;
|
||||
for($yy = 0; $yy < $this->totalHeight; ++$yy){
|
||||
if($yy === $this->leavesSizeY){
|
||||
$checkRadius = $this->leavesAbsoluteMaxRadius;
|
||||
}
|
||||
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
|
||||
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findRandomLeavesSize(Random $random){
|
||||
$this->totalHeight += $random->nextRange(-1, 2);
|
||||
$this->leavesSizeY = 1 + $random->nextRange(0, 2);
|
||||
$this->leavesAbsoluteMaxRadius = 2 + $random->nextRange(0, 1);
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
if($this->leavesSizeY === -1 or $this->leavesAbsoluteMaxRadius === -1){
|
||||
$this->findRandomLeavesSize($random);
|
||||
}
|
||||
$level->setBlockIdAt($x, $y - 1, $z, Block::DIRT);
|
||||
$leavesRadius = 0;
|
||||
$leavesMaxRadius = 1;
|
||||
$leavesBottomY = $this->totalHeight - $this->leavesSizeY;
|
||||
$firstMaxedRadius = false;
|
||||
for($leavesY = 0; $leavesY <= $leavesBottomY; ++$leavesY){
|
||||
$yy = $this->totalHeight - $leavesY;
|
||||
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
|
||||
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
|
||||
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
if($leavesRadius >= $leavesMaxRadius){
|
||||
$leavesRadius = $firstMaxedRadius ? 1 : 0;
|
||||
$firstMaxedRadius = true;
|
||||
if(++$leavesMaxRadius > $this->leavesAbsoluteMaxRadius){
|
||||
$leavesMaxRadius = $this->leavesAbsoluteMaxRadius;
|
||||
}
|
||||
}else{
|
||||
++$leavesRadius;
|
||||
}
|
||||
}
|
||||
$trunkHeightReducer = $random->nextRange(0, 3);
|
||||
for($yy = 0; $yy < ($this->totalHeight - $trunkHeightReducer); ++$yy){
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
<?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/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class SmallTree extends Tree{
|
||||
public $type = 0;
|
||||
private $trunkHeight = 5;
|
||||
private static $leavesHeight = 4; // All trees appear to be 4 tall
|
||||
private static $leafRadii = [1, 1.41, 2.83, 2.24];
|
||||
|
||||
private $addLeavesVines = false;
|
||||
private $addLogVines = false;
|
||||
private $addCocoaPlants = false;
|
||||
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$radiusToCheck = 0;
|
||||
for($yy = 0; $yy < $this->trunkHeight + 3; ++$yy){
|
||||
if($yy == 1 or $yy === $this->trunkHeight){
|
||||
++$radiusToCheck;
|
||||
}
|
||||
for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){
|
||||
for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
// The base dirt block
|
||||
$level->setBlockIdAt($x, $y, $z, Block::DIRT);
|
||||
|
||||
// Adjust the tree trunk's height randomly
|
||||
// plot [-14:11] int( x / 8 ) + 5
|
||||
// - min=4 (all leaves are 4 tall, some trunk must show)
|
||||
// - max=6 (top leaves are within ground-level whacking range
|
||||
// on all small trees)
|
||||
$heightPre = $random->nextRange(-14, 11);
|
||||
$this->trunkHeight = intval($heightPre / 8) + 5;
|
||||
|
||||
// Adjust the starting leaf density using the trunk height as a
|
||||
// starting position (tall trees with skimpy leaves don't look
|
||||
// too good)
|
||||
$leafPre = $random->nextRange($this->trunkHeight, 10) / 20; // (TODO: seed may apply)
|
||||
|
||||
// Now build the tree (from the top down)
|
||||
$leaflevel = 0;
|
||||
for($yy = ($this->trunkHeight + 1); $yy >= 0; --$yy){
|
||||
if($leaflevel < self::$leavesHeight){
|
||||
// The size is a slight variation on the trunkheight
|
||||
$radius = self::$leafRadii[$leaflevel] + $leafPre;
|
||||
$bRadius = 3;
|
||||
for($xx = -$bRadius; $xx <= $bRadius; ++$xx){
|
||||
for($zz = -$bRadius; $zz <= $bRadius; ++$zz){
|
||||
if(sqrt($xx ** 2 + $zz ** 2) <= $radius){
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
$leaflevel++;
|
||||
}
|
||||
|
||||
// Place the trunk last
|
||||
if($leaflevel > 1){
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -22,64 +22,56 @@
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Wood;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class SpruceTree extends Tree{
|
||||
var $type = 1;
|
||||
private $totalHeight = 8;
|
||||
private $leavesBottomY = -1;
|
||||
private $leavesMaxRadius = -1;
|
||||
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$this->findRandomLeavesSize($random);
|
||||
$checkRadius = 0;
|
||||
for($yy = 0; $yy < $this->totalHeight + 2; ++$yy){
|
||||
if($yy === $this->leavesBottomY){
|
||||
$checkRadius = $this->leavesMaxRadius;
|
||||
}
|
||||
for($xx = -$checkRadius; $xx < ($checkRadius + 1); ++$xx){
|
||||
for($zz = -$checkRadius; $zz < ($checkRadius + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findRandomLeavesSize(Random $random){
|
||||
$this->totalHeight += $random->nextRange(-1, 2);
|
||||
$this->leavesBottomY = (int) ($this->totalHeight - $random->nextRange(1, 2) - 3);
|
||||
$this->leavesMaxRadius = 1 + $random->nextRange(0, 1);
|
||||
public function __construct(){
|
||||
$this->trunkBlock = Block::LOG;
|
||||
$this->leafBlock = Block::LEAVES;
|
||||
$this->type = Wood::SPRUCE;
|
||||
$this->treeHeight = 10;
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
if($this->leavesBottomY === -1 or $this->leavesMaxRadius === -1){
|
||||
$this->findRandomLeavesSize($random);
|
||||
}
|
||||
$level->setBlockIdAt($x, $y - 1, $z, Block::DIRT);
|
||||
$leavesRadius = 0;
|
||||
for($yy = $this->totalHeight; $yy >= $this->leavesBottomY; --$yy){
|
||||
for($xx = -$leavesRadius; $xx <= $leavesRadius; ++$xx){
|
||||
for($zz = -$leavesRadius; $zz <= $leavesRadius; ++$zz){
|
||||
if(abs($xx) != $leavesRadius or abs($zz) != $leavesRadius or $leavesRadius <= 0){
|
||||
$level->setBlockIdAt($x + $xx, $y + $yy, $z + $zz, Block::LEAVES);
|
||||
$level->setBlockDataAt($x + $xx, $y + $yy, $z + $zz, $this->type);
|
||||
$this->treeHeight = $random->nextBoundedInt(4) + 6;
|
||||
|
||||
$topSize = $this->treeHeight - (1 + $random->nextBoundedInt(2));
|
||||
$lRadius = 2 + $random->nextBoundedInt(2);
|
||||
|
||||
$this->placeTrunk($level, $x, $y, $z, $random, $this->treeHeight - $random->nextBoundedInt(3));
|
||||
|
||||
$radius = $random->nextBoundedInt(2);
|
||||
$maxR = 1;
|
||||
$minR = 0;
|
||||
|
||||
for($yy = 0; $yy <= $topSize; ++$yy){
|
||||
$yyy = $y + $this->treeHeight - $yy;
|
||||
|
||||
for($xx = $x - $radius; $xx <= $x + $radius; ++$xx){
|
||||
$xOff = $xx - $x;
|
||||
for($zz = $z - $radius; $zz <= $z + $radius; ++$zz){
|
||||
$zOff = $zz - $z;
|
||||
if(abs($xOff) === $radius and abs($zOff) === $radius and $radius > 0){
|
||||
continue;
|
||||
}
|
||||
|
||||
$level->setBlockIdAt($xx, $yyy, $zz, $this->leafBlock);
|
||||
$level->setBlockDataAt($xx, $yyy, $zz, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
if($radius >= $maxR){
|
||||
$radius = $minR;
|
||||
$minR = 1;
|
||||
if(++$maxR > $lRadius){
|
||||
$maxR = $lRadius;
|
||||
}
|
||||
}else{
|
||||
++$radius;
|
||||
}
|
||||
if($leavesRadius > 0 and $yy === ($y + $this->leavesBottomY + 1)){
|
||||
--$leavesRadius;
|
||||
}elseif($leavesRadius < $this->leavesMaxRadius){
|
||||
++$leavesRadius;
|
||||
}
|
||||
}
|
||||
for($yy = 0; $yy < ($this->totalHeight - 1); ++$yy){
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, Block::TRUNK);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,43 +21,49 @@
|
||||
|
||||
namespace pocketmine\level\generator\object;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\Sapling;
|
||||
use pocketmine\level\ChunkManager;
|
||||
use pocketmine\utils\Random;
|
||||
|
||||
class Tree{
|
||||
abstract class Tree{
|
||||
public $overridable = [
|
||||
0 => true,
|
||||
2 => true,
|
||||
3 => true,
|
||||
Block::AIR => true,
|
||||
6 => true,
|
||||
17 => true,
|
||||
18 => true,
|
||||
Block::SNOW_LAYER => true,
|
||||
Block::LOG2 => true,
|
||||
Block::LEAVES2 => true
|
||||
];
|
||||
|
||||
public $type = 0;
|
||||
public $trunkBlock = Block::LOG;
|
||||
public $leafBlock = Block::LEAVES;
|
||||
public $treeHeight = 7;
|
||||
|
||||
public static function growTree(ChunkManager $level, $x, $y, $z, Random $random, $type = 0){
|
||||
switch($type & 0x03){
|
||||
switch($type){
|
||||
case Sapling::SPRUCE:
|
||||
if($random->nextRange(0, 1) === 1){
|
||||
$tree = new SpruceTree();
|
||||
}else{
|
||||
$tree = new PineTree();
|
||||
}
|
||||
$tree = new SpruceTree();
|
||||
break;
|
||||
case Sapling::BIRCH:
|
||||
$tree = new SmallTree();
|
||||
$tree->type = Sapling::BIRCH;
|
||||
if($random->nextBoundedInt(39) === 0){
|
||||
$tree = new BirchTree(true);
|
||||
}else{
|
||||
$tree = new BirchTree();
|
||||
}
|
||||
break;
|
||||
case Sapling::JUNGLE:
|
||||
$tree = new SmallTree();
|
||||
$tree->type = Sapling::JUNGLE;
|
||||
$tree = new JungleTree();
|
||||
break;
|
||||
case Sapling::OAK:
|
||||
default:
|
||||
$tree = new OakTree();
|
||||
/*if($random->nextRange(0, 9) === 0){
|
||||
$tree = new BigTree();
|
||||
}else{*/
|
||||
$tree = new SmallTree();
|
||||
|
||||
//}
|
||||
break;
|
||||
}
|
||||
@ -65,4 +71,57 @@ class Tree{
|
||||
$tree->placeObject($level, $x, $y, $z, $random);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
$radiusToCheck = 0;
|
||||
for($yy = 0; $yy < $this->treeHeight + 3; ++$yy){
|
||||
if($yy == 1 or $yy === $this->treeHeight){
|
||||
++$radiusToCheck;
|
||||
}
|
||||
for($xx = -$radiusToCheck; $xx < ($radiusToCheck + 1); ++$xx){
|
||||
for($zz = -$radiusToCheck; $zz < ($radiusToCheck + 1); ++$zz){
|
||||
if(!isset($this->overridable[$level->getBlockIdAt($x + $xx, $y + $yy, $z + $zz)])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random){
|
||||
|
||||
$this->placeTrunk($level, $x, $y, $z, $random, $this->treeHeight - 1);
|
||||
|
||||
for($yy = $y - 3 + $this->treeHeight; $yy <= $y + $this->treeHeight; ++$yy){
|
||||
$yOff = $yy - ($y + $this->treeHeight);
|
||||
$mid = (int) (1 - $yOff / 2);
|
||||
for($xx = $x - $mid; $xx <= $x + $mid; ++$xx){
|
||||
$xOff = $xx - $x;
|
||||
for($zz = $z - $mid; $zz <= $z + $mid; ++$zz){
|
||||
$zOff = $zz - $z;
|
||||
if(abs($xOff) === $mid and abs($zOff) === $mid and ($yOff === 0 or $random->nextBoundedInt(2) === 0)){
|
||||
continue;
|
||||
}
|
||||
$level->setBlockIdAt($xx, $yy, $zz, $this->leafBlock);
|
||||
$level->setBlockDataAt($xx, $yy, $zz, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function placeTrunk(ChunkManager $level, $x, $y, $z, Random $random, $trunkHeight){
|
||||
// The base dirt block
|
||||
$level->setBlockIdAt($x, $y - 1, $z, Block::DIRT);
|
||||
|
||||
for($yy = 0; $yy <= $trunkHeight; ++$yy){
|
||||
$blockId = $level->getBlockIdAt($x, $y + $yy, $z);
|
||||
if(isset($this->overridable[$blockId])){
|
||||
$level->setBlockIdAt($x, $y + $yy, $z, $this->trunkBlock);
|
||||
$level->setBlockDataAt($x, $y + $yy, $z, $this->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user