diff --git a/build/android/Makefile b/build/android/Makefile index 394b3a5e3..43ce24712 100644 --- a/build/android/Makefile +++ b/build/android/Makefile @@ -311,7 +311,7 @@ $(OPENSSL_LIB): $(OPENSSL_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-openssl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \ + --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ CC=${CROSS_PREFIX}gcc ./Configure android-${TARGET_ARCH} no-idea no-seed -no-sha0 -DL_ENDIAN;\ CC=${CROSS_PREFIX}gcc ANDROID_DEV=/tmp/ndk-${TARGET_HOST} make build_libs; \ @@ -359,7 +359,7 @@ $(LEVELDB_LIB): $(LEVELDB_TIMESTAMP) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-leveldb; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \ + --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ export CXX=${CROSS_PREFIX}g++; \ @@ -518,7 +518,7 @@ $(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB) export TOOLCHAIN=/tmp/ndk-${TARGET_HOST}-curl; \ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \ - --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \ + --install-dir=$${TOOLCHAIN}; \ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \ export CC=${CROSS_PREFIX}gcc; \ export CXX=${CROSS_PREFIX}g++; \ diff --git a/cmake/Modules/FindGettextLib.cmake b/cmake/Modules/FindGettextLib.cmake index c6f731e04..5c992f0fa 100644 --- a/cmake/Modules/FindGettextLib.cmake +++ b/cmake/Modules/FindGettextLib.cmake @@ -74,6 +74,7 @@ IF(GETTEXT_FOUND) SET(GETTEXT_MO_DEST_PATH ${LOCALEDIR}//LC_MESSAGES) FILE(GLOB GETTEXT_AVAILABLE_LOCALES RELATIVE ${GETTEXT_PO_PATH} "${GETTEXT_PO_PATH}/*") LIST(REMOVE_ITEM GETTEXT_AVAILABLE_LOCALES minetest.pot) + LIST(REMOVE_ITEM GETTEXT_AVAILABLE_LOCALES timestamp) MACRO(SET_MO_PATHS _buildvar _destvar _locale) STRING(REPLACE "" ${_locale} ${_buildvar} ${GETTEXT_MO_BUILD_PATH}) STRING(REPLACE "" ${_locale} ${_destvar} ${GETTEXT_MO_DEST_PATH}) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 81a35976b..0b12652f1 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -2336,7 +2336,6 @@ This is basically a reference to a C++ `ServerActiveObject` * `right_click(clicker)`; `clicker` is another `ObjectRef` * `get_hp()`: returns number of hitpoints (2 * number of hearts) * `set_hp(hp)`: set number of hitpoints (2 * number of hearts) -* `apply_damage(damage)`: set amount of damage to object. If damage < 0, heal the target * `get_inventory()`: returns an `InvRef` * `get_wield_list()`: returns the name of the inventory list the wielded item is in * `get_wield_index()`: returns the index of the wielded item @@ -2515,7 +2514,8 @@ an itemstring, a table or `nil`. Returns taken `ItemStack`. ### `PseudoRandom` -A pseudorandom number generator. +A 16-bit pseudorandom number generator. +Uses a well-known LCG algorithm introduced by K&R. It can be created via `PseudoRandom(seed)`. @@ -2525,6 +2525,19 @@ It can be created via `PseudoRandom(seed)`. * `((max - min) == 32767) or ((max-min) <= 6553))` must be true due to the simple implementation making bad distribution otherwise. +### `PcgRandom` +A 32-bit pseudorandom number generator. +Uses PCG32, an algorithm of the permuted congruential generator family, offering very strong randomness. + +It can be created via `PcgRandom(seed)` or `PcgRandom(seed, sequence)`. + +#### Methods +* `next()`: return next integer random number [`-2147483648`...`2147483647`] +* `next(min, max)`: return next integer random number [`min`...`max`] +* `rand_normal_dist(min, max, num_trials=6)`: return normally distributed random number [`min`...`max`] + * This is only a rough approximation of a normal distribution with mean=(max-min)/2 and variance=1 + * Increasing num_trials improves accuracy of the approximation + ### `PerlinNoise` A perlin noise generator. It can be created via `PerlinNoise(seed, octaves, persistence, scale)` diff --git a/minetest.conf.example b/minetest.conf.example index 2c6f5e861..fd43d2797 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -197,7 +197,7 @@ # which PNG optimizers usually discard, sometimes resulting in a dark or # light edge to transparent textures. Apply this filter to clean that up # at texture load time. -#texture_clean_transparent = true +#texture_clean_transparent = false # When using bilinear/trilinear/anisotropic filters, low-resolution textures # can be blurred, so automatically upscale them with nearest-neighbor # interpolation to preserve crisp pixels. This sets the minimum texture size @@ -205,7 +205,7 @@ # memory. Powers of 2 are recommended. Setting this higher than 1 may not # have a visible effect unless bilinear/trilinear/anisotropic filtering is # enabled. -#texture_min_size = 16 +#texture_min_size = 64 # Set to true to pre-generate all item visuals #preload_item_visuals = false # Set to true to enable shaders. Disable them if video_driver = direct3d9/8. diff --git a/src/client/tile.cpp b/src/client/tile.cpp index 7b326c152..1cf9f1506 100644 --- a/src/client/tile.cpp +++ b/src/client/tile.cpp @@ -192,13 +192,18 @@ video::IImage *textureMinSizeUpscale(video::IVideoDriver *driver, video::IImage if(orig == NULL) return orig; s32 scaleto = g_settings->getS32("texture_min_size"); - if (scaleto > 0) { + if (scaleto > 1) { + const core::dimension2d dim = orig->getDimension(); + + // Don't upscale 1px images. They don't benefit from it anyway + // (wouldn't have been blurred) and MIGHT be sun/moon tonemaps. + if ((dim.Width <= 1) || (dim.Height <= 1)) + return orig; /* Calculate scaling needed to make the shortest texture dimension * equal to the target minimum. If e.g. this is a vertical frames * animation, the short dimension will be the real size. */ - const core::dimension2d dim = orig->getDimension(); u32 xscale = scaleto / dim.Width; u32 yscale = scaleto / dim.Height; u32 scale = (xscale > yscale) ? xscale : yscale; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index ff2d148aa..4ad0418bb 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -149,8 +149,8 @@ void set_default_settings(Settings *settings) settings->setDefault("anisotropic_filter", "false"); settings->setDefault("bilinear_filter", "false"); settings->setDefault("trilinear_filter", "false"); - settings->setDefault("texture_clean_transparent", "true"); - settings->setDefault("texture_min_size", "16"); + settings->setDefault("texture_clean_transparent", "false"); + settings->setDefault("texture_min_size", "64"); settings->setDefault("preload_item_visuals", "false"); settings->setDefault("enable_bumpmapping", "false"); settings->setDefault("enable_parallax_occlusion", "false"); diff --git a/src/mapgen.cpp b/src/mapgen.cpp index fd4fe5bb0..851f018ee 100644 --- a/src/mapgen.cpp +++ b/src/mapgen.cpp @@ -515,14 +515,10 @@ void MapgenParams::load(const Settings &settings) std::string seed_str; const char *seed_name = (&settings == g_settings) ? "fixed_map_seed" : "seed"; - if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) { + if (settings.getNoEx(seed_name, seed_str) && !seed_str.empty()) seed = read_seed(seed_str.c_str()); - } else { - seed = ((u64)(myrand() & 0xFFFF) << 0) | - ((u64)(myrand() & 0xFFFF) << 16) | - ((u64)(myrand() & 0xFFFF) << 32) | - ((u64)(myrand() & 0xFFFF) << 48); - } + else + myrand_bytes(&seed, sizeof(seed)); settings.getNoEx("mg_name", mg_name); settings.getS16NoEx("water_level", water_level); diff --git a/src/mapgen.h b/src/mapgen.h index 8cac58599..5b5ed19a2 100644 --- a/src/mapgen.h +++ b/src/mapgen.h @@ -204,6 +204,8 @@ public: virtual GenElement *getByName(const std::string &name); + INodeDefManager *getNodeDef() { return m_ndef; } + protected: INodeDefManager *m_ndef; std::vector m_elements; diff --git a/src/mapgen_v5.cpp b/src/mapgen_v5.cpp index 9086acc37..240ae500a 100644 --- a/src/mapgen_v5.cpp +++ b/src/mapgen_v5.cpp @@ -377,9 +377,9 @@ int MapgenV5::generateBaseTerrain() stone_surface_max_y = y; } } - index2d = index2d - ystride; + index2d -= ystride; } - index2d = index2d + ystride; + index2d += ystride; } return stone_surface_max_y; @@ -391,10 +391,6 @@ bool MapgenV5::generateBiomes(float *heat_map, float *humidity_map) if (node_max.Y < water_level) return false; - MapNode n_air(CONTENT_AIR); - MapNode n_stone(c_stone); - MapNode n_water(c_water_source); - v3s16 em = vm->m_area.getExtent(); u32 index = 0; bool desert_stone = false; @@ -496,9 +492,9 @@ void MapgenV5::generateCaves(int max_stone_y) if (d1*d2 > 0.125) vm->m_data[i] = MapNode(CONTENT_AIR); } - index2d = index2d - ystride; + index2d -= ystride; } - index2d = index2d + ystride; + index2d += ystride; } if (node_max.Y > LARGE_CAVE_DEPTH) @@ -528,27 +524,25 @@ void MapgenV5::dustTopNodes() if (biome->c_dust == CONTENT_IGNORE) continue; - s16 y_full_max = full_node_max.Y; - u32 vi_full_max = vm->m_area.index(x, y_full_max, z); - content_t c_full_max = vm->m_data[vi_full_max].getContent(); + u32 vi = vm->m_area.index(x, full_node_max.Y, z); + content_t c_full_max = vm->m_data[vi].getContent(); s16 y_start; if (c_full_max == CONTENT_AIR) { - y_start = y_full_max - 1; + y_start = full_node_max.Y - 1; } else if (c_full_max == CONTENT_IGNORE) { - s16 y_max = node_max.Y; - u32 vi_max = vm->m_area.index(x, y_max, z); - content_t c_max = vm->m_data[vi_max].getContent(); + vi = vm->m_area.index(x, node_max.Y + 1, z); + content_t c_max = vm->m_data[vi].getContent(); if (c_max == CONTENT_AIR) - y_start = y_max - 1; + y_start = node_max.Y; else continue; } else { continue; } - u32 vi = vm->m_area.index(x, y_start, z); + vi = vm->m_area.index(x, y_start, z); for (s16 y = y_start; y >= node_min.Y - 1; y--) { if (vm->m_data[vi].getContent() != CONTENT_AIR) break; diff --git a/src/mapgen_v6.cpp b/src/mapgen_v6.cpp index 2ecf42a0f..8885c71e5 100644 --- a/src/mapgen_v6.cpp +++ b/src/mapgen_v6.cpp @@ -877,9 +877,10 @@ void MapgenV6::placeTreesAndJungleGrass() for (u32 i = 0; i < grass_count; i++) { s16 x = grassrandom.range(p2d_min.X, p2d_max.X); s16 z = grassrandom.range(p2d_min.Y, p2d_max.Y); - - s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////optimize this! - if (y < water_level || y < node_min.Y || y > node_max.Y) + int mapindex = central_area_size.X * (z - node_min.Z) + + (x - node_min.X); + s16 y = heightmap[mapindex]; + if (y < water_level) continue; u32 vi = vm->m_area.index(x, y, z); @@ -895,7 +896,9 @@ void MapgenV6::placeTreesAndJungleGrass() for (u32 i = 0; i < tree_count; i++) { s16 x = myrand_range(p2d_min.X, p2d_max.X); s16 z = myrand_range(p2d_min.Y, p2d_max.Y); - s16 y = findGroundLevelFull(v2s16(x, z)); ////////////////////optimize this! + int mapindex = central_area_size.X * (z - node_min.Z) + + (x - node_min.X); + s16 y = heightmap[mapindex]; // Don't make a tree under water level // Don't make a tree so high that it doesn't fit if(y < water_level || y > node_max.Y - 6) diff --git a/src/mapgen_v7.cpp b/src/mapgen_v7.cpp index 5e6e15ad0..44cb37f11 100644 --- a/src/mapgen_v7.cpp +++ b/src/mapgen_v7.cpp @@ -58,7 +58,7 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) //// amount of elements to skip for the next index //// for noise/height/biome maps (not vmanip) this->ystride = csize.X; - this->zstride = csize.X * csize.Y; + this->zstride = csize.X * (csize.Y + 2); this->biomemap = new u8[csize.X * csize.Z]; this->heightmap = new s16[csize.X * csize.Z]; @@ -77,10 +77,10 @@ MapgenV7::MapgenV7(int mapgenid, MapgenParams *params, EmergeManager *emerge) noise_ridge_uwater = new Noise(&sp->np_ridge_uwater, seed, csize.X, csize.Z); //// 3d terrain noise - noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y, csize.Z); - noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y, csize.Z); - noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y, csize.Z); - noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y, csize.Z); + noise_mountain = new Noise(&sp->np_mountain, seed, csize.X, csize.Y + 2, csize.Z); + noise_ridge = new Noise(&sp->np_ridge, seed, csize.X, csize.Y + 2, csize.Z); + noise_cave1 = new Noise(&sp->np_cave1, seed, csize.X, csize.Y + 2, csize.Z); + noise_cave2 = new Noise(&sp->np_cave2, seed, csize.X, csize.Y + 2, csize.Z); //// Biome noise noise_heat = new Noise(¶ms->np_biome_heat, seed, csize.X, csize.Z); @@ -314,7 +314,9 @@ void MapgenV7::makeChunk(BlockMakeData *data) updateLiquid(&data->transforming_liquid, full_node_min, full_node_max); if (flags & MG_LIGHT) - calcLighting(node_min, node_max); + calcLighting(node_min - v3s16(0, 1, 0), node_max + v3s16(0, 1, 0), + full_node_min, full_node_max); + //setLighting(node_min - v3s16(1, 0, 1) * MAP_BLOCKSIZE, // node_max + v3s16(1, 0, 1) * MAP_BLOCKSIZE, 0xFF); @@ -326,7 +328,7 @@ void MapgenV7::calculateNoise() { //TimeTaker t("calculateNoise", NULL, PRECISION_MICRO); int x = node_min.X; - int y = node_min.Y; + int y = node_min.Y - 1; int z = node_min.Z; noise_terrain_persist->perlinMap2D(x, z); @@ -489,8 +491,8 @@ int MapgenV7::generateBaseTerrain() if (surface_y > stone_surface_max_y) stone_surface_max_y = surface_y; - u32 i = vm->m_area.index(x, node_min.Y, z); - for (s16 y = node_min.Y; y <= node_max.Y; y++) { + u32 i = vm->m_area.index(x, node_min.Y - 1, z); + for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { if (vm->m_data[i].getContent() == CONTENT_IGNORE) { if (y <= surface_y) vm->m_data[i] = n_stone; @@ -516,7 +518,7 @@ int MapgenV7::generateMountainTerrain(int ymax) u32 j = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 y = node_min.Y; y <= node_max.Y; y++) { + for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); for (s16 x = node_min.X; x <= node_max.X; x++) { int index = (z - node_min.Z) * csize.X + (x - node_min.X); @@ -549,7 +551,7 @@ void MapgenV7::generateRidgeTerrain() float width = 0.2; // TODO: figure out acceptable perlin noise values for (s16 z = node_min.Z; z <= node_max.Z; z++) - for (s16 y = node_min.Y; y <= node_max.Y; y++) { + for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 vi = vm->m_area.index(node_min.X, y, z); for (s16 x = node_min.X; x <= node_max.X; x++, index++, vi++) { int j = (z - node_min.Z) * csize.X + (x - node_min.X); @@ -583,10 +585,6 @@ bool MapgenV7::generateBiomes(float *heat_map, float *humidity_map) if (node_max.Y < water_level) return false; - MapNode n_air(CONTENT_AIR); - MapNode n_stone(c_stone); - MapNode n_water(c_water_source); - v3s16 em = vm->m_area.getExtent(); u32 index = 0; bool desert_stone = false; @@ -608,16 +606,6 @@ bool MapgenV7::generateBiomes(float *heat_map, float *humidity_map) for (s16 y = node_max.Y; y >= node_min.Y; y--) { content_t c = vm->m_data[i].getContent(); - // It could be the case that the elevation is equal to the chunk - // boundary, but the chunk above has not been generated yet - if (y == node_max.Y && c_above == CONTENT_IGNORE && - y == heightmap[index] && c == c_stone) { - int j = (z - node_min.Z) * zstride + - (y - node_min.Y) * ystride + - (x - node_min.X); - have_air = !getMountainTerrainFromMap(j, index, y); - } - if (c != CONTENT_IGNORE && c != CONTENT_AIR && (y == node_max.Y || have_air)) { biome = bmgr->getBiome(heat_map[index], humidity_map[index], y); dfiller = biome->depth_filler + noise_filler_depth->result[index]; @@ -691,27 +679,25 @@ void MapgenV7::dustTopNodes() if (biome->c_dust == CONTENT_IGNORE) continue; - s16 y_full_max = full_node_max.Y; - u32 vi_full_max = vm->m_area.index(x, y_full_max, z); - content_t c_full_max = vm->m_data[vi_full_max].getContent(); + u32 vi = vm->m_area.index(x, full_node_max.Y, z); + content_t c_full_max = vm->m_data[vi].getContent(); s16 y_start; if (c_full_max == CONTENT_AIR) { - y_start = y_full_max - 1; + y_start = full_node_max.Y - 1; } else if (c_full_max == CONTENT_IGNORE) { - s16 y_max = node_max.Y; - u32 vi_max = vm->m_area.index(x, y_max, z); - content_t c_max = vm->m_data[vi_max].getContent(); + vi = vm->m_area.index(x, node_max.Y + 1, z); + content_t c_max = vm->m_data[vi].getContent(); if (c_max == CONTENT_AIR) - y_start = y_max - 1; + y_start = node_max.Y; else continue; } else { continue; } - u32 vi = vm->m_area.index(x, y_start, z); + vi = vm->m_area.index(x, y_start, z); for (s16 y = y_start; y >= node_min.Y - 1; y--) { if (vm->m_data[vi].getContent() != CONTENT_AIR) break; @@ -831,7 +817,7 @@ void MapgenV7::generateCaves(int max_stone_y) u32 index2d = 0; for (s16 z = node_min.Z; z <= node_max.Z; z++) { - for (s16 y = node_min.Y; y <= node_max.Y; y++) { + for (s16 y = node_min.Y - 1; y <= node_max.Y + 1; y++) { u32 i = vm->m_area.index(node_min.X, y, z); for (s16 x = node_min.X; x <= node_max.X; x++, i++, index++, index2d++) { diff --git a/src/mg_ore.cpp b/src/mg_ore.cpp index c62f05860..850f25516 100644 --- a/src/mg_ore.cpp +++ b/src/mg_ore.cpp @@ -308,7 +308,7 @@ void OreVein::generate(MMVManip *vm, int mapseed, u32 blockseed, } // randval ranges from -1..1 - float randval = (float)pr.next() / (PSEUDORANDOM_MAX / 2) - 1.f; + float randval = (float)pr.next() / (pr.RANDOM_RANGE / 2) - 1.f; float noiseval = contour(noise->result[index]); float noiseval2 = contour(noise2->result[index]); if (noiseval * noiseval2 + randval * random_factor < nthresh) diff --git a/src/mg_schematic.cpp b/src/mg_schematic.cpp index a05e372e5..23b62115f 100644 --- a/src/mg_schematic.cpp +++ b/src/mg_schematic.cpp @@ -207,6 +207,11 @@ bool Schematic::loadSchematicFromFile(const char *filename, INodeDefManager *nde bool have_cignore = false; std::ifstream is(filename, std::ios_base::binary); + if (!is.good()) { + errorstream << "loadSchematicFile: unable to open file '" + << filename << "'" << std::endl; + return false; + } u32 signature = readU32(is); if (signature != MTSCHEM_FILE_SIGNATURE) { diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 69deb4dd5..d51324ed4 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -2924,7 +2924,7 @@ void Connection::Send(u16 peer_id, u8 channelnum, ConnectionCommand c; - c.send(peer_id, channelnum, pkt->oldForgePacket(), reliable); + c.send(peer_id, channelnum, pkt, reliable); putCommand(c); } diff --git a/src/network/connection.h b/src/network/connection.h index 33b7d0f7f..0aa63d4cf 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -437,19 +437,12 @@ struct ConnectionCommand peer_id = peer_id_; } void send(u16 peer_id_, u8 channelnum_, - SharedBuffer data_, bool reliable_) + NetworkPacket* pkt, bool reliable_) { type = CONNCMD_SEND; peer_id = peer_id_; channelnum = channelnum_; - data = data_; - reliable = reliable_; - } - void sendToAll(u8 channelnum_, SharedBuffer data_, bool reliable_) - { - type = CONNCMD_SEND_TO_ALL; - channelnum = channelnum_; - data = data_; + data = pkt->oldForgePacket(); reliable = reliable_; } diff --git a/src/network/networkpacket.cpp b/src/network/networkpacket.cpp index b2b1974d7..cfbca32d3 100644 --- a/src/network/networkpacket.cpp +++ b/src/network/networkpacket.cpp @@ -510,7 +510,7 @@ NetworkPacket& NetworkPacket::operator<<(video::SColor src) return *this; } -SharedBuffer NetworkPacket::oldForgePacket() +Buffer NetworkPacket::oldForgePacket() { SharedBuffer sb(m_datasize + 2); writeU16(&sb[0], m_command); diff --git a/src/network/networkpacket.h b/src/network/networkpacket.h index e8c8565b0..4a801b444 100644 --- a/src/network/networkpacket.h +++ b/src/network/networkpacket.h @@ -104,7 +104,7 @@ public: NetworkPacket& operator<<(video::SColor src); // Temp, we remove SharedBuffer when migration finished - SharedBuffer oldForgePacket(); + Buffer oldForgePacket(); private: template void checkDataSize() { diff --git a/src/noise.cpp b/src/noise.cpp index 5223450dc..bb7c9969e 100644 --- a/src/noise.cpp +++ b/src/noise.cpp @@ -62,6 +62,94 @@ FlagDesc flagdesc_noiseparams[] = { /////////////////////////////////////////////////////////////////////////////// +PcgRandom::PcgRandom(u64 state, u64 seq) +{ + seed(state, seq); +} + +void PcgRandom::seed(u64 state, u64 seq) +{ + m_state = 0U; + m_inc = (seq << 1u) | 1u; + next(); + m_state += state; + next(); +} + + +u32 PcgRandom::next() +{ + u64 oldstate = m_state; + m_state = oldstate * 6364136223846793005ULL + m_inc; + + u32 xorshifted = ((oldstate >> 18u) ^ oldstate) >> 27u; + u32 rot = oldstate >> 59u; + return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); +} + + +u32 PcgRandom::range(u32 bound) +{ + /* + If the bound is not a multiple of the RNG's range, it may cause bias, + e.g. a RNG has a range from 0 to 3 and we take want a number 0 to 2. + Using rand() % 3, the number 0 would be twice as likely to appear. + With a very large RNG range, the effect becomes less prevalent but + still present. This can be solved by modifying the range of the RNG + to become a multiple of bound by dropping values above the a threshhold. + In our example, threshhold == 4 - 3 = 1 % 3 == 1, so reject 0, thus + making the range 3 with no bias. + + This loop looks dangerous, but will always terminate due to the + RNG's property of uniformity. + */ + u32 threshhold = -bound % bound; + u32 r; + + while ((r = next()) < threshhold) + ; + + return r % bound; +} + + +s32 PcgRandom::range(s32 min, s32 max) +{ + assert(max >= min); + u32 bound = max - min + 1; + return range(bound) + min; +} + + +void PcgRandom::bytes(void *out, size_t len) +{ + u8 *outb = (u8 *)out; + int bytes_left = 0; + u32 r; + + while (len--) { + if (bytes_left == 0) { + bytes_left = sizeof(u32); + r = next(); + } + + *outb = r & 0xFF; + outb++; + bytes_left--; + r >>= 8; + } +} + + +s32 PcgRandom::randNormalDist(s32 min, s32 max, int num_trials) +{ + s32 accum = 0; + for (int i = 0; i != num_trials; i++) + accum += range(min, max); + return ((float)accum / num_trials) + 0.5f; +} + +/////////////////////////////////////////////////////////////////////////////// float noise2d(int x, int y, int seed) { diff --git a/src/noise.h b/src/noise.h index e59e73b23..d2287835e 100644 --- a/src/noise.h +++ b/src/noise.h @@ -30,47 +30,67 @@ #include "irr_v3d.h" #include "util/string.h" -#define PSEUDORANDOM_MAX 32767 - extern FlagDesc flagdesc_noiseparams[]; -class PseudoRandom -{ +// Note: this class is not polymorphic so that its high level of +// optimizability may be preserved in the common use case +class PseudoRandom { public: - PseudoRandom(): m_next(0) + const static u32 RANDOM_RANGE = 32767; + + inline PseudoRandom(int seed=0): + m_next(seed) { } - PseudoRandom(int seed): m_next(seed) - { - } - void seed(int seed) + + inline void seed(int seed) { m_next = seed; } - // Returns 0...PSEUDORANDOM_MAX - int next() + + inline int next() { m_next = m_next * 1103515245 + 12345; - return((unsigned)(m_next/65536) % (PSEUDORANDOM_MAX + 1)); + return (unsigned)(m_next / 65536) % (RANDOM_RANGE + 1); } - int range(int min, int max) + + inline int range(int min, int max) { - if (max-min > (PSEUDORANDOM_MAX + 1) / 10) - { - //dstream<<"WARNING: PseudoRandom::range: max > 32767"< max) - { - assert("Something wrong with random number" == NULL); - //return max; - } - return (next()%(max-min+1))+min; + assert(max >= min); + /* + Here, we ensure the range is not too large relative to RANDOM_MAX, + as otherwise the effects of bias would become noticable. Unlike + PcgRandom, we cannot modify this RNG's range as it would change the + output of this RNG for reverse compatibility. + */ + assert((u32)(max - min) <= (RANDOM_RANGE + 1) / 10); + + return (next() % (max - min + 1)) + min; } + private: int m_next; }; +class PcgRandom { +public: + const static s32 RANDOM_MIN = -0x7fffffff - 1; + const static s32 RANDOM_MAX = 0x7fffffff; + const static u32 RANDOM_RANGE = 0xffffffff; + + PcgRandom(u64 state=0x853c49e6748fea9bULL, u64 seq=0xda3e39cb94b95bdbULL); + void seed(u64 state, u64 seq=0xda3e39cb94b95bdbULL); + u32 next(); + u32 range(u32 bound); + s32 range(s32 min, s32 max); + void bytes(void *out, size_t len); + s32 randNormalDist(s32 min, s32 max, int num_trials=6); + +private: + u64 m_state; + u64 m_inc; +}; + #define NOISE_FLAG_DEFAULTS 0x01 #define NOISE_FLAG_EASED 0x02 #define NOISE_FLAG_ABSVALUE 0x04 @@ -89,7 +109,8 @@ struct NoiseParams { float lacunarity; u32 flags; - NoiseParams() { + NoiseParams() + { offset = 0.0f; scale = 1.0f; spread = v3f(250, 250, 250); diff --git a/src/player.cpp b/src/player.cpp index 0da761eed..08cb24248 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "player.h" #include +#include "jthread/jmutexautolock.h" #include "util/numeric.h" #include "hud.h" #include "constants.h" @@ -240,6 +241,8 @@ void Player::deSerialize(std::istream &is, std::string playername) u32 Player::addHud(HudElement *toadd) { + JMutexAutoLock lock(m_mutex); + u32 id = getFreeHudID(); if (id < hud.size()) @@ -252,6 +255,8 @@ u32 Player::addHud(HudElement *toadd) HudElement* Player::getHud(u32 id) { + JMutexAutoLock lock(m_mutex); + if (id < hud.size()) return hud[id]; @@ -260,6 +265,8 @@ HudElement* Player::getHud(u32 id) HudElement* Player::removeHud(u32 id) { + JMutexAutoLock lock(m_mutex); + HudElement* retval = NULL; if (id < hud.size()) { retval = hud[id]; @@ -270,6 +277,8 @@ HudElement* Player::removeHud(u32 id) void Player::clearHud() { + JMutexAutoLock lock(m_mutex); + while(!hud.empty()) { delete hud.back(); hud.pop_back(); diff --git a/src/player.h b/src/player.h index def428847..d4698ea4a 100644 --- a/src/player.h +++ b/src/player.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_bloated.h" #include "inventory.h" #include "constants.h" // BS +#include "jthread/jmutex.h" #include #define PLAYERNAME_SIZE 20 @@ -202,7 +203,7 @@ public: return m_collisionbox; } - u32 getFreeHudID() const { + u32 getFreeHudID() { size_t size = hud.size(); for (size_t i = 0; i != size; i++) { if (!hud[i]) @@ -318,6 +319,11 @@ protected: bool m_dirty; std::vector hud; +private: + // Protect some critical areas + // hud for example can be modified by EmergeThread + // and ServerThread + JMutex m_mutex; }; diff --git a/src/script/lua_api/l_craft.cpp b/src/script/lua_api/l_craft.cpp index 40342871d..b321fb32f 100644 --- a/src/script/lua_api/l_craft.cpp +++ b/src/script/lua_api/l_craft.cpp @@ -393,6 +393,8 @@ int ModApiCraft::l_get_craft_recipe(lua_State *L) std::vector recipes = server->cdef() ->getCraftRecipes(output, server, 1); + lua_createtable(L, 1, 0); + if (recipes.empty()) { lua_pushnil(L); lua_setfield(L, -2, "items"); diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index a76e8625c..8806581de 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -89,7 +89,7 @@ struct EnumString ModApiMapgen::es_Rotation[] = /////////////////////////////////////////////////////////////////////////////// -bool read_schematic(lua_State *L, int index, Schematic *schem, +bool read_schematic_def(lua_State *L, int index, Schematic *schem, INodeDefManager *ndef, std::map &replace_names) { //// Get schematic size @@ -175,20 +175,45 @@ bool read_schematic(lua_State *L, int index, Schematic *schem, } -bool get_schematic(lua_State *L, int index, Schematic *schem, - INodeDefManager *ndef, std::map &replace_names) +Schematic *get_schematic(lua_State *L, int index, SchematicManager *schemmgr, + std::map &replace_names) { if (index < 0) index = lua_gettop(L) + 1 + index; - if (lua_istable(L, index)) { - return read_schematic(L, index, schem, ndef, replace_names); - } else if (lua_isstring(L, index)) { + Schematic *schem; + + if (lua_isnumber(L, index)) { + return (Schematic *)schemmgr->get(lua_tointeger(L, index)); + } else if (lua_istable(L, index)) { + schem = new Schematic; + if (!read_schematic_def(L, index, schem, + schemmgr->getNodeDef(), replace_names)) { + delete schem; + return NULL; + } + } else if (lua_isstring(L, index)) { const char *filename = lua_tostring(L, index); - return schem->loadSchematicFromFile(filename, ndef, replace_names); + schem = (Schematic *)schemmgr->getByName(filename); + if (schem) + return schem; + + schem = new Schematic; + if (!schem->loadSchematicFromFile(filename, + schemmgr->getNodeDef(), replace_names)) { + delete schem; + return NULL; + } } else { - return false; + return NULL; } + + if (schemmgr->add(schem) == (u32)-1) { + delete schem; + return 0; + } + + return schem; } @@ -469,32 +494,10 @@ int ModApiMapgen::l_register_biome(lua_State *L) ndef->pendNodeResolve(nri); verbosestream << "register_biome: " << b->name << std::endl; - lua_pushinteger(L, id); return 1; } -int ModApiMapgen::l_clear_registered_biomes(lua_State *L) -{ - BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; - bmgr->clear(); - return 0; -} - -int ModApiMapgen::l_clear_registered_decorations(lua_State *L) -{ - DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; - dmgr->clear(); - return 0; -} - -int ModApiMapgen::l_clear_registered_ores(lua_State *L) -{ - OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; - omgr->clear(); - return 0; -} - // register_decoration({lots of stuff}) int ModApiMapgen::l_register_decoration(lua_State *L) { @@ -504,6 +507,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L) INodeDefManager *ndef = getServer(L)->getNodeDefManager(); DecorationManager *decomgr = getServer(L)->getEmergeManager()->decomgr; BiomeManager *biomemgr = getServer(L)->getEmergeManager()->biomemgr; + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; enum DecorationType decotype = (DecorationType)getenumfield(L, index, "deco_type", es_DecorationType, -1); @@ -562,7 +566,7 @@ int ModApiMapgen::l_register_decoration(lua_State *L) success = regDecoSimple(L, nri, (DecoSimple *)deco); break; case DECO_SCHEMATIC: - success = regDecoSchematic(L, ndef, (DecoSchematic *)deco); + success = regDecoSchematic(L, schemmgr, (DecoSchematic *)deco); break; case DECO_LSYSTEM: break; @@ -582,7 +586,6 @@ int ModApiMapgen::l_register_decoration(lua_State *L) } verbosestream << "register_decoration: " << deco->name << std::endl; - lua_pushinteger(L, id); return 1; } @@ -627,8 +630,8 @@ bool ModApiMapgen::regDecoSimple(lua_State *L, return true; } -bool ModApiMapgen::regDecoSchematic(lua_State *L, INodeDefManager *ndef, - DecoSchematic *deco) +bool ModApiMapgen::regDecoSchematic(lua_State *L, + SchematicManager *schemmgr, DecoSchematic *deco) { int index = 1; @@ -641,19 +644,12 @@ bool ModApiMapgen::regDecoSchematic(lua_State *L, INodeDefManager *ndef, read_schematic_replacements(L, replace_names, lua_gettop(L)); lua_pop(L, 1); - // TODO(hmmmm): get a ref from registered schematics - Schematic *schem = new Schematic; lua_getfield(L, index, "schematic"); - if (!get_schematic(L, -1, schem, ndef, replace_names)) { - lua_pop(L, 1); - delete schem; - return false; - } + Schematic *schem = get_schematic(L, -1, schemmgr, replace_names); lua_pop(L, 1); deco->schematic = schem; - - return true; + return schem != NULL; } // register_ore({lots of stuff}) @@ -741,11 +737,100 @@ int ModApiMapgen::l_register_ore(lua_State *L) ndef->pendNodeResolve(nri); verbosestream << "register_ore: " << ore->name << std::endl; - lua_pushinteger(L, id); return 1; } +// register_schematic({schematic}, replacements={}) +int ModApiMapgen::l_register_schematic(lua_State *L) +{ + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; + + std::map replace_names; + if (lua_istable(L, 2)) + read_schematic_replacements(L, replace_names, 2); + + Schematic *schem = get_schematic(L, 1, schemmgr, replace_names); + if (!schem) + return 0; + printf("register_schematic!\n"); + verbosestream << "register_schematic: " << schem->name << std::endl; + lua_pushinteger(L, schem->id); + return 1; +} + +// clear_registered_biomes() +int ModApiMapgen::l_clear_registered_biomes(lua_State *L) +{ + BiomeManager *bmgr = getServer(L)->getEmergeManager()->biomemgr; + bmgr->clear(); + return 0; +} + +// clear_registered_decorations() +int ModApiMapgen::l_clear_registered_decorations(lua_State *L) +{ + DecorationManager *dmgr = getServer(L)->getEmergeManager()->decomgr; + dmgr->clear(); + return 0; +} + +// clear_registered_ores() +int ModApiMapgen::l_clear_registered_ores(lua_State *L) +{ + OreManager *omgr = getServer(L)->getEmergeManager()->oremgr; + omgr->clear(); + return 0; +} + +// clear_registered_schematics() +int ModApiMapgen::l_clear_registered_schematics(lua_State *L) +{ + SchematicManager *smgr = getServer(L)->getEmergeManager()->schemmgr; + smgr->clear(); + return 0; +} + +// generate_ores(vm, p1, p2, [ore_id]) +int ModApiMapgen::l_generate_ores(lua_State *L) +{ + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->params.seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); + + v3s16 pmin = read_v3s16(L, 2); + v3s16 pmax = read_v3s16(L, 3); + + emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); + + return 0; +} + +// generate_decorations(vm, p1, p2, [deco_id]) +int ModApiMapgen::l_generate_decorations(lua_State *L) +{ + EmergeManager *emerge = getServer(L)->getEmergeManager(); + + Mapgen mg; + mg.seed = emerge->params.seed; + mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; + mg.ndef = getServer(L)->getNodeDefManager(); + + u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); + + v3s16 pmin = read_v3s16(L, 2); + v3s16 pmax = read_v3s16(L, 3); + + emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); + + return 0; +} + // create_schematic(p1, p2, probability_list, filename) int ModApiMapgen::l_create_schematic(lua_State *L) { @@ -806,53 +891,11 @@ int ModApiMapgen::l_create_schematic(lua_State *L) return 1; } -// generate_ores(vm, p1, p2, [ore_id]) -int ModApiMapgen::l_generate_ores(lua_State *L) -{ - EmergeManager *emerge = getServer(L)->getEmergeManager(); - - Mapgen mg; - mg.seed = emerge->params.seed; - mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; - mg.ndef = getServer(L)->getNodeDefManager(); - - u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); - - v3s16 pmin = read_v3s16(L, 2); - v3s16 pmax = read_v3s16(L, 3); - - emerge->oremgr->placeAllOres(&mg, blockseed, pmin, pmax); - - return 0; -} - -// generate_decorations(vm, p1, p2, [deco_id]) -int ModApiMapgen::l_generate_decorations(lua_State *L) -{ - EmergeManager *emerge = getServer(L)->getEmergeManager(); - - Mapgen mg; - mg.seed = emerge->params.seed; - mg.vm = LuaVoxelManip::checkobject(L, 1)->vm; - mg.ndef = getServer(L)->getNodeDefManager(); - - u32 blockseed = Mapgen::getBlockSeed(mg.vm->m_area.MinEdge, mg.seed); - - v3s16 pmin = read_v3s16(L, 2); - v3s16 pmax = read_v3s16(L, 3); - - emerge->decomgr->placeAllDecos(&mg, blockseed, pmin, pmax); - - return 0; -} - // place_schematic(p, schematic, rotation, replacement) int ModApiMapgen::l_place_schematic(lua_State *L) { - Schematic schem; - Map *map = &(getEnv(L)->getMap()); - INodeDefManager *ndef = getServer(L)->getNodeDefManager(); + SchematicManager *schemmgr = getServer(L)->getEmergeManager()->schemmgr; //// Read position v3s16 p = read_v3s16(L, 1); @@ -873,12 +916,14 @@ int ModApiMapgen::l_place_schematic(lua_State *L) read_schematic_replacements(L, replace_names, 4); //// Read schematic - if (!get_schematic(L, 2, &schem, ndef, replace_names)) { + Schematic *schem = get_schematic(L, 2, schemmgr, replace_names); + if (!schem) { errorstream << "place_schematic: failed to get schematic" << std::endl; return 0; } - schem.placeStructure(map, p, 0, (Rotation)rot, force_placement, ndef); + schem->placeStructure(map, p, 0, (Rotation)rot, force_placement, + schemmgr->getNodeDef()); return 1; } @@ -895,14 +940,15 @@ void ModApiMapgen::Initialize(lua_State *L, int top) API_FCT(register_biome); API_FCT(register_decoration); API_FCT(register_ore); + API_FCT(register_schematic); API_FCT(clear_registered_biomes); API_FCT(clear_registered_decorations); API_FCT(clear_registered_ores); + API_FCT(clear_registered_schematics); API_FCT(generate_ores); API_FCT(generate_decorations); - API_FCT(create_schematic); API_FCT(place_schematic); } diff --git a/src/script/lua_api/l_mapgen.h b/src/script/lua_api/l_mapgen.h index dfed8475f..2ad436e77 100644 --- a/src/script/lua_api/l_mapgen.h +++ b/src/script/lua_api/l_mapgen.h @@ -26,6 +26,7 @@ class INodeDefManager; struct NodeResolveInfo; class DecoSimple; class DecoSchematic; +class SchematicManager; class ModApiMapgen : public ModApiBase { private: @@ -56,12 +57,18 @@ private: // register_ore({lots of stuff}) static int l_register_ore(lua_State *L); + // register_schematic({schematic}, replacements={}) + static int l_register_schematic(lua_State *L); + // clear_registered_biomes() static int l_clear_registered_biomes(lua_State *L); // clear_registered_decorations() static int l_clear_registered_decorations(lua_State *L); + // clear_registered_schematics() + static int l_clear_registered_schematics(lua_State *L); + // generate_ores(vm, p1, p2) static int l_generate_ores(lua_State *L); @@ -80,7 +87,7 @@ private: static bool regDecoSimple(lua_State *L, NodeResolveInfo *nri, DecoSimple *deco); static bool regDecoSchematic(lua_State *L, - INodeDefManager *ndef, DecoSchematic *deco); + SchematicManager *schemmgr, DecoSchematic *deco); static struct EnumString es_BiomeTerrainType[]; static struct EnumString es_DecorationType[]; diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 5a82b6485..bf3dca589 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -23,12 +23,19 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "common/c_content.h" #include "log.h" -// garbage collector -int LuaPerlinNoise::gc_object(lua_State *L) +/////////////////////////////////////// +/* + LuaPerlinNoise +*/ + +LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) : + np(*params) +{ +} + + +LuaPerlinNoise::~LuaPerlinNoise() { - LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); - delete o; - return 0; } @@ -54,19 +61,6 @@ int LuaPerlinNoise::l_get3d(lua_State *L) } -LuaPerlinNoise::LuaPerlinNoise(NoiseParams *params) : - np(*params) -{ -} - - -LuaPerlinNoise::~LuaPerlinNoise() -{ -} - - -// LuaPerlinNoise(seed, octaves, persistence, scale) -// Creates an LuaPerlinNoise and leaves it on top of stack int LuaPerlinNoise::create_object(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -91,14 +85,22 @@ int LuaPerlinNoise::create_object(lua_State *L) } -LuaPerlinNoise* LuaPerlinNoise::checkobject(lua_State *L, int narg) +int LuaPerlinNoise::gc_object(lua_State *L) +{ + LuaPerlinNoise *o = *(LuaPerlinNoise **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPerlinNoise *LuaPerlinNoise::checkobject(lua_State *L, int narg) { NO_MAP_LOCK_REQUIRED; luaL_checktype(L, narg, LUA_TUSERDATA); void *ud = luaL_checkudata(L, narg, className); if (!ud) luaL_typerror(L, narg, className); - return *(LuaPerlinNoise**)ud; // unbox pointer + return *(LuaPerlinNoise **)ud; } @@ -111,7 +113,7 @@ void LuaPerlinNoise::Register(lua_State *L) lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -121,12 +123,11 @@ void LuaPerlinNoise::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); - lua_pop(L, 1); // drop metatable + lua_pop(L, 1); - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); - // Can be created from Lua (PerlinNoise(seed, octaves, persistence) lua_register(L, className, create_object); } @@ -138,16 +139,26 @@ const luaL_reg LuaPerlinNoise::methods[] = { {0,0} }; - +/////////////////////////////////////// /* - PerlinNoiseMap - */ + LuaPerlinNoiseMap +*/ -int LuaPerlinNoiseMap::gc_object(lua_State *L) +LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size) { - LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); - delete o; - return 0; + m_is3d = size.Z > 1; + np = *params; + try { + noise = new Noise(&np, seed, size.X, size.Y, size.Z); + } catch (InvalidNoiseParamsException &e) { + throw LuaError(e.what()); + } +} + + +LuaPerlinNoiseMap::~LuaPerlinNoiseMap() +{ + delete noise; } @@ -251,26 +262,6 @@ int LuaPerlinNoiseMap::l_get3dMap_flat(lua_State *L) } -LuaPerlinNoiseMap::LuaPerlinNoiseMap(NoiseParams *params, int seed, v3s16 size) -{ - m_is3d = size.Z > 1; - np = *params; - try { - noise = new Noise(&np, seed, size.X, size.Y, size.Z); - } catch (InvalidNoiseParamsException &e) { - throw LuaError(e.what()); - } -} - - -LuaPerlinNoiseMap::~LuaPerlinNoiseMap() -{ - delete noise; -} - - -// LuaPerlinNoiseMap(np, size) -// Creates an LuaPerlinNoiseMap and leaves it on top of stack int LuaPerlinNoiseMap::create_object(lua_State *L) { NoiseParams np; @@ -286,6 +277,14 @@ int LuaPerlinNoiseMap::create_object(lua_State *L) } +int LuaPerlinNoiseMap::gc_object(lua_State *L) +{ + LuaPerlinNoiseMap *o = *(LuaPerlinNoiseMap **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TUSERDATA); @@ -294,7 +293,7 @@ LuaPerlinNoiseMap *LuaPerlinNoiseMap::checkobject(lua_State *L, int narg) if (!ud) luaL_typerror(L, narg, className); - return *(LuaPerlinNoiseMap **)ud; // unbox pointer + return *(LuaPerlinNoiseMap **)ud; } @@ -307,7 +306,7 @@ void LuaPerlinNoiseMap::Register(lua_State *L) lua_pushliteral(L, "__metatable"); lua_pushvalue(L, methodtable); - lua_settable(L, metatable); // hide metatable from Lua getmetatable() + lua_settable(L, metatable); lua_pushliteral(L, "__index"); lua_pushvalue(L, methodtable); @@ -317,12 +316,11 @@ void LuaPerlinNoiseMap::Register(lua_State *L) lua_pushcfunction(L, gc_object); lua_settable(L, metatable); - lua_pop(L, 1); // drop metatable + lua_pop(L, 1); - luaL_openlib(L, 0, methods, 0); // fill methodtable - lua_pop(L, 1); // drop methodtable + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); - // Can be created from Lua (PerlinNoiseMap(np, size) lua_register(L, className, create_object); } @@ -336,32 +334,23 @@ const luaL_reg LuaPerlinNoiseMap::methods[] = { {0,0} }; +/////////////////////////////////////// /* LuaPseudoRandom */ -// garbage collector -int LuaPseudoRandom::gc_object(lua_State *L) -{ - LuaPseudoRandom *o = *(LuaPseudoRandom **)(lua_touserdata(L, 1)); - delete o; - return 0; -} - - -// next(self, min=0, max=32767) -> get next value int LuaPseudoRandom::l_next(lua_State *L) { NO_MAP_LOCK_REQUIRED; LuaPseudoRandom *o = checkobject(L, 1); int min = 0; int max = 32767; - lua_settop(L, 3); // Fill 2 and 3 with nil if they don't exist - if(!lua_isnil(L, 2)) + lua_settop(L, 3); + if (lua_isnumber(L, 2)) min = luaL_checkinteger(L, 2); - if(!lua_isnil(L, 3)) + if (lua_isnumber(L, 3)) max = luaL_checkinteger(L, 3); - if(max < min){ + if (max < min) { errorstream<<"PseudoRandom.next(): max="<m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + + lua_pushinteger(L, o->m_rnd.range(min, max)); + return 1; +} + + +int LuaPcgRandom::l_rand_normal_dist(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + LuaPcgRandom *o = checkobject(L, 1); + u32 min = lua_isnumber(L, 2) ? lua_tointeger(L, 2) : o->m_rnd.RANDOM_MIN; + u32 max = lua_isnumber(L, 3) ? lua_tointeger(L, 3) : o->m_rnd.RANDOM_MAX; + int num_trials = lua_isnumber(L, 4) ? lua_tointeger(L, 4) : 6; + + lua_pushinteger(L, o->m_rnd.randNormalDist(min, max, num_trials)); + return 1; +} + + +int LuaPcgRandom::create_object(lua_State *L) +{ + lua_Integer seed = luaL_checknumber(L, 1); + LuaPcgRandom *o = lua_isnumber(L, 2) ? + new LuaPcgRandom(seed, lua_tointeger(L, 2)) : + new LuaPcgRandom(seed); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); + return 1; +} + + +int LuaPcgRandom::gc_object(lua_State *L) +{ + LuaPcgRandom *o = *(LuaPcgRandom **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + + +LuaPcgRandom *LuaPcgRandom::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + return *(LuaPcgRandom **)ud; +} + + +void LuaPcgRandom::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pop(L, 1); + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + lua_register(L, className, create_object); +} + + +const char LuaPcgRandom::className[] = "PcgRandom"; +const luaL_reg LuaPcgRandom::methods[] = { + luamethod(LuaPcgRandom, next), + luamethod(LuaPcgRandom, rand_normal_dist), + {0,0} +}; diff --git a/src/script/lua_api/l_noise.h b/src/script/lua_api/l_noise.h index 3e22ac7a0..56d2d59f8 100644 --- a/src/script/lua_api/l_noise.h +++ b/src/script/lua_api/l_noise.h @@ -64,6 +64,9 @@ class LuaPerlinNoiseMap : public ModApiBase { static const char className[]; static const luaL_reg methods[]; + // Exported functions + + // garbage collector static int gc_object(lua_State *L); static int l_get2dMap(lua_State *L); @@ -104,18 +107,51 @@ private: static int l_next(lua_State *L); public: - LuaPseudoRandom(int seed); - - ~LuaPseudoRandom(); - - const PseudoRandom& getItem() const; - PseudoRandom& getItem(); + LuaPseudoRandom(int seed) : + m_pseudo(seed) {} // LuaPseudoRandom(seed) // Creates an LuaPseudoRandom and leaves it on top of stack static int create_object(lua_State *L); - static LuaPseudoRandom* checkobject(lua_State *L, int narg); + static LuaPseudoRandom *checkobject(lua_State *L, int narg); + + static void Register(lua_State *L); +}; + +/* + LuaPcgRandom +*/ +class LuaPcgRandom : public ModApiBase { +private: + PcgRandom m_rnd; + + static const char className[]; + static const luaL_reg methods[]; + + // Exported functions + + // garbage collector + static int gc_object(lua_State *L); + + // next(self, min=-2147483648, max=2147483647) -> get next value + static int l_next(lua_State *L); + + // rand_normal_dist(self, min=-2147483648, max=2147483647, num_trials=6) -> + // get next normally distributed random value + static int l_rand_normal_dist(lua_State *L); + +public: + LuaPcgRandom(u64 seed) : + m_rnd(seed) {} + LuaPcgRandom(u64 seed, u64 seq) : + m_rnd(seed, seq) {} + + // LuaPcgRandom(seed) + // Creates an LuaPcgRandom and leaves it on top of stack + static int create_object(lua_State *L); + + static LuaPcgRandom *checkobject(lua_State *L, int narg); static void Register(lua_State *L); }; diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 079558c51..d8cfaf82b 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -29,8 +29,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "content_sao.h" #include "server.h" #include "hud.h" -#include "settings.h" -#include "main.h" struct EnumString es_HudElementType[] = @@ -257,10 +255,10 @@ int ObjectRef::l_set_hp(lua_State *L) ObjectRef *ref = checkobject(L, 1); luaL_checknumber(L, 2); ServerActiveObject *co = getobject(ref); - if(co == NULL) - return 0; + if(co == NULL) return 0; int hp = lua_tonumber(L, 2); - + /*infostream<<"ObjectRef::l_set_hp(): id="<getId() + <<" hp="<setHP(hp); if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { @@ -291,38 +289,6 @@ int ObjectRef::l_get_hp(lua_State *L) return 1; } -// apply_damage(self, damage) -// damage = amount of damage to apply -// if damage is negative, heal the player -// returns: nil -int ObjectRef::l_apply_damage(lua_State *L) -{ - NO_MAP_LOCK_REQUIRED; - ObjectRef *ref = checkobject(L, 1); - luaL_checknumber(L, 2); - ServerActiveObject *co = getobject(ref); - if(co == NULL) - return 0; - - int damage = lua_tonumber(L, 2); - - // No damage, no heal => do nothing - if (damage == 0) - return 0; - - // If damage is positive (not healing) and damage is disabled, ignore - if (damage > 0 && g_settings->getBool("enable_damage") == false) - return 0; - - // Do damage/heal - co->setHP(co->getHP() - damage); - if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) { - getServer(L)->SendPlayerHPOrDie(((PlayerSAO*)co)->getPeerID(), co->getHP() == 0); - } - - return 0; -} - // get_inventory(self) int ObjectRef::l_get_inventory(lua_State *L) { @@ -1379,7 +1345,6 @@ const luaL_reg ObjectRef::methods[] = { luamethod(ObjectRef, right_click), luamethod(ObjectRef, set_hp), luamethod(ObjectRef, get_hp), - luamethod(ObjectRef, apply_damage), luamethod(ObjectRef, get_inventory), luamethod(ObjectRef, get_wield_list), luamethod(ObjectRef, get_wield_index), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 94b07070c..d51ca379f 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -83,12 +83,6 @@ private: // 0 if not applicable to this type of object static int l_get_hp(lua_State *L); - // apply_damage(self, damage) - // damage = amount of damage to apply - // if damage is negative, heal the player - // returns: nil - static int l_apply_damage(lua_State *L); - // get_inventory(self) static int l_get_inventory(lua_State *L); diff --git a/src/script/scripting_game.cpp b/src/script/scripting_game.cpp index e716bc979..5bcd2a33d 100644 --- a/src/script/scripting_game.cpp +++ b/src/script/scripting_game.cpp @@ -92,6 +92,7 @@ void GameScripting::InitializeModApi(lua_State *L, int top) LuaPerlinNoise::Register(L); LuaPerlinNoiseMap::Register(L); LuaPseudoRandom::Register(L); + LuaPcgRandom::Register(L); LuaVoxelManip::Register(L); NodeMetaRef::Register(L); NodeTimerRef::Register(L); diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index 19f927134..a1f1fd0ab 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "../constants.h" // BS, MAP_BLOCKSIZE +#include "../noise.h" // PseudoRandom, PcgRandom #include #include @@ -115,36 +116,32 @@ void FacePositionCache::generateFacePosition(u16 d) myrand */ -static unsigned long next = 1; +PcgRandom g_pcgrand; -/* RAND_MAX assumed to be 32767 */ -int myrand(void) +u32 myrand() { - next = next * 1103515245 + 12345; - return((unsigned)(next/65536) % 32768); + return g_pcgrand.next(); } -void mysrand(unsigned seed) +void mysrand(unsigned int seed) { - next = seed; + g_pcgrand.seed(seed); +} + +void myrand_bytes(void *out, size_t len) +{ + g_pcgrand.bytes(out, len); } int myrand_range(int min, int max) { - if(max-min > MYRAND_MAX) - { - errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"< max) - { - errorstream<<"WARNING: myrand_range: min > max"<> r; - k *= m; - + k *= m; + k ^= k >> r; + k *= m; + h ^= k; - h *= m; + h *= m; } const unsigned char *data2 = (const unsigned char *)data; @@ -178,13 +175,13 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed) case 1: h ^= (u64)data2[0]; h *= m; } - + h ^= h >> r; h *= m; h ^= h >> r; - + return h; -} +} /* @@ -197,7 +194,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 camera_fov, f32 range, f32 *distance_ptr) { v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; - + // Block center position v3f blockpos( ((float)blockpos_nodes.X + MAP_BLOCKSIZE/2) * BS, @@ -213,7 +210,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, if(distance_ptr) *distance_ptr = d; - + // If block is far away, it's not in sight if(d > range) return false; @@ -221,7 +218,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // Maximum radius of a block. The magic number is // sqrt(3.0) / 2.0 in literal form. f32 block_max_radius = 0.866025403784 * MAP_BLOCKSIZE * BS; - + // If block is (nearly) touching the camera, don't // bother validating further (that is, render it anyway) if(d < block_max_radius) @@ -242,7 +239,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // Cosine of the angle between the camera direction // and the block direction (camera_dir is an unit vector) f32 cosangle = dforward / blockpos_adj.getLength(); - + // If block is not in the field of view, skip it if(cosangle < cos(camera_fov / 2)) return false; diff --git a/src/util/numeric.h b/src/util/numeric.h index 42d9a87a9..ccc9fbee4 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -239,10 +239,10 @@ inline float wrapDegrees_180(float f) /* Pseudo-random (VC++ rand() sucks) */ -int myrand(void); -void mysrand(unsigned seed); -#define MYRAND_MAX 32767 - +#define MYRAND_RANGE 0xffffffff +u32 myrand(); +void mysrand(unsigned int seed); +void myrand_bytes(void *out, size_t len); int myrand_range(int min, int max); /*