forked from Mirrorlandia_minetest/minetest
master #5
@ -8837,8 +8837,8 @@ Used by `minetest.register_node`.
|
|||||||
-- depending on the alpha channel being below/above 50% in value
|
-- depending on the alpha channel being below/above 50% in value
|
||||||
-- * "blend": The alpha channel specifies how transparent a given pixel
|
-- * "blend": The alpha channel specifies how transparent a given pixel
|
||||||
-- of the rendered node is
|
-- of the rendered node is
|
||||||
-- The default is "opaque" for drawtypes normal, liquid and flowingliquid;
|
-- The default is "opaque" for drawtypes normal, liquid and flowingliquid,
|
||||||
-- "clip" otherwise.
|
-- mesh and nodebox or "clip" otherwise.
|
||||||
-- If set to a boolean value (deprecated): true either sets it to blend
|
-- If set to a boolean value (deprecated): true either sets it to blend
|
||||||
-- or clip, false sets it to clip or opaque mode depending on the drawtype.
|
-- or clip, false sets it to clip or opaque mode depending on the drawtype.
|
||||||
|
|
||||||
|
@ -758,14 +758,32 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
|
|||||||
std::string tmp_file = path + ".~mt";
|
std::string tmp_file = path + ".~mt";
|
||||||
|
|
||||||
// Write to a tmp file
|
// Write to a tmp file
|
||||||
std::ofstream os(tmp_file.c_str(), std::ios::binary);
|
bool tmp_success = false;
|
||||||
if (!os.good())
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// We've observed behavior suggesting that the MSVC implementation of std::ofstream::flush doesn't
|
||||||
|
// actually flush, so we use win32 APIs.
|
||||||
|
HANDLE tmp_handle = CreateFile(
|
||||||
|
tmp_file.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (tmp_handle == INVALID_HANDLE_VALUE) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
DWORD bytes_written;
|
||||||
|
tmp_success = (WriteFile(tmp_handle, content.c_str(), content.size(), &bytes_written, nullptr) &&
|
||||||
|
FlushFileBuffers(tmp_handle));
|
||||||
|
CloseHandle(tmp_handle);
|
||||||
|
#else
|
||||||
|
std::ofstream os(tmp_file.c_str(), std::ios::binary);
|
||||||
|
if (!os.good()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
os << content;
|
os << content;
|
||||||
os.flush();
|
os.flush();
|
||||||
os.close();
|
os.close();
|
||||||
if (os.fail()) {
|
tmp_success = !os.fail();
|
||||||
// Remove the temporary file because writing it failed and it's useless.
|
#endif
|
||||||
|
|
||||||
|
if (!tmp_success) {
|
||||||
remove(tmp_file.c_str());
|
remove(tmp_file.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -777,14 +795,12 @@ bool safeWriteToFile(const std::string &path, const std::string &content)
|
|||||||
// When creating the file, it can cause Windows Search indexer, virus scanners and other apps
|
// When creating the file, it can cause Windows Search indexer, virus scanners and other apps
|
||||||
// to query the file. This can make the move file call below fail.
|
// to query the file. This can make the move file call below fail.
|
||||||
// We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
|
// We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed
|
||||||
int number_attempts = 0;
|
for (int attempt = 0; attempt < 5; attempt++) {
|
||||||
while (number_attempts < 5) {
|
|
||||||
rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
|
rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(),
|
||||||
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH);
|
||||||
if (rename_success)
|
if (rename_success)
|
||||||
break;
|
break;
|
||||||
sleep_ms(1);
|
sleep_ms(1);
|
||||||
++number_attempts;
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// On POSIX compliant systems rename() is specified to be able to swap the
|
// On POSIX compliant systems rename() is specified to be able to swap the
|
||||||
|
@ -420,8 +420,6 @@ void ContentFeatures::reset()
|
|||||||
|
|
||||||
void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
|
void ContentFeatures::setAlphaFromLegacy(u8 legacy_alpha)
|
||||||
{
|
{
|
||||||
// No special handling for nodebox/mesh here as it doesn't make sense to
|
|
||||||
// throw warnings when the server is too old to support the "correct" way
|
|
||||||
switch (drawtype) {
|
switch (drawtype) {
|
||||||
case NDT_NORMAL:
|
case NDT_NORMAL:
|
||||||
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
|
alpha = legacy_alpha == 255 ? ALPHAMODE_OPAQUE : ALPHAMODE_CLIP;
|
||||||
@ -648,6 +646,8 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
|||||||
if (is.eof())
|
if (is.eof())
|
||||||
throw SerializationError("");
|
throw SerializationError("");
|
||||||
alpha = static_cast<enum AlphaMode>(tmp);
|
alpha = static_cast<enum AlphaMode>(tmp);
|
||||||
|
if (alpha == ALPHAMODE_LEGACY_COMPAT)
|
||||||
|
alpha = ALPHAMODE_OPAQUE;
|
||||||
|
|
||||||
tmp = readU8(is);
|
tmp = readU8(is);
|
||||||
if (is.eof())
|
if (is.eof())
|
||||||
@ -749,55 +749,6 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ContentFeatures::textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles, int length)
|
|
||||||
{
|
|
||||||
video::IVideoDriver *driver = RenderingEngine::get_video_driver();
|
|
||||||
static thread_local bool long_warning_printed = false;
|
|
||||||
std::set<std::string> seen;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
if (seen.find(tiles[i].name) != seen.end())
|
|
||||||
continue;
|
|
||||||
seen.insert(tiles[i].name);
|
|
||||||
|
|
||||||
// Load the texture and see if there's any transparent pixels
|
|
||||||
video::ITexture *texture = tsrc->getTexture(tiles[i].name);
|
|
||||||
video::IImage *image = driver->createImage(texture,
|
|
||||||
core::position2d<s32>(0, 0), texture->getOriginalSize());
|
|
||||||
if (!image)
|
|
||||||
continue;
|
|
||||||
core::dimension2d<u32> dim = image->getDimension();
|
|
||||||
bool ok = true;
|
|
||||||
for (u16 x = 0; x < dim.Width; x++) {
|
|
||||||
for (u16 y = 0; y < dim.Height; y++) {
|
|
||||||
if (image->getPixel(x, y).getAlpha() < 255) {
|
|
||||||
ok = false;
|
|
||||||
goto break_loop;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break_loop:
|
|
||||||
image->drop();
|
|
||||||
if (ok)
|
|
||||||
continue;
|
|
||||||
warningstream << "Texture \"" << tiles[i].name << "\" of "
|
|
||||||
<< name << " has transparency, assuming "
|
|
||||||
"use_texture_alpha = \"clip\"." << std::endl;
|
|
||||||
if (!long_warning_printed) {
|
|
||||||
warningstream << " This warning can be a false-positive if "
|
|
||||||
"unused pixels in the texture are transparent. However if "
|
|
||||||
"it is meant to be transparent, you *MUST* update the "
|
|
||||||
"nodedef and set use_texture_alpha = \"clip\"! This "
|
|
||||||
"compatibility code will be removed in a few releases."
|
|
||||||
<< std::endl;
|
|
||||||
long_warning_printed = true;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
bool isWorldAligned(AlignStyle style, WorldAlignMode mode, NodeDrawType drawtype)
|
||||||
{
|
{
|
||||||
if (style == ALIGN_STYLE_WORLD)
|
if (style == ALIGN_STYLE_WORLD)
|
||||||
@ -841,11 +792,6 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc
|
|||||||
|
|
||||||
bool is_liquid = false;
|
bool is_liquid = false;
|
||||||
|
|
||||||
if (alpha == ALPHAMODE_LEGACY_COMPAT) {
|
|
||||||
// Before working with the alpha mode, resolve any legacy kludges
|
|
||||||
alpha = textureAlphaCheck(tsrc, tdef, 6) ? ALPHAMODE_CLIP : ALPHAMODE_OPAQUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
|
MaterialType material_type = alpha == ALPHAMODE_OPAQUE ?
|
||||||
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
|
TILE_MATERIAL_OPAQUE : (alpha == ALPHAMODE_CLIP ? TILE_MATERIAL_BASIC :
|
||||||
TILE_MATERIAL_ALPHA);
|
TILE_MATERIAL_ALPHA);
|
||||||
|
@ -258,7 +258,7 @@ enum AlphaMode : u8 {
|
|||||||
ALPHAMODE_BLEND,
|
ALPHAMODE_BLEND,
|
||||||
ALPHAMODE_CLIP,
|
ALPHAMODE_CLIP,
|
||||||
ALPHAMODE_OPAQUE,
|
ALPHAMODE_OPAQUE,
|
||||||
ALPHAMODE_LEGACY_COMPAT, /* means either opaque or clip */
|
ALPHAMODE_LEGACY_COMPAT, /* only sent by old servers, equals OPAQUE */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -466,11 +466,9 @@ struct ContentFeatures
|
|||||||
case NDT_NORMAL:
|
case NDT_NORMAL:
|
||||||
case NDT_LIQUID:
|
case NDT_LIQUID:
|
||||||
case NDT_FLOWINGLIQUID:
|
case NDT_FLOWINGLIQUID:
|
||||||
alpha = ALPHAMODE_OPAQUE;
|
|
||||||
break;
|
|
||||||
case NDT_NODEBOX:
|
case NDT_NODEBOX:
|
||||||
case NDT_MESH:
|
case NDT_MESH:
|
||||||
alpha = ALPHAMODE_LEGACY_COMPAT; // this should eventually be OPAQUE
|
alpha = ALPHAMODE_OPAQUE;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
alpha = ALPHAMODE_CLIP;
|
alpha = ALPHAMODE_CLIP;
|
||||||
@ -529,16 +527,6 @@ struct ContentFeatures
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef SERVER
|
|
||||||
/*
|
|
||||||
* Checks if any tile texture has any transparent pixels.
|
|
||||||
* Prints a warning and returns true if that is the case, false otherwise.
|
|
||||||
* This is supposed to be used for use_texture_alpha backwards compatibility.
|
|
||||||
*/
|
|
||||||
bool textureAlphaCheck(ITextureSource *tsrc, const TileDef *tiles,
|
|
||||||
int length);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void setAlphaFromLegacy(u8 legacy_alpha);
|
void setAlphaFromLegacy(u8 legacy_alpha);
|
||||||
|
|
||||||
u8 getAlphaForLegacy() const;
|
u8 getAlphaForLegacy() const;
|
||||||
|
@ -9,7 +9,7 @@ set (UNITTEST_SRCS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_compression.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_compression.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_connection.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_connection.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_craft.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_craft.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_filepath.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_filesys.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_irrptr.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_irrptr.cpp
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/test_lua.cpp
|
${CMAKE_CURRENT_SOURCE_DIR}/test_lua.cpp
|
||||||
|
@ -26,10 +26,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||||||
#include "nodedef.h"
|
#include "nodedef.h"
|
||||||
#include "noise.h"
|
#include "noise.h"
|
||||||
|
|
||||||
class TestFilePath : public TestBase {
|
class TestFileSys : public TestBase
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
TestFilePath() { TestManager::registerTestModule(this); }
|
TestFileSys() { TestManager::registerTestModule(this); }
|
||||||
const char *getName() { return "TestFilePath"; }
|
const char *getName() { return "TestFileSys"; }
|
||||||
|
|
||||||
void runTests(IGameDef *gamedef);
|
void runTests(IGameDef *gamedef);
|
||||||
|
|
||||||
@ -38,17 +39,19 @@ public:
|
|||||||
void testRemoveLastPathComponent();
|
void testRemoveLastPathComponent();
|
||||||
void testRemoveLastPathComponentWithTrailingDelimiter();
|
void testRemoveLastPathComponentWithTrailingDelimiter();
|
||||||
void testRemoveRelativePathComponent();
|
void testRemoveRelativePathComponent();
|
||||||
|
void testSafeWriteToFile();
|
||||||
};
|
};
|
||||||
|
|
||||||
static TestFilePath g_test_instance;
|
static TestFileSys g_test_instance;
|
||||||
|
|
||||||
void TestFilePath::runTests(IGameDef *gamedef)
|
void TestFileSys::runTests(IGameDef *gamedef)
|
||||||
{
|
{
|
||||||
TEST(testIsDirDelimiter);
|
TEST(testIsDirDelimiter);
|
||||||
TEST(testPathStartsWith);
|
TEST(testPathStartsWith);
|
||||||
TEST(testRemoveLastPathComponent);
|
TEST(testRemoveLastPathComponent);
|
||||||
TEST(testRemoveLastPathComponentWithTrailingDelimiter);
|
TEST(testRemoveLastPathComponentWithTrailingDelimiter);
|
||||||
TEST(testRemoveRelativePathComponent);
|
TEST(testRemoveRelativePathComponent);
|
||||||
|
TEST(testSafeWriteToFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -74,7 +77,7 @@ std::string p(std::string path)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TestFilePath::testIsDirDelimiter()
|
void TestFileSys::testIsDirDelimiter()
|
||||||
{
|
{
|
||||||
UASSERT(fs::IsDirDelimiter('/') == true);
|
UASSERT(fs::IsDirDelimiter('/') == true);
|
||||||
UASSERT(fs::IsDirDelimiter('A') == false);
|
UASSERT(fs::IsDirDelimiter('A') == false);
|
||||||
@ -87,7 +90,7 @@ void TestFilePath::testIsDirDelimiter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TestFilePath::testPathStartsWith()
|
void TestFileSys::testPathStartsWith()
|
||||||
{
|
{
|
||||||
const int numpaths = 12;
|
const int numpaths = 12;
|
||||||
std::string paths[numpaths] = {
|
std::string paths[numpaths] = {
|
||||||
@ -163,7 +166,7 @@ void TestFilePath::testPathStartsWith()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TestFilePath::testRemoveLastPathComponent()
|
void TestFileSys::testRemoveLastPathComponent()
|
||||||
{
|
{
|
||||||
std::string path, result, removed;
|
std::string path, result, removed;
|
||||||
|
|
||||||
@ -200,7 +203,7 @@ void TestFilePath::testRemoveLastPathComponent()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TestFilePath::testRemoveLastPathComponentWithTrailingDelimiter()
|
void TestFileSys::testRemoveLastPathComponentWithTrailingDelimiter()
|
||||||
{
|
{
|
||||||
std::string path, result, removed;
|
std::string path, result, removed;
|
||||||
|
|
||||||
@ -236,7 +239,7 @@ void TestFilePath::testRemoveLastPathComponentWithTrailingDelimiter()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void TestFilePath::testRemoveRelativePathComponent()
|
void TestFileSys::testRemoveRelativePathComponent()
|
||||||
{
|
{
|
||||||
std::string path, result;
|
std::string path, result;
|
||||||
|
|
||||||
@ -262,3 +265,15 @@ void TestFilePath::testRemoveRelativePathComponent()
|
|||||||
result = fs::RemoveRelativePathComponents(path);
|
result = fs::RemoveRelativePathComponents(path);
|
||||||
UASSERT(result == p("/a/e"));
|
UASSERT(result == p("/a/e"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void TestFileSys::testSafeWriteToFile()
|
||||||
|
{
|
||||||
|
const std::string dest_path = fs::TempPath() + DIR_DELIM + "testSafeWriteToFile.txt";
|
||||||
|
const std::string test_data("hello\0world", 11);
|
||||||
|
fs::safeWriteToFile(dest_path, test_data);
|
||||||
|
UASSERT(fs::PathExists(dest_path));
|
||||||
|
std::string contents_actual;
|
||||||
|
UASSERT(fs::ReadFile(dest_path, contents_actual));
|
||||||
|
UASSERT(contents_actual == test_data);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user