From 5ea6052a8ad7fa0136e2115bc627a5da5458a304 Mon Sep 17 00:00:00 2001 From: Shoghi Cervantes Pueyo Date: Sat, 18 May 2013 02:05:56 +0200 Subject: [PATCH] New Level Generator API & Support for MC Superflat Presets --- src/API/LevelAPI.php | 16 +- src/utils/MersenneTwister.php | 524 ++++++++++++++++++ src/utils/Random.php | 61 ++ src/world/Level.php | 14 +- src/world/generator/Generator.class.php | 357 ------------ src/world/generator/LevelGenerator.php | 38 ++ .../generator/SuperflatGenerator.class.php | 171 +++--- src/world/generator/WorldGenerator.php | 68 +++ 8 files changed, 809 insertions(+), 440 deletions(-) create mode 100644 src/utils/MersenneTwister.php create mode 100644 src/utils/Random.php delete mode 100644 src/world/generator/Generator.class.php create mode 100644 src/world/generator/LevelGenerator.php create mode 100644 src/world/generator/WorldGenerator.php diff --git a/src/API/LevelAPI.php b/src/API/LevelAPI.php index 9301747d1..88d2617a5 100644 --- a/src/API/LevelAPI.php +++ b/src/API/LevelAPI.php @@ -86,18 +86,18 @@ class LevelAPI{ } public function generateLevel($name, $seed = false){ - $path = DATA_PATH."worlds/".$name."/"; - $generator = "SuperflatGenerator"; + $options = array(); + if($this->server->api->getProperty("generator-settings") !== false and trim($this->server->api->getProperty("generator-settings")) != ""){ + $options["preset"] = $this->server->api->getProperty("generator-settings"); + } if($this->server->api->getProperty("generator") !== false and class_exists($this->server->api->getProperty("generator"))){ $generator = $this->server->api->getProperty("generator"); + $generator = new $generator($options); + }else{ + $generator = new SuperflatGenerator($options); } - $gen = new WorldGenerator($generator, ($seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)):(int) $seed)); - if($this->server->api->getProperty("generator-settings") !== false and trim($this->server->api->getProperty("generator-settings")) != ""){ - $gen->set("preset", $this->server->api->getProperty("generator-settings")); - } - $gen->init(); + $gen = new WorldGenerator($generator, $name, $seed === false ? Utils::readInt(Utils::getRandomBytes(4, false)):(int) $seed); $gen->generate(); - $gen->save($path, $name); } public function loadLevel($name){ diff --git a/src/utils/MersenneTwister.php b/src/utils/MersenneTwister.php new file mode 100644 index 000000000..a4253152b --- /dev/null +++ b/src/utils/MersenneTwister.php @@ -0,0 +1,524 @@ +bits32 = PHP_INT_MAX == 2147483647; + + if(func_num_args() == 1) { + $this->init_with_integer(func_get_arg(0)); + } + } + + function init_with_integer($integer_seed) { + $integer_seed = force_32_bit_int($integer_seed); + + $mt = &$this->mt; + $mti = &$this->mti; + + $mt = array_fill(0, N, 0); + + $mt[0] = $integer_seed; + + for($mti = 1; $mti < N; $mti++) { + $mt[$mti] = add_2(mul(1812433253, + ($mt[$mti - 1] ^ (($mt[$mti - 1] >> 30) & 3))), $mti); + /* + mt[mti] = + (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); + */ + } + } + + function init_with_array(array $integer_array) { + $integer_array = + array_map(__NAMESPACE__ . "\\force_32_bit_int", $integer_array); + + $mt = &$this->mt; + $mti = &$this->mti; + + $key_length = count($integer_array); + + $this->init_with_integer(19650218); + $i=1; $j=0; + $k = (N>$key_length ? N : $key_length); + for (; $k; $k--) { + $mt[$i] = add_3($mt[$i] ^ + mul_by_1664525($mt[$i-1] ^ (($mt[$i-1] >> 30) & 3)), + $integer_array[$j], $j); + /* + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL)) + + init_key[j] + j; + */ + $i++; $j++; + if ($i>=N) { $mt[0] = $mt[N-1]; $i=1; } + if ($j>=$key_length) $j=0; + } + for ($k=N-1; $k; $k--) { + $mt[$i] = sub($mt[$i] ^ + mul($mt[$i-1] ^ (($mt[$i-1] >> 30) & 3), 1566083941), $i); + /* + mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL)) + - i; + */ + $i++; + if ($i>=N) { $mt[0] = $mt[N-1]; $i=1; } + } + + $mt[0] = (1 << 31); /* MSB is 1; assuring non-zero initial array */ + } + + function init_with_string($string) { + $remainder = strlen($string) % 4; + + if($remainder > 0) + $string .= str_repeat("\0", 4 - $remainder); + + $integer_array = array_values(unpack("N*", $string)); + + $this->init_with_array($integer_array); + } + + function init_with_files(array $filenames, $max_ints = -1) { + $limit_applies = $max_ints !== -1; + + if($limit_applies) + $limit = $max_ints * 4; # 32 bits is 4 characters + + $data = ""; + + foreach ($filenames as $filename) { + $contents = + file_get_contents($filename, FALSE, NULL, 0, + $limit_applies? $limit - strlen($data): -1); + + if($contents === FALSE) { + throw new Exception("problem reading from $filename"); + } else { + $data .= $contents; + + if($limit_applies && strlen($data) == $limit) { + break; + } + } + } + + $this->init_with_string($data); + } + + function init_with_file($filename, $max_ints = -1) { + $this->init_with_files(array($filename), $max_ints); + } + + function int32() { + static $mag01 = array(0, MATRIX_A); + + $mt = &$this->mt; + $mti = &$this->mti; + + if ($mti >= N) { /* generate N words all at once */ + for ($kk=0;$kk> 1) & MASK31) ^ $mag01[$y & 1]; + } + for (;$kk> 1) & MASK31) ^ $mag01[$y & 1]; + } + $y = ($mt[N-1]&UPPER_MASK)|($mt[0]&LOWER_MASK); + $mt[N-1] = $mt[M-1] ^ (($y >> 1) & MASK31) ^ $mag01[$y & 1]; + + $mti = 0; + } + + $y = $mt[$mti++]; + + /* Tempering */ + $y ^= ($y >> 11) & MASK21; + $y ^= ($y << 7) & ((0x9d2c << 16) | 0x5680); + $y ^= ($y << 15) & (0xefc6 << 16); + $y ^= ($y >> 18) & MASK14; + + return $y; + } + + function int31() { + return $this->int32() & MASK31; + } + + /* generates a random number on [0,1]-real-interval */ + function real_closed() { + return + signed2unsigned($this->int32()) * (1.0 / 4294967295.0); + } + + /* generates a random number on [0,1)-real-interval */ + function real_halfopen() { + return + signed2unsigned($this->int32()) * (1.0 / 4294967296.0); + } + + /* generates a random number on (0,1)-real-interval */ + function real_open() { + return (signed2unsigned($this->int32()) + .5) * + (1.0 / 4294967296.0); + } + + /* generates a random number on [0,1) with 53-bit resolution */ + function real_halfopen2() { + return + ((($this->int32() & MASK27) * 67108864.0) + + ($this->int32() & MASK26)) * + (1.0 / 9007199254740992.0); + } + + function rangeint($lower_bound, $upper_bound) { + $lower_bound = intval($lower_bound); + $upper_bound = intval($upper_bound); + + if($this->bits32) { + $pow_2_32 = pow(2, 32); + + $size_of_range = $upper_bound - $lower_bound + 1; + + $remainder = fmod($pow_2_32, $size_of_range); + + if($remainder == 0) { + return $lower_bound + + ($this->int32() & unsigned2signed($size_of_range - 1)); + } else { + $start_of_partial_range = $pow_2_32 - $remainder; + $start_as_int = unsigned2signed($start_of_partial_range); + do { + $rand = $this->int32(); + } while($rand >= $start_as_int && $rand < 0); + + $result = $lower_bound + + fmod(signed2unsigned($rand), $size_of_range); + + return intval($result); + } + } else { + if($lower_bound == -PHP_INT_MAX - 1 && $upper_bound == PHP_INT_MAX) { + return ($this->int32() << 32) & $this->int32(); + } else { + $pow_2_32 = 1 << 32; + + $size_of_range = $upper_bound - $lower_bound + 1; + + if($size_of_range > $pow_2_32) { + $size_of_range >>= 32; + $shift = 32; + $low_bits = $this->int32(); + } else { + $shift = 0; + $low_bits = 0; + } + + $remainder = $pow_2_32 % $size_of_range; + + if($remainder == 0) { + $high_bits = $this->int32() & ($size_of_range - 1); + } else { + $start_of_partial_range = $pow_2_32 - $remainder; + do { + $rand = $this->int32(); + } while($rand >= $start_of_partial_range); + + $high_bits = $rand % $size_of_range; + } + + return $lower_bound + (($high_bits << $shift) | $low_bits); + } + } + } + + /* + in each of the next 3 functions, we loop until we have a number that + meets the function's post-condition. this may be more work than + is really necessary, but i am concerned about rounding errors. + + why no rangereal_closed? because, due to the aforementioned + rounding errors, i am unable to guarantee that $upper_bound + would be a possible return value of such a function. + */ + + function rangereal_open($lower_bound, $upper_bound) { + do { + $rand = $lower_bound + + $this->real_open() * ($upper_bound - $lower_bound); + } while($rand <= $lower_bound || $rand >= $upper_bound); + + return $rand; + } + + function rangereal_halfopen($lower_bound, $upper_bound) { + do { + $rand = $lower_bound + + $this->real_halfopen() * ($upper_bound - $lower_bound); + } while($rand >= $upper_bound); + /* + $rand cannot go any lower than $lower_bound, because + $this->real_halfopen() cannot go any lower than 0 + */ + + return $rand; + } + + function rangereal_halfopen2($lower_bound, $upper_bound) { + do { + $rand = $lower_bound + + $this->real_halfopen2() * ($upper_bound - $lower_bound); + } while($rand >= $upper_bound); + + return $rand; + } + + function test() { + ob_start(); + $this->exercise(); + $output = ob_get_clean(); + + if(md5($output) !== "cb33e6acc162cbe20f7fcac26adddd02") { + print "Test failed.\n"; + } + } + + private function exercise() { + /* + we keep the names "genrand_int32" and "genrand_real2" because + we want the output of this function to be identical to that + produced by mtTest.c. (The file mtTest.c is part of the archive + mt19937ar.sep.tgz, mentioned above.) + */ + + $this->init_with_array(array(0x123, 0x234, 0x345, 0x456)); + printf("1000 outputs of genrand_int32()\n"); + for ($i=0; $i<1000; $i++) { + printf("%10lu ", $this->int32()); + if ($i%5==4) printf("\n"); + } + printf("\n1000 outputs of genrand_real2()\n"); + for ($i=0; $i<1000; $i++) { + printf("%10.8f ", $this->real_halfopen()); + if ($i%5==4) printf("\n"); + } + } +} + +function signed2unsigned($signed_integer) { + ## assert(is_integer($signed_integer)); + ## assert(($signed_integer & ~MASK32) === 0); + + return $signed_integer >= 0? $signed_integer: + TWO_TO_THE_32 + $signed_integer; +} + +function unsigned2signed($unsigned_integer) { + ## assert($unsigned_integer >= 0); + ## assert($unsigned_integer < pow(2, 32)); + ## assert(floor($unsigned_integer) === floatval($unsigned_integer)); + + return intval($unsigned_integer < TWO_TO_THE_31? $unsigned_integer: + $unsigned_integer - TWO_TO_THE_32); +} + +function force_32_bit_int($x) { + /* + it would be un-PHP-like to require is_integer($x), + so we have to handle cases like this: + + $x === pow(2, 31) + $x === strval(pow(2, 31)) + + we are also opting to do something sensible (rather than dying) + if the seed is outside the range of a 32-bit unsigned integer. + */ + + if(is_integer($x)) { + /* + we mask in case we are on a 64-bit machine and at least one + bit is set between position 32 and position 63. + */ + return $x & MASK32; + } else { + $x = floatval($x); + + $x = $x < 0? ceil($x): floor($x); + + $x = fmod($x, TWO_TO_THE_32); + + if($x < 0) + $x += TWO_TO_THE_32; + + return unsigned2signed($x); + } +} + +/* + takes 2 integers, treats them as unsigned 32-bit integers, + and adds them. + + it works by splitting each integer into + 2 "half-integers", then adding the high and low half-integers + separately. + + a slight complication is that the sum of the low half-integers + may not fit into 16 bits; any "overspill" is added to the sum + of the high half-integers. +*/ +function add_2($n1, $n2) { + $x = ($n1 & 0xffff) + ($n2 & 0xffff); + + return + (((($n1 >> 16) & 0xffff) + + (($n2 >> 16) & 0xffff) + + ($x >> 16)) << 16) | ($x & 0xffff); +} + +# takes 2 integers, treats them as unsigned 32-bit integers, +# and adds them. +# +# for how it works, see the comment for add_2. +# +function add_3($n1, $n2, $n3) { + $x = ($n1 & 0xffff) + ($n2 & 0xffff) + ($n3 & 0xffff); + + return + (((($n1 >> 16) & 0xffff) + + (($n2 >> 16) & 0xffff) + + (($n3 >> 16) & 0xffff) + + ($x >> 16)) << 16) | ($x & 0xffff); +} + +# takes 2 integers, treats them as unsigned 32-bit integers, +# and subtracts the second from the first. +# +# the explanation of why this works is too long to be +# included here, so it has been moved into the file why-sub-works.txt. +# +function sub($a, $b) { + return (($a & MASK31) - ($b & MASK31)) ^ + (($a ^ $b) & 0x80000000); +} + +function mul($a, $b) { + /* + a and b, considered as unsigned integers, can be expressed as follows: + + a = 2**16 * a1 + a2, + + b = 2**16 * b1 + b2, + + where + + 0 <= a2 < 2**16, + 0 <= b2 < 2**16. + + given those 2 equations, what this function essentially does is to + use the following identity: + + a * b = 2**32 * a1 * b1 + 2**16 * a1 * b2 + 2**16 * b1 * a2 + a2 * b2 + + note that the first term, i.e. 2**32 * a1 * b1, is unnecessary here, + so we don't compute it. + + we could make the following code clearer by using intermediate + variables, but that would probably hurt performance. + */ + + return + unsigned2signed( + fmod( + TWO_TO_THE_16 * + /* + the next line of code calculates a1 * b2, + the line after that calculates b1 * a2, + and the line after that calculates a2 * b2. + */ + ((($a >> 16) & 0xffff) * ($b & 0xffff) + + (($b >> 16) & 0xffff) * ($a & 0xffff)) + + ($a & 0xffff) * ($b & 0xffff), + + TWO_TO_THE_32)); +} + +/* + mul_by_1664525($x) should be more efficient than mul(1664525, $x). +*/ +function mul_by_1664525($n) { + return unsigned2signed(fmod(1664525 * ($n >= 0? + $n: (TWO_TO_THE_32 + $n)), TWO_TO_THE_32)); +} + +function do_bc_op($bc_op, array $numbers) { + $modulus = pow(2, 32); + + $result = + call_user_func_array($bc_op, + array_map(__NAMESPACE__ . "\\signed2unsigned", $numbers)); + + $result = bcmod($result, $modulus); + + return unsigned2signed(bccomp($result, 0) < 0? + bcadd($result, $modulus): $result); +} + +function get_random_32bit_int() { + return rand(0, 0xffff) | (rand(0, 0xffff) << 16); +} diff --git a/src/utils/Random.php b/src/utils/Random.php new file mode 100644 index 000000000..deb83a204 --- /dev/null +++ b/src/utils/Random.php @@ -0,0 +1,61 @@ +random = new mersenne_twister\twister(0); + $this->setSeed($seed); + } + + public function setSeed($seed = false){ + $this->random->init_with_integer($seed !== false ? (int) $seed:Utils::readInt(Utils::getRandomBytes(4, false))); + } + + public function nextInt(){ + return $this->random->int32(); + } + + public function nextFloat(){ + return $this->random->real_closed(); + } + + public function nextBytes($byteCount){ + $bytes = ""; + for($i = 0; $i < $byteCount; ++$i){ + $bytes .= chr($this->random->rangeint(0, 0xFF)); + } + return $bytes; + } + + public function nextBoolean(){ + return $this->random->rangeint(0, 1) === 1; + } + +} \ No newline at end of file diff --git a/src/world/Level.php b/src/world/Level.php index b62cb5295..a053c8c60 100644 --- a/src/world/Level.php +++ b/src/world/Level.php @@ -85,8 +85,8 @@ class Level{ unset($this->level); } - public function save(){ - if($this->server->saveEnabled === false){ + public function save($force = false){ + if($this->server->saveEnabled === false and $force === false){ return; } $entities = array(); @@ -169,6 +169,10 @@ class Level{ return BlockAPI::get($b[0], $b[1], new Position($pos->x, $pos->y, $pos->z, $this)); } + public function setBlockRaw(Vector3 $pos, Block $block){ + return $this->level->setBlock($pos->x, $pos->y, $pos->z, $block->getID(), $block->getMetadata()); + } + public function setBlock(Vector3 $pos, Block $block, $update = true, $tiles = false){ if((($pos instanceof Position) and $pos->level !== $this) or $pos->x < 0 or $pos->y < 0 or $pos->z < 0){ return false; @@ -195,12 +199,12 @@ class Level{ return false; } - public function getMiniChunk($X, $Z){ + public function getMiniChunk($X, $Y, $Z){ return $this->level->getMiniChunk($X, $Z); } - public function setMiniChunk($X, $Z, $data){ - return $this->level->setMiniChunk($X, $Z, $data); + public function setMiniChunk($X, $Y, $Z, $data){ + return $this->level->setMiniChunk($X, $Y, $Z, $data); } public function loadChunk($X, $Z){ diff --git a/src/world/generator/Generator.class.php b/src/world/generator/Generator.class.php deleted file mode 100644 index 5ebc9d8b8..000000000 --- a/src/world/generator/Generator.class.php +++ /dev/null @@ -1,357 +0,0 @@ -seed = (int) $seed; - $this->raw = b""; - $this->genName = $genName; - $this->gen = new $genName($this->seed); - } - - public function getSpawn(){ - return $this->gen->getSpawn(); - } - - public function set($name, $value){ - $this->gen->set($name, $value); - } - - public function init(){ - $this->raw = "\x15\x01\x00\x00\x15\x16\x00\x00\x15\x2b\x00\x00\x15\x40\x00\x00". //Location Header - "\x15\x55\x00\x00\x15\x6a\x00\x00\x15\x7f\x00\x00\x15\x94\x00\x00". - "\x15\xa9\x00\x00\x15\xbe\x00\x00\x15\xd3\x00\x00\x15\xe8\x00\x00". - "\x15\xfd\x00\x00\x15\x12\x01\x00\x15\x27\x01\x00\x15\x3c\x01\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x51\x01\x00\x15\x66\x01\x00\x15\x7b\x01\x00\x15\x90\x01\x00". - "\x15\xa5\x01\x00\x15\xba\x01\x00\x15\xcf\x01\x00\x15\xe4\x01\x00". - "\x15\xf9\x01\x00\x15\x0e\x02\x00\x15\x23\x02\x00\x15\x38\x02\x00". - "\x15\x4d\x02\x00\x15\x62\x02\x00\x15\x77\x02\x00\x15\x8c\x02\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xa1\x02\x00\x15\xb6\x02\x00\x15\xcb\x02\x00\x15\xe0\x02\x00". - "\x15\xf5\x02\x00\x15\x0a\x03\x00\x15\x1f\x03\x00\x15\x34\x03\x00". - "\x15\x49\x03\x00\x15\x5e\x03\x00\x15\x73\x03\x00\x15\x88\x03\x00". - "\x15\x9d\x03\x00\x15\xb2\x03\x00\x15\xc7\x03\x00\x15\xdc\x03\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xf1\x03\x00\x15\x06\x04\x00\x15\x1b\x04\x00\x15\x30\x04\x00". - "\x15\x45\x04\x00\x15\x5a\x04\x00\x15\x6f\x04\x00\x15\x84\x04\x00". - "\x15\x99\x04\x00\x15\xae\x04\x00\x15\xc3\x04\x00\x15\xd8\x04\x00". - "\x15\xed\x04\x00\x15\x02\x05\x00\x15\x17\x05\x00\x15\x2c\x05\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x41\x05\x00\x15\x56\x05\x00\x15\x6b\x05\x00\x15\x80\x05\x00". - "\x15\x95\x05\x00\x15\xaa\x05\x00\x15\xbf\x05\x00\x15\xd4\x05\x00". - "\x15\xe9\x05\x00\x15\xfe\x05\x00\x15\x13\x06\x00\x15\x28\x06\x00". - "\x15\x3d\x06\x00\x15\x52\x06\x00\x15\x67\x06\x00\x15\x7c\x06\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x91\x06\x00\x15\xa6\x06\x00\x15\xbb\x06\x00\x15\xd0\x06\x00". - "\x15\xe5\x06\x00\x15\xfa\x06\x00\x15\x0f\x07\x00\x15\x24\x07\x00". - "\x15\x39\x07\x00\x15\x4e\x07\x00\x15\x63\x07\x00\x15\x78\x07\x00". - "\x15\x8d\x07\x00\x15\xa2\x07\x00\x15\xb7\x07\x00\x15\xcc\x07\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xe1\x07\x00\x15\xf6\x07\x00\x15\x0b\x08\x00\x15\x20\x08\x00". - "\x15\x35\x08\x00\x15\x4a\x08\x00\x15\x5f\x08\x00\x15\x74\x08\x00". - "\x15\x89\x08\x00\x15\x9e\x08\x00\x15\xb3\x08\x00\x15\xc8\x08\x00". - "\x15\xdd\x08\x00\x15\xf2\x08\x00\x15\x07\x09\x00\x15\x1c\x09\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x31\x09\x00\x15\x46\x09\x00\x15\x5b\x09\x00\x15\x70\x09\x00". - "\x15\x85\x09\x00\x15\x9a\x09\x00\x15\xaf\x09\x00\x15\xc4\x09\x00". - "\x15\xd9\x09\x00\x15\xee\x09\x00\x15\x03\x0a\x00\x15\x18\x0a\x00". - "\x15\x2d\x0a\x00\x15\x42\x0a\x00\x15\x57\x0a\x00\x15\x6c\x0a\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x81\x0a\x00\x15\x96\x0a\x00\x15\xab\x0a\x00\x15\xc0\x0a\x00". - "\x15\xd5\x0a\x00\x15\xea\x0a\x00\x15\xff\x0a\x00\x15\x14\x0b\x00". - "\x15\x29\x0b\x00\x15\x3e\x0b\x00\x15\x53\x0b\x00\x15\x68\x0b\x00". - "\x15\x7d\x0b\x00\x15\x92\x0b\x00\x15\xa7\x0b\x00\x15\xbc\x0b\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xd1\x0b\x00\x15\xe6\x0b\x00\x15\xfb\x0b\x00\x15\x10\x0c\x00". - "\x15\x25\x0c\x00\x15\x3a\x0c\x00\x15\x4f\x0c\x00\x15\x64\x0c\x00". - "\x15\x79\x0c\x00\x15\x8e\x0c\x00\x15\xa3\x0c\x00\x15\xb8\x0c\x00". - "\x15\xcd\x0c\x00\x15\xe2\x0c\x00\x15\xf7\x0c\x00\x15\x0c\x0d\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x21\x0d\x00\x15\x36\x0d\x00\x15\x4b\x0d\x00\x15\x60\x0d\x00". - "\x15\x75\x0d\x00\x15\x8a\x0d\x00\x15\x9f\x0d\x00\x15\xb4\x0d\x00". - "\x15\xc9\x0d\x00\x15\xde\x0d\x00\x15\xf3\x0d\x00\x15\x08\x0e\x00". - "\x15\x1d\x0e\x00\x15\x32\x0e\x00\x15\x47\x0e\x00\x15\x5c\x0e\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x71\x0e\x00\x15\x86\x0e\x00\x15\x9b\x0e\x00\x15\xb0\x0e\x00". - "\x15\xc5\x0e\x00\x15\xda\x0e\x00\x15\xef\x0e\x00\x15\x04\x0f\x00". - "\x15\x19\x0f\x00\x15\x2e\x0f\x00\x15\x43\x0f\x00\x15\x58\x0f\x00". - "\x15\x6d\x0f\x00\x15\x82\x0f\x00\x15\x97\x0f\x00\x15\xac\x0f\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xc1\x0f\x00\x15\xd6\x0f\x00\x15\xeb\x0f\x00\x15\x00\x10\x00". - "\x15\x15\x10\x00\x15\x2a\x10\x00\x15\x3f\x10\x00\x15\x54\x10\x00". - "\x15\x69\x10\x00\x15\x7e\x10\x00\x15\x93\x10\x00\x15\xa8\x10\x00". - "\x15\xbd\x10\x00\x15\xd2\x10\x00\x15\xe7\x10\x00\x15\xfc\x10\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x11\x11\x00\x15\x26\x11\x00\x15\x3b\x11\x00\x15\x50\x11\x00". - "\x15\x65\x11\x00\x15\x7a\x11\x00\x15\x8f\x11\x00\x15\xa4\x11\x00". - "\x15\xb9\x11\x00\x15\xce\x11\x00\x15\xe3\x11\x00\x15\xf8\x11\x00". - "\x15\x0d\x12\x00\x15\x22\x12\x00\x15\x37\x12\x00\x15\x4c\x12\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\x61\x12\x00\x15\x76\x12\x00\x15\x8b\x12\x00\x15\xa0\x12\x00". - "\x15\xb5\x12\x00\x15\xca\x12\x00\x15\xdf\x12\x00\x15\xf4\x12\x00". - "\x15\x09\x13\x00\x15\x1e\x13\x00\x15\x33\x13\x00\x15\x48\x13\x00". - "\x15\x5d\x13\x00\x15\x72\x13\x00\x15\x87\x13\x00\x15\x9c\x13\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x15\xb1\x13\x00\x15\xc6\x13\x00\x15\xdb\x13\x00\x15\xf0\x13\x00". - "\x15\x05\x14\x00\x15\x1a\x14\x00\x15\x2f\x14\x00\x15\x44\x14\x00". - "\x15\x59\x14\x00\x15\x6e\x14\x00\x15\x83\x14\x00\x15\x98\x14\x00". - "\x15\xad\x14\x00\x15\xc2\x14\x00\x15\xd7\x14\x00\x15\xec\x14\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00". - "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; - $this->gen->init(); - } - - public function generate(){ - for($Z = 0; $Z < 16; ++$Z){ - for($X = 0; $X < 16; ++$X){ - $chunk = str_pad($this->getChunk($X, $Z), 86012, "\x00", STR_PAD_RIGHT); - $this->raw .= Utils::writeLInt(strlen($chunk)) . $chunk; - } - console("[NOTICE] Generating level ".ceil(($Z + 1)/0.16)."%"); - } - return true; - } - - private function getChunk($X, $Z){ - $chunk = b""; - $columns = array(); - $X *= 16; - $Z *= 16; - for($x = 0; $x < 16; ++$x){ - for($z = 0; $z < 16; ++$z){ - $columns[$x * 16 + $z] = $this->gen->getColumn($X + $x, $Z + $z); - } - } - for($i = 0; $i < 4; ++$i){ - for($x = 0; $x < 16; ++$x){ - for($z = 0; $z < 16; ++$z){ - $chunk .= $columns[$x * 16 + $z][$i]; - } - } - } - unset($columns); - return $chunk; - } - - public function save($dir, $name){ - @mkdir($dir, 0777, true); - file_put_contents($dir."chunks.dat", $this->raw); - $s = $this->getSpawn(); - $array = array(); - file_put_contents($dir."entities.dat", serialize($array)); - file_put_contents($dir."tileEntities.dat", serialize($array)); - $level = array( - "LevelName" => $name, - "Time" => 0, - "Gamemode" => CREATIVE, - "RandomSeed" => $this->seed, - "Generator" => $this->genName, - "SpawnX" => $s[0], - "SpawnY" => $s[1], - "SpawnZ" => $s[2], - ); - file_put_contents($dir."level.dat", serialize($level)); - } - -} \ No newline at end of file diff --git a/src/world/generator/LevelGenerator.php b/src/world/generator/LevelGenerator.php new file mode 100644 index 000000000..0d5cc0fcc --- /dev/null +++ b/src/world/generator/LevelGenerator.php @@ -0,0 +1,38 @@ +config = array( - "preset" => "7;70x1;3x3;2", - "spawn-surface" => 24, - "spawn-radius" => 10, - "torches" => 0, - "seed" => (int) $seed, - ); - $this->parsePreset(); - } - - public function set($name, $value){ - $this->config[$name] = $value; - if($name === "preset"){ - $this->parsePreset(); +class SuperflatGenerator implements LevelGenerator{ + private $config, $structure, $chunks, $options, $floorLevel; + + public function __construct(array $options = array()){ + $this->preset = "2;7,2x3,2;1;spawn(radius=10 block=24)"; + $this->options = $options; + if(isset($options["preset"])){ + $this->parsePreset($options["preset"]); + }else{ + $this->parsePreset($this->preset); } } - - private function parsePreset(){ - $this->structure = array( - 0 => "", - 1 => "", - 2 => str_repeat("\x00", 64), - 3 => str_repeat("\x00", 64), - ); - $preset = explode(";", trim($this->config["preset"])); - foreach($preset as $i => $data){ - $num = 1; - if(preg_match('#([a-zA-Z\-_]*)\((.*)\)#', $data, $matches) > 0){ //Property - $this->config[$matches[1]] = $matches[2]; - continue; - }elseif(preg_match('#([0-9]*)x([0-9:]*)#', $data, $matches) > 0){ - $num = (int) $matches[1]; - $d = explode(":", $matches[2]); - }else{ - $d = explode(":", $data); - } - $block = (int) array_shift($d); - $meta = (int) @array_shift($d); - for($j = 0; $j < $num; ++$j){ - $this->structure[0] .= chr($block & 0xFF); - $this->structure[1] .= dechex($meta & 0x0F); + + public function parsePreset($preset){ + $this->preset = $preset; + $preset = explode(";", $preset); + $version = (int) $preset[0]; + $blocks = @$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); + $y = 0; + $this->structure = array(); + $this->chunks = array(); + foreach($matches[3] as $i => $b){ + $b = BlockAPI::fromString($b); + $cnt = $matches[2][$i] === "" ? 1:intval($matches[2][$i]); + for($cY = $y, $y += $cnt; $cY < $y; ++$cY){ + $this->structure[$cY] = $b; } } - $this->structure[1] = pack("h*", str_pad($this->structure[1], (strlen($this->structure[1])&0xFE) + 2, "0", STR_PAD_RIGHT)); //invert nibbles - $this->structure[0] = substr($this->structure[0], 0, 128); - $this->structure[1] = substr($this->structure[1], 0, 64); - $this->structure[2] = substr($this->structure[2], 0, 64); - $this->structure[3] = substr($this->structure[3], 0, 64); - } - - public function init(){ - $this->spawn = array(128, strlen($this->structure[0]), 128); - } - - public function getSpawn(){ - return $this->spawn; - } - - public function getColumn($x, $z){ - $x = (int) $x; - $z = (int) $z; - $column = $this->structure; - if(floor(sqrt(pow($x - $this->spawn[0], 2) + pow($z - $this->spawn[2], 2))) <= $this->config["spawn-radius"]){ - $column[0]{strlen($column[0])-1} = chr($this->config["spawn-surface"]); + + $this->floorLevel = $y; + + for(;$y < 0xFF; ++$y){ + $this->structure[$y] = new AirBlock(); } - if(($x % 8) === 0 and ($z % 8) === 0 and $this->config["torches"] == "1"){ - $column[0] .= chr(50); + + + for($Y = 0; $Y < 8; ++$Y){ + $this->chunks[$Y] = ""; + $startY = $Y << 4; + $endY = $startY + 16; + for($Z = 0; $Z < 16; ++$Z){ + for($X = 0; $X < 16; ++$X){ + $blocks = ""; + $metas = ""; + for($y = $startY; $y < $endY; ++$y){ + $blocks .= chr($this->structure[$y]->getID()); + $metas .= substr(dechex($this->structure[$y]->getMetadata()), -1); + } + $this->chunks[$Y] .= $blocks.Utils::hexToStr($metas)."\x00\x00\x00\x00\x00\x00\x00\x00"; + } + } + } + + preg_match_all('#(([0-9a-z_]{1,})\(?([0-9a-z_ =:]{0,})\)?),?#', $options, $matches); + foreach($matches[2] as $i => $option){ + $params = true; + if($matches[3][$i] !== ""){ + $params = array(); + $p = explode(" ", $matches[3][$i]); + foreach($p as $k){ + $k = explode("=", $k); + if(isset($k[1])){ + $params[$k[0]] = $k[1]; + } + } + } + $this->options[$option] = $params; } - $column[0] .= str_repeat(chr(0), 128 - strlen($column[0])); - $column[1] .= str_repeat(chr(0), 64 - strlen($column[1])); - $column[2] .= str_repeat(chr(0), 64 - strlen($column[2])); - $column[3] .= str_repeat(chr(0), 64 - strlen($column[3])); - return $column; } + + public function generateChunk(Level $level, $chunkX, $chunkY, $chunkZ, Random $random){ + $level->setMiniChunk($chunkX, $chunkZ, $chunkY, $this->chunks[$chunkY]); + } + + public function populateChunk(Level $level, $chunkX, $chunkY, $chunkZ, Random $random){ + } + + public function populateLevel(Level $level, Random $random){ + if(isset($this->options["spawn"])){ + $spawn = array(10, new SandstoneBlock()); + if(isset($this->options["spawn"]["radius"])){ + $spawn[0] = intval($this->options["spawn"]["radius"]); + } + if(isset($this->options["spawn"]["block"])){ + $spawn[1] = BlockAPI::fromString($this->options["spawn"]["block"])->getBlock(); + if(!($spawn[1] instanceof Block)){ + $spawn[1] = new SandstoneBlock(); + } + } + + $start = 128 - $spawn[0]; + $end = 128 + $spawn[0]; + for($x = $start; $x <= $end; ++$x){ + for($z = $start; $z <= $end; ++$z){ + if(floor(sqrt(pow($x - 128, 2) + pow($z - 128, 2))) <= $spawn[0]){ + $level->setBlock(new Vector3($x, $this->floorLevel - 1, $z), $spawn[1]); + } + } + } + } + } + + public function getSpawn(Random $random){ + return new Vector3(128, $this->floorLevel, 128); + } } \ No newline at end of file diff --git a/src/world/generator/WorldGenerator.php b/src/world/generator/WorldGenerator.php new file mode 100644 index 000000000..a021e2de2 --- /dev/null +++ b/src/world/generator/WorldGenerator.php @@ -0,0 +1,68 @@ +seed = $seed !== false ? (int) $seed:Utils::readInt(Utils::getRandomBytes(4, false)); + $this->random = new Random($this->seed); + $this->width = (int) $width; + $this->height = (int) $height; + $this->path = DATA_PATH."worlds/".$name."/"; + $this->generator = $generator; + $level = new PMFLevel($this->path."level.pmf", array( + "name" => $name, + "seed" => $this->seed, + "time" => 0, + "spawnX" => 128, + "spawnY" => 128, + "spawnZ" => 128, + "extra" => "", + "width" => $this->width, + "height" => $this->height + )); + $entities = new Config($this->path."entities.yml", CONFIG_YAML); + $tileEntities = new Config($this->path."tileEntities.yml", CONFIG_YAML); + $this->level = new Level($level, $entities, $tileEntities, $name); + } + + public function generate(){ + for($Z = 0; $Z < $this->width; ++$Z){ + for($X = 0; $X < $this->width; ++$X){ + for($Y = 0; $Y < $this->height; ++$Y){ + $this->generator->generateChunk($this->level, $X, $Y, $Z, $this->random); + $this->generator->populateChunk($this->level, $X, $Y, $Z, $this->random); + } + } + console("[NOTICE] Generating level ".ceil((($Z + 1)/$this->width) * 100)."%"); + } + $this->generator->populateLevel($this->level, $this->random); + $this->level->setSpawn($this->generator->getSpawn($this->random)); + $this->level->save(true); + } + +} \ No newline at end of file