Merge branch 'mcpe-0.11'

This commit is contained in:
Shoghi Cervantes 2015-04-09 18:18:10 +02:00
commit 86184a230e
201 changed files with 7164 additions and 3333 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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],

View File

@ -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{

View File

@ -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;

View File

@ -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);

View File

@ -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){

View 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 [];
}
}
}

View File

@ -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(){

View File

@ -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){

View File

@ -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"));

View File

@ -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);
}

View 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;
}
}

View File

@ -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]);

View 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;
}
}

View File

@ -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;
}

View File

@ -23,5 +23,9 @@ namespace pocketmine\entity;
interface Ageable{
const DATA_AGEABLE_FLAGS = 14;
const DATA_FLAG_BABY = 0;
public function isBaby();
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View 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);
}
}
}

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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){

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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){

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View 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))
];
}
}

View File

@ -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);
}
}

View 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);
}
}

View File

@ -24,4 +24,9 @@ namespace pocketmine\entity;
class Wolf extends Animal implements Tameable{
const NETWORK_ID = 14;
public function getName(){
return "Wolf";
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
/**

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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());
}

View File

@ -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,
];

View File

@ -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(){

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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;

View 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";
}
}
}

View 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";
}
}
}

View File

@ -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];

View File

@ -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();
}

View File

@ -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();
}
}

View 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;
}
}

View File

@ -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);
}

View File

@ -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){

View File

@ -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();
}
}

View File

@ -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());

View File

@ -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);

View File

@ -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();

View File

@ -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(){

View File

@ -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;

View File

@ -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
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View 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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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));
}
}

View 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);
}
}
}

View 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();
}

View 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;
}
}

View File

@ -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){

View File

@ -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){

View File

@ -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.

View 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);
}
}

View 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";
}
}

View 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;
}
}

View 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),
]);
}
}

View 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 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;
}
}

View File

@ -0,0 +1,52 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
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";
}
}

View 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
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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),
]);
}
}

View File

@ -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";
}
}

View 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),
]);
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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);
}
}

View File

@ -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());
}
}
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}
}

View File

@ -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);
}
}

View File

@ -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