Now texture handling is fast. Also now players are saved on disk.

master
Perttu Ahola 2011-01-28 01:38:16 +02:00
parent bd100c5483
commit 64b5975732
23 changed files with 1167 additions and 656 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -271,6 +271,10 @@
RelativePath=".\src\materials.cpp"
>
</File>
<File
RelativePath=".\src\mineral.cpp"
>
</File>
<File
RelativePath=".\src\player.cpp"
>

View File

@ -38,18 +38,12 @@ configure_file(
"${PROJECT_BINARY_DIR}/cmake_config.h"
)
set(minetest_SRCS
guiMainMenu.cpp
set(common_SRCS
mineral.cpp
porting.cpp
guiMessageMenu.cpp
materials.cpp
guiTextInputMenu.cpp
guiInventoryMenu.cpp
irrlichtwrapper.cpp
guiPauseMenu.cpp
defaultsettings.cpp
mapnode.cpp
tile.cpp
voxel.cpp
mapblockobject.cpp
inventory.cpp
@ -59,7 +53,6 @@ set(minetest_SRCS
filesys.cpp
connection.cpp
environment.cpp
client.cpp
server.cpp
socket.cpp
mapblock.cpp
@ -68,34 +61,24 @@ set(minetest_SRCS
map.cpp
player.cpp
utility.cpp
main.cpp
test.cpp
)
set(minetest_SRCS
${common_SRCS}
guiMainMenu.cpp
guiMessageMenu.cpp
guiTextInputMenu.cpp
guiInventoryMenu.cpp
guiPauseMenu.cpp
irrlichtwrapper.cpp
client.cpp
main.cpp
)
set(minetestserver_SRCS
porting.cpp
materials.cpp
defaultsettings.cpp
mapnode.cpp
voxel.cpp
mapblockobject.cpp
inventory.cpp
debug.cpp
serialization.cpp
light.cpp
filesys.cpp
connection.cpp
environment.cpp
server.cpp
socket.cpp
mapblock.cpp
mapsector.cpp
heightmap.cpp
map.cpp
player.cpp
utility.cpp
${common_SRCS}
servermain.cpp
test.cpp
)
include_directories(

View File

@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/
#include "environment.h"
#include "filesys.h"
Environment::Environment(Map *map, std::ostream &dout):
m_dout(dout)
@ -192,6 +193,7 @@ void Environment::addPlayer(Player *player)
DSTACK(__FUNCTION_NAME);
/*
Check that only one local player exists and peer_ids are unique.
Also check that names are unique.
Exception: there can be multiple players with peer_id=0
*/
#ifndef SERVER
@ -201,8 +203,12 @@ void Environment::addPlayer(Player *player)
*/
assert(!(player->isLocal() == true && getLocalPlayer() != NULL));
#endif
// If peer id is non-zero, it has to be unique.
if(player->peer_id != 0)
assert(getPlayer(player->peer_id) == NULL);
// Name has to be unique.
assert(getPlayer(player->getName()) == NULL);
// Add.
m_players.push_back(player);
}
@ -300,6 +306,181 @@ void Environment::printPlayers(std::ostream &o)
}
}
void Environment::serializePlayers(const std::string &savedir)
{
std::string players_path = savedir + "/players";
fs::CreateDir(players_path);
core::map<Player*, bool> saved_players;
std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
for(u32 i=0; i<player_files.size(); i++)
{
if(player_files[i].dir)
continue;
// Full path to this file
std::string path = players_path + "/" + player_files[i].name;
dstream<<"Checking player file "<<path<<std::endl;
// Load player to see what is its name
ServerRemotePlayer testplayer;
{
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
if(is.good() == false)
{
dstream<<"Failed to read "<<path<<std::endl;
continue;
}
testplayer.deSerialize(is);
}
dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
// Search for the player
std::string playername = testplayer.getName();
Player *player = getPlayer(playername.c_str());
if(player == NULL)
{
dstream<<"Didn't find matching player, ignoring file."<<std::endl;
continue;
}
dstream<<"Found matching player, overwriting."<<std::endl;
// OK, found. Save player there.
{
// Open file and serialize
std::ofstream os(path.c_str(), std::ios_base::binary);
if(os.good() == false)
{
dstream<<"Failed to overwrite "<<path<<std::endl;
continue;
}
player->serialize(os);
saved_players.insert(player, true);
}
}
for(core::list<Player*>::Iterator i = m_players.begin();
i != m_players.end(); i++)
{
Player *player = *i;
if(saved_players.find(player) != NULL)
{
dstream<<"Player "<<player->getName()
<<" was already saved."<<std::endl;
continue;
}
std::string playername = player->getName();
// Don't save unnamed player
if(playername == "")
{
dstream<<"Not saving unnamed player."<<std::endl;
continue;
}
/*
Find a sane filename
*/
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS) == false)
playername = "player";
std::string path = players_path + "/" + playername;
bool found = false;
for(u32 i=0; i<1000; i++)
{
if(fs::PathExists(path) == false)
{
found = true;
break;
}
path = players_path + "/" + playername + itos(i);
}
if(found == false)
{
dstream<<"Didn't find free file for player"<<std::endl;
continue;
}
{
dstream<<"Saving player "<<player->getName()<<" to "
<<path<<std::endl;
// Open file and serialize
std::ofstream os(path.c_str(), std::ios_base::binary);
if(os.good() == false)
{
dstream<<"Failed to overwrite "<<path<<std::endl;
continue;
}
player->serialize(os);
saved_players.insert(player, true);
}
}
}
void Environment::deSerializePlayers(const std::string &savedir)
{
std::string players_path = savedir + "/players";
core::map<Player*, bool> saved_players;
std::vector<fs::DirListNode> player_files = fs::GetDirListing(players_path);
for(u32 i=0; i<player_files.size(); i++)
{
if(player_files[i].dir)
continue;
// Full path to this file
std::string path = players_path + "/" + player_files[i].name;
dstream<<"Checking player file "<<path<<std::endl;
// Load player to see what is its name
ServerRemotePlayer testplayer;
{
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
if(is.good() == false)
{
dstream<<"Failed to read "<<path<<std::endl;
continue;
}
testplayer.deSerialize(is);
}
dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl;
// Search for the player
std::string playername = testplayer.getName();
Player *player = getPlayer(playername.c_str());
bool newplayer = false;
if(player == NULL)
{
dstream<<"Is a new player"<<std::endl;
player = new ServerRemotePlayer();
newplayer = true;
}
// Load player
{
dstream<<"Reading player "<<testplayer.getName()<<" from "
<<path<<std::endl;
// Open file and deserialize
std::ifstream is(path.c_str(), std::ios_base::binary);
if(is.good() == false)
{
dstream<<"Failed to read "<<path<<std::endl;
continue;
}
player->deSerialize(is);
}
if(newplayer)
addPlayer(player);
}
}
#ifndef SERVER
void Environment::updateMeshes(v3s16 blockpos)
{

View File

@ -63,6 +63,10 @@ public:
core::list<Player*> getPlayers();
core::list<Player*> getPlayers(bool ignore_disconnected);
void printPlayers(std::ostream &o);
void serializePlayers(const std::string &savedir);
// This loads players as ServerRemotePlayers
void deSerializePlayers(const std::string &savedir);
#ifndef SERVER
void updateMeshes(v3s16 blockpos);

View File

@ -217,7 +217,7 @@ void InventoryList::serialize(std::ostream &os)
os<<"\n";
}
os<<"end\n";
os<<"EndInventoryList\n";
}
void InventoryList::deSerialize(std::istream &is)
@ -238,7 +238,7 @@ void InventoryList::deSerialize(std::istream &is)
std::string name;
std::getline(iss, name, ' ');
if(name == "end")
if(name == "EndInventoryList")
{
break;
}
@ -497,7 +497,7 @@ void Inventory::serialize(std::ostream &os)
list->serialize(os);
}
os<<"end\n";
os<<"EndInventory\n";
}
void Inventory::deSerialize(std::istream &is)
@ -514,7 +514,7 @@ void Inventory::deSerialize(std::istream &is)
std::string name;
std::getline(iss, name, ' ');
if(name == "end")
if(name == "EndInventory")
{
break;
}

View File

@ -122,10 +122,12 @@ public:
#ifndef SERVER
video::ITexture * getImage()
{
if(m_content >= USEFUL_CONTENT_COUNT)
/*if(m_content >= USEFUL_CONTENT_COUNT)
return NULL;
return g_irrlicht->getTexture(g_content_inventory_texture_paths[m_content]);
return g_irrlicht->getTexture(g_content_inventory_texture_paths[m_content]);*/
return g_irrlicht->getTexture(content_features(m_content).inventory_texture);
}
#endif
std::string getText()
@ -250,19 +252,19 @@ public:
#ifndef SERVER
video::ITexture * getImage()
{
std::string basename;
std::string name;
if(m_subname == "Stick")
basename = porting::getDataPath("stick.png");
name = "stick.png";
else if(m_subname == "lump_of_coal")
basename = porting::getDataPath("lump_of_coal.png");
name = "lump_of_coal.png";
else if(m_subname == "lump_of_iron")
basename = porting::getDataPath("lump_of_iron.png");
name = "lump_of_iron.png";
else
basename = porting::getDataPath("cloud.png[[mod:crack3");
name = "cloud.png";
// Get such a texture
return g_irrlicht->getTexture(basename);
return g_irrlicht->getTexture(name);
}
#endif
std::string getText()
@ -330,28 +332,35 @@ public:
{
std::string basename;
if(m_toolname == "WPick")
basename = porting::getDataPath("tool_wpick.png").c_str();
basename = "tool_wpick.png";
else if(m_toolname == "STPick")
basename = porting::getDataPath("tool_stpick.png").c_str();
basename = "tool_stpick.png";
else if(m_toolname == "MesePick")
basename = porting::getDataPath("tool_mesepick.png").c_str();
// Default to cloud texture
basename = "tool_mesepick.png";
else
basename = porting::getDataPath("cloud.png").c_str();
//basename = tile_texture_path_get(TILE_CLOUD);
basename = "cloud.png";
/*
Calculate some progress value with sane amount of
Calculate a progress value with sane amount of
maximum states
*/
u32 maxprogress = 30;
u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
// Make texture name for the new texture with a progress bar
float value_f = (float)toolprogress / (float)maxprogress;
std::ostringstream os;
os<<"[progressbar"<<value_f;
TextureSpec spec;
spec.addTid(g_irrlicht->getTextureId(basename));
spec.addTid(g_irrlicht->getTextureId(os.str()));
return g_irrlicht->getTexture(spec);
/*// Make texture name for the new texture with a progress bar
float value_f = (float)toolprogress / (float)maxprogress;
std::ostringstream os;
os<<basename<<"[[mod:progressbar"<<value_f;
return g_irrlicht->getTexture(os.str());
return g_irrlicht->getTexture(os.str());*/
/*// Make texture name for the new texture with a progress bar
std::ostringstream os;

View File

@ -17,13 +17,15 @@ void IrrlichtWrapper::Run()
*/
if(m_get_texture_queue.size() > 0)
{
GetRequest<std::string, video::ITexture*, u8, u8>
GetRequest<TextureSpec, video::ITexture*, u8, u8>
request = m_get_texture_queue.pop();
dstream<<"got texture request with key="
<<request.key<<std::endl;
dstream<<"got texture request with"
<<" key.tids[0]="<<request.key.tids[0]
<<" [1]="<<request.key.tids[1]
<<std::endl;
GetResult<std::string, video::ITexture*, u8, u8>
GetResult<TextureSpec, video::ITexture*, u8, u8>
result;
result.key = request.key;
result.callers = request.callers;
@ -33,9 +35,29 @@ void IrrlichtWrapper::Run()
}
}
video::ITexture* IrrlichtWrapper::getTexture(const std::string &spec)
textureid_t IrrlichtWrapper::getTextureId(const std::string &name)
{
if(spec == "")
u32 id = m_namecache.getId(name);
return id;
}
std::string IrrlichtWrapper::getTextureName(textureid_t id)
{
std::string name("");
m_namecache.getValue(id, name);
// In case it was found, return the name; otherwise return an empty name.
return name;
}
video::ITexture* IrrlichtWrapper::getTexture(const std::string &name)
{
TextureSpec spec(getTextureId(name));
return getTexture(spec);
}
video::ITexture* IrrlichtWrapper::getTexture(const TextureSpec &spec)
{
if(spec.empty())
return NULL;
video::ITexture *t = m_texturecache.get(spec);
@ -44,26 +66,26 @@ video::ITexture* IrrlichtWrapper::getTexture(const std::string &spec)
if(get_current_thread_id() == m_main_thread)
{
dstream<<"Getting texture directly: spec="
<<spec<<std::endl;
dstream<<"Getting texture directly: spec.tids[0]="
<<spec.tids[0]<<std::endl;
t = getTextureDirect(spec);
}
else
{
// We're gonna ask the result to be put into here
ResultQueue<std::string, video::ITexture*, u8, u8> result_queue;
ResultQueue<TextureSpec, video::ITexture*, u8, u8> result_queue;
// Throw a request in
m_get_texture_queue.add(spec, 0, 0, &result_queue);
dstream<<"Waiting for texture from main thread: "
<<spec<<std::endl;
dstream<<"Waiting for texture from main thread: spec.tids[0]="
<<spec.tids[0]<<std::endl;
try
{
// Wait result for a second
GetResult<std::string, video::ITexture*, u8, u8>
GetResult<TextureSpec, video::ITexture*, u8, u8>
result = result_queue.pop_front(1000);
// Check that at least something worked OK
@ -83,144 +105,177 @@ video::ITexture* IrrlichtWrapper::getTexture(const std::string &spec)
return t;
}
/*
Non-thread-safe functions
*/
// Draw a progress bar on the image
void make_progressbar(float value, video::IImage *image);
/*
Texture modifier functions
Texture fetcher/maker function, called always from the main thread
*/
// blitted_name = eg. "mineral_coal.png"
video::ITexture * make_blitname(const std::string &blitted_name,
video::ITexture *original,
const char *newname, video::IVideoDriver* driver)
video::ITexture* IrrlichtWrapper::getTextureDirect(const TextureSpec &spec)
{
if(original == NULL)
// This would result in NULL image
if(spec.empty())
return NULL;
// Size of the base image
core::dimension2d<u32> dim(16, 16);
// Position to copy the blitted to in the base image
core::position2d<s32> pos_base(0, 0);
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_other(0, 0);
// Don't generate existing stuff
video::ITexture *t = m_texturecache.get(spec);
if(t != NULL)
{
dstream<<"WARNING: Existing stuff requested from "
"getTextureDirect()"<<std::endl;
return t;
}
video::IVideoDriver* driver = m_device->getVideoDriver();
video::IImage *baseimage = driver->createImage(original, pos_base, dim);
assert(baseimage);
/*
An image will be built from files and then converted into a texture.
*/
video::IImage *baseimg = NULL;
video::IImage *blittedimage = driver->createImageFromFile(porting::getDataPath(blitted_name.c_str()).c_str());
assert(blittedimage);
/*
Irrlicht requires a name for every texture, with which it
will be stored internally in irrlicht.
*/
std::string texture_name;
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
textureid_t tid = spec.tids[i];
if(tid == 0)
continue;
std::string name = getTextureName(tid);
// Add something to the name so that it is a unique identifier.
texture_name += "[";
texture_name += name;
texture_name += "]";
if(name[0] != '[')
{
// A normal texture; load it from a file
std::string path = porting::getDataPath(name.c_str());
dstream<<"getTextureDirect(): Loading path \""<<path
<<"\""<<std::endl;
video::IImage *image = driver->createImageFromFile(path.c_str());
if(image == NULL)
{
dstream<<"WARNING: Could not load image \""<<name
<<"\" from path \""<<path<<"\""
<<" while building texture"<<std::endl;
continue;
}
// If base image is NULL, load as base.
if(baseimg == NULL)
{
dstream<<"Setting "<<name<<" as base"<<std::endl;
/*
Copy it this way to get an alpha channel.
Otherwise images with alpha cannot be blitted on
images that don't have alpha in the original file.
*/
// This is a deprecated method
//baseimg = driver->createImage(video::ECF_A8R8G8B8, image);
core::dimension2d<u32> dim = image->getDimension();
baseimg = driver->createImage(video::ECF_A8R8G8B8, dim);
image->copyTo(baseimg);
image->drop();
//baseimg = image;
}
// Else blit on base.
else
{
dstream<<"Blitting "<<name<<" on base"<<std::endl;
// Size of the copied area
core::dimension2d<u32> dim = image->getDimension();
//core::dimension2d<u32> dim(16,16);
// Position to copy the blitted to in the base image
core::position2d<s32> pos_to(0,0);
// Position to copy the blitted from in the blitted image
core::position2d<s32> pos_from(0,0);
// Blit
image->copyToWithAlpha(baseimg, pos_to,
core::rect<s32>(pos_from, dim),
video::SColor(255,255,255,255),
NULL);
// Drop image
image->drop();
}
}
else
{
// A special texture modification
dstream<<"getTextureDirect(): generating \""<<name<<"\""
<<std::endl;
if(name.substr(0,6) == "[crack")
{
u16 progression = stoi(name.substr(6));
// Size of the base image
core::dimension2d<u32> dim(16, 16);
// Size of the crack image
//core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(0, 0);
// Position to copy the crack from in the crack image
core::position2d<s32> pos_other(0, 16 * progression);
video::IImage *crackimage = driver->createImageFromFile(
porting::getDataPath("crack.png").c_str());
crackimage->copyToWithAlpha(baseimg, v2s32(0,0),
core::rect<s32>(pos_other, dim),
video::SColor(255,255,255,255),
NULL);
crackimage->drop();
}
else if(name.substr(0,12) == "[progressbar")
{
float value = stof(name.substr(12));
make_progressbar(value, baseimg);
}
else
{
dstream<<"WARNING: getTextureDirect(): Invalid "
" texture: \""<<name<<"\""<<std::endl;
}
}
}
// If no resulting image, return NULL
if(baseimg == NULL)
{
dstream<<"getTextureDirect(): baseimg is NULL (attempted to"
" create texture \""<<texture_name<<"\""<<std::endl;
return NULL;
}
// Then copy the right part of blittedimage to baseimage
blittedimage->copyToWithAlpha(baseimage, v2s32(0,0),
core::rect<s32>(pos_other, dim),
video::SColor(255,255,255,255),
NULL);
blittedimage->drop();
/*// DEBUG: Paint some pixels
video::SColor c(255,255,0,0);
baseimg->setPixel(1,1, c);
baseimg->setPixel(1,14, c);
baseimg->setPixel(14,1, c);
baseimg->setPixel(14,14, c);*/
// Create texture from resulting image
t = driver->addTexture(texture_name.c_str(), baseimg);
baseimg->drop();
video::ITexture *newtexture = driver->addTexture(newname, baseimage);
dstream<<"getTextureDirect(): created texture \""<<texture_name
<<"\""<<std::endl;
baseimage->drop();
return t;
return newtexture;
}
video::ITexture * make_crack(u16 progression, video::ITexture *original,
const char *newname, video::IVideoDriver* driver)
void make_progressbar(float value, video::IImage *image)
{
if(original == NULL)
return NULL;
if(image == NULL)
return;
// Size of the base image
core::dimension2d<u32> dim(16, 16);
// Size of the crack image
//core::dimension2d<u32> dim_crack(16, 16 * CRACK_ANIMATION_LENGTH);
// Position to copy the crack to in the base image
core::position2d<s32> pos_base(0, 0);
// Position to copy the crack from in the crack image
core::position2d<s32> pos_other(0, 16 * progression);
video::IImage *baseimage = driver->createImage(original, pos_base, dim);
assert(baseimage);
video::IImage *crackimage = driver->createImageFromFile(porting::getDataPath("crack.png").c_str());
assert(crackimage);
// Then copy the right part of crackimage to baseimage
crackimage->copyToWithAlpha(baseimage, v2s32(0,0),
core::rect<s32>(pos_other, dim),
video::SColor(255,255,255,255),
NULL);
crackimage->drop();
// Create texture from resulting image
video::ITexture *newtexture = driver->addTexture(newname, baseimage);
baseimage->drop();
return newtexture;
}
#if 0
video::ITexture * make_sidegrass(video::ITexture *original,
const char *newname, video::IVideoDriver* driver)
{
if(original == NULL)
return NULL;
// Size of the base image
core::dimension2d<u32> dim(16, 16);
// Position to copy the grass to in the base image
core::position2d<s32> pos_base(0, 0);
// Position to copy the grass from in the grass image
core::position2d<s32> pos_other(0, 0);
video::IImage *baseimage = driver->createImage(original, pos_base, dim);
assert(baseimage);
video::IImage *grassimage = driver->createImageFromFile(porting::getDataPath("grass_side.png").c_str());
assert(grassimage);
// Then copy the right part of grassimage to baseimage
grassimage->copyToWithAlpha(baseimage, v2s32(0,0),
core::rect<s32>(pos_other, dim),
video::SColor(255,255,255,255),
NULL);
grassimage->drop();
// Create texture from resulting image
video::ITexture *newtexture = driver->addTexture(newname, baseimage);
baseimage->drop();
return newtexture;
}
#endif
video::ITexture * make_progressbar(float value, video::ITexture *original,
const char *newname, video::IVideoDriver* driver)
{
if(original == NULL)
return NULL;
core::position2d<s32> pos_base(0, 0);
core::dimension2d<u32> dim = original->getOriginalSize();
video::IImage *baseimage = driver->createImage(original, pos_base, dim);
assert(baseimage);
core::dimension2d<u32> size = baseimage->getDimension();
core::dimension2d<u32> size = image->getDimension();
u32 barheight = 1;
u32 barpad_x = 1;
@ -242,177 +297,9 @@ video::ITexture * make_progressbar(float value, video::ITexture *original,
u32 x = x0 + barpos.X;
for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
{
baseimage->setPixel(x,y, *c);
image->setPixel(x,y, *c);
}
}
video::ITexture *newtexture = driver->addTexture(newname, baseimage);
baseimage->drop();
return newtexture;
}
/*
Texture fetcher/maker function, called always from the main thread
*/
video::ITexture* IrrlichtWrapper::getTextureDirect(const std::string &spec)
{
if(spec == "")
return NULL;
video::IVideoDriver* driver = m_device->getVideoDriver();
/*
Input (spec) is something like this:
"/usr/share/minetest/stone.png[[mod:mineral0[[mod:crack3"
*/
video::ITexture* t = NULL;
std::string modmagic = "[[mod:";
Strfnd f(spec);
std::string path = f.next(modmagic);
t = driver->getTexture(path.c_str());
std::string texture_name = path;
while(f.atend() == false)
{
std::string mod = f.next(modmagic);
texture_name += modmagic + mod;
dstream<<"Making texture \""<<texture_name<<"\""<<std::endl;
/*if(mod == "sidegrass")
{
t = make_sidegrass(t, texture_name.c_str(), driver);
}
else*/
if(mod.substr(0, 9) == "blitname:")
{
//t = make_sidegrass(t, texture_name.c_str(), driver);
t = make_blitname(mod.substr(9), t, texture_name.c_str(), driver);
}
else if(mod.substr(0,5) == "crack")
{
u16 prog = stoi(mod.substr(5));
t = make_crack(prog, t, texture_name.c_str(), driver);
}
else if(mod.substr(0,11) == "progressbar")
{
float value = stof(mod.substr(11));
t = make_progressbar(value, t, texture_name.c_str(), driver);
}
else
{
dstream<<"Invalid texture mod: \""<<mod<<"\""<<std::endl;
}
}
return t;
#if 0
video::ITexture* t = NULL;
const char *modmagic = "[[mod:";
const s32 modmagic_len = 6;
enum{
READMODE_PATH,
READMODE_MOD
} readmode = READMODE_PATH;
s32 specsize = spec.size()+1;
char *strcache = (char*)malloc(specsize);
assert(strcache);
char *path = NULL;
s32 length = 0;
// Next index of modmagic to be found
s32 modmagic_i = 0;
u32 i=0;
for(;;)
{
strcache[length++] = spec[i];
bool got_modmagic = false;
/*
Check modmagic
*/
if(spec[i] == modmagic[modmagic_i])
{
modmagic_i++;
if(modmagic_i == modmagic_len)
{
got_modmagic = true;
modmagic_i = 0;
length -= modmagic_len;
}
}
else
modmagic_i = 0;
// Set i to be the length of read string
i++;
if(got_modmagic || i >= spec.size())
{
strcache[length] = '\0';
// Now our string is in strcache, ending in \0
if(readmode == READMODE_PATH)
{
// Get initial texture (strcache is path)
assert(t == NULL);
t = driver->getTexture(strcache);
readmode = READMODE_MOD;
path = strcache;
strcache = (char*)malloc(specsize);
assert(strcache);
}
else
{
dstream<<"Parsing mod \""<<strcache<<"\""<<std::endl;
// The name of the result of adding this mod.
// This doesn't have to be fast so std::string is used.
std::string name(path);
name += "[[mod:";
name += strcache;
dstream<<"Name of modded texture is \""<<name<<"\""
<<std::endl;
// Sidegrass
if(strcmp(strcache, "sidegrass") == 0)
{
t = make_sidegrass(t, name.c_str(), driver);
}
else
{
dstream<<"Invalid texture mod"<<std::endl;
}
}
length = 0;
}
if(i >= spec.size())
break;
}
/*if(spec.mod == NULL)
{
dstream<<"IrrlichtWrapper::getTextureDirect: Loading texture "
<<spec.path<<std::endl;
return driver->getTexture(spec.path.c_str());
}
dstream<<"IrrlichtWrapper::getTextureDirect: Loading and modifying "
"texture "<<spec.path<<" to make "<<spec.name<<std::endl;
video::ITexture *base = driver->getTexture(spec.path.c_str());
video::ITexture *result = spec.mod->make(base, spec.name.c_str(), driver);
delete spec.mod;*/
if(strcache)
free(strcache);
if(path)
free(path);
return t;
#endif
}

View File

@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h"
#include "debug.h"
#include "utility.h"
#include "texture.h"
#include <jmutex.h>
#include <jmutexautolock.h>
@ -36,7 +37,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
threads, because texture pointers have to be handled in
background threads.
*/
#if 0
class TextureCache
{
public:
@ -73,12 +74,55 @@ private:
core::map<std::string, video::ITexture*> m_textures;
JMutex m_mutex;
};
#endif
/*
A thread-safe texture pointer cache
*/
class TextureCache
{
public:
TextureCache()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
void set(const TextureSpec &spec, video::ITexture *texture)
{
if(texture == NULL)
return;
JMutexAutoLock lock(m_mutex);
m_textures[spec] = texture;
}
video::ITexture* get(const TextureSpec &spec)
{
JMutexAutoLock lock(m_mutex);
core::map<TextureSpec, video::ITexture*>::Node *n;
n = m_textures.find(spec);
if(n != NULL)
return n->getValue();
return NULL;
}
private:
core::map<TextureSpec, video::ITexture*> m_textures;
JMutex m_mutex;
};
/*
A thread-safe wrapper for irrlicht, to be accessed from
background worker threads.
Queues tasks to be done in the main thread.
Also caches texture specification strings to ids and textures.
*/
class IrrlichtWrapper
@ -103,30 +147,55 @@ public:
return m_device->getTimer()->getRealTime();
}
/*
Path can contain stuff like
"/usr/share/minetest/stone.png[[mod:mineral0[[mod:crack3"
/*
Format of a texture name:
"stone.png" (filename in image data directory)
"[crack1" (a name starting with "[" is a special feature)
"[progress1.0" (a name starting with "[" is a special feature)
*/
video::ITexture* getTexture(const std::string &spec);
/*
Loads texture defined by "name" and assigns a texture id to it.
If texture has to be generated, generates it.
If the texture has already been loaded, returns existing id.
*/
textureid_t getTextureId(const std::string &name);
// The reverse of the above
std::string getTextureName(textureid_t id);
// Gets a texture based on a filename
video::ITexture* getTexture(const std::string &name);
// Gets a texture based on a TextureSpec (a textureid_t is fine too)
video::ITexture* getTexture(const TextureSpec &spec);
private:
/*
Non-thread-safe variants of stuff, for internal use
*/
video::ITexture* getTextureDirect(const std::string &spec);
// DEPRECATED NO-OP
//video::ITexture* getTextureDirect(const std::string &spec);
// Constructs a texture according to spec
video::ITexture* getTextureDirect(const TextureSpec &spec);
/*
Members
*/
// The id of the thread that can (and has to) use irrlicht directly
threadid_t m_main_thread;
// The irrlicht device
JMutex m_device_mutex;
IrrlichtDevice *m_device;
TextureCache m_texturecache;
RequestQueue<std::string, video::ITexture*, u8, u8> m_get_texture_queue;
// Queued texture fetches (to be processed by the main thread)
RequestQueue<TextureSpec, video::ITexture*, u8, u8> m_get_texture_queue;
// Cache of textures by spec
TextureCache m_texturecache;
// A mapping from texture id to string spec
MutexedIdGenerator<std::string> m_namecache;
};
#endif

View File

@ -104,8 +104,11 @@ SUGG: Meshes of blocks could be split into 6 meshes facing into
Gaming ideas:
-------------
- Aim for something like controlling a single dwarf in Dwarf Fortress.
- Aim for something like controlling a single dwarf in Dwarf Fortress
- The player could go faster by a crafting a boat, or riding an animal
- Random NPC traders. what else?
Documentation:
--------------
@ -165,6 +168,20 @@ TODO: Make fetching sector's blocks more efficient when rendering
TODO: Flowing water animation
FIXME: The new texture stuff is slow on wine
- A basic grassy ground block takes 20-40ms
- A bit more complicated block can take 270ms
- On linux, a similar one doesn't take long at all (14ms)
- It is NOT a bad std::string implementation of MSVC.
- Can take up to 200ms? Is it when loading textures or always?
- Updating excess amount of meshes when making footprints is too
slow. It has to be fixed.
-> implement Map::updateNodeMeshes()
The fix:
* Optimize TileSpec to only contain a reference number that
is fast to compare, which refers to a cached string, or
* Make TextureSpec for using instead of strings
Configuration:
--------------
@ -281,18 +298,6 @@ TODO: Flowing water to actually contain flow direction information
TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator, which is faster)
FIXME: The new texture stuff is slow on wine
- A basic grassy ground block takes 20-40ms
- A bit more complicated block can take 270ms
- On linux, a similar one doesn't take long at all (14ms)
- Is it a bad std::string implementation of MSVC?
- Can take up to 200ms? Is it when loading textures or always?
- Updating excess amount of meshes when making footprints is too
slow. It has to be fixed.
-> implement Map::updateNodeMeshes()
TODO: Optimize TileSpec to only contain a reference number that
is fast to compare, which refers to a cached string
Doing now:
----------
@ -360,6 +365,7 @@ Doing now:
#include "filesys.h"
#include "config.h"
#include "guiMainMenu.h"
#include "mineral.h"
IrrlichtWrapper *g_irrlicht;
@ -1445,7 +1451,6 @@ int main(int argc, char *argv[])
// C-style stuff initialization
initializeMaterialProperties();
init_mapnode();
// Debug handler
BEGIN_DEBUG_EXCEPTION_HANDLER
@ -1683,7 +1688,8 @@ int main(int argc, char *argv[])
*/
init_content_inventory_texture_paths();
//init_tile_textures();
init_mapnode(g_irrlicht);
init_mineral(g_irrlicht);
/*
GUI stuff
@ -2378,7 +2384,7 @@ int main(int argc, char *argv[])
bool nodefound = false;
v3s16 nodepos;
v3s16 neighbourpos;
core::aabbox3d<f32> nodefacebox;
core::aabbox3d<f32> nodehilightbox;
f32 mindistance = BS * 1001;
v3s16 pos_i = floatToInt(player_position);
@ -2470,7 +2476,7 @@ int main(int argc, char *argv[])
nodepos = np;
neighbourpos = np;
mindistance = distance;
nodefacebox = box;
nodehilightbox = box;
}
}
}
@ -2513,7 +2519,16 @@ int main(int argc, char *argv[])
nodepos = np;
neighbourpos = np + dirs[i];
mindistance = distance;
nodefacebox = facebox;
//nodehilightbox = facebox;
const float d = 0.502;
core::aabbox3d<f32> nodebox
(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
v3f nodepos_f = intToFloat(nodepos);
nodebox.MinEdge += nodepos_f;
nodebox.MaxEdge += nodepos_f;
nodehilightbox = nodebox;
}
} // if distance < mindistance
} // for dirs
@ -2531,15 +2546,7 @@ int main(int argc, char *argv[])
// Visualize selection
const float d = 0.502;
core::aabbox3d<f32> nodebox(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);
v3f nodepos_f = intToFloat(nodepos);
//v3f nodepos_f(nodepos.X*BS, nodepos.Y*BS, nodepos.Z*BS);
nodebox.MinEdge += nodepos_f;
nodebox.MaxEdge += nodepos_f;
hilightboxes.push_back(nodebox);
//hilightboxes.push_back(nodefacebox);
hilightboxes.push_back(nodehilightbox);
// Handle digging

View File

@ -263,6 +263,7 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
//u8 li = decode_light(light);
u8 li = light;
//u8 li = 255; //DEBUG
u8 alpha = tile.alpha;
/*u8 alpha = 255;
@ -309,15 +310,16 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
struct NodeMod mod = n->getValue();
if(mod.type == NODEMOD_CHANGECONTENT)
{
//spec = content_tile(mod.param, face_dir);
MapNode mn2(mod.param);
spec = mn2.getTile(face_dir);
}
if(mod.type == NODEMOD_CRACK)
{
std::ostringstream os;
os<<"[[mod:crack"<<mod.param;
spec.name += os.str();
os<<"[crack"<<mod.param;
textureid_t tid = g_irrlicht->getTextureId(os.str());
spec.spec.addTid(tid);
}
}
@ -601,7 +603,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
*/
{
TimeTaker timer2("updateMesh() collect");
// 4-23ms for MAP_BLOCKSIZE=16
//TimeTaker timer2("updateMesh() collect");
// Lock this, as m_temp_mods will be used directly
JMutexAutoLock lock(m_temp_mods_mutex);
@ -667,22 +670,25 @@ void MapBlock::updateMesh(u32 daynight_ratio)
// avg 0ms (100ms spikes when loading textures the first time)
//TimeTaker timer2("updateMesh() mesh building");
video::SMaterial material;
material.Lighting = false;
material.BackfaceCulling = false;
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
material.setFlag(video::EMF_FOG_ENABLE, true);
for(u32 i=0; i<fastfaces_new.size(); i++)
{
FastFace &f = fastfaces_new[i];
const u16 indices[] = {0,1,2,2,3,0};
video::ITexture *texture = g_irrlicht->getTexture(f.tile.name);
video::SMaterial material;
material.Lighting = false;
material.BackfaceCulling = false;
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
material.setFlag(video::EMF_FOG_ENABLE, true);
video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
material.setTexture(0, texture);
if(f.tile.alpha != 255)
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
else
material.MaterialType = video::EMT_SOLID;
collector.append(material, f.vertices, 4, indices, 6);
}
@ -691,13 +697,22 @@ void MapBlock::updateMesh(u32 daynight_ratio)
/*
Add special graphics:
- torches
TODO: Optimize by using same meshbuffer for same textures
- flowing water
*/
// 0ms
//TimeTaker timer2("updateMesh() adding special stuff");
// Flowing water material
video::SMaterial material_w1;
material_w1.setFlag(video::EMF_LIGHTING, false);
material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
material_w1.setFlag(video::EMF_FOG_ENABLE, true);
material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
material_w1.setTexture(0,
g_irrlicht->getTexture("water.png"));
for(s16 z=0; z<MAP_BLOCKSIZE; z++)
for(s16 y=0; y<MAP_BLOCKSIZE; y++)
for(s16 x=0; x<MAP_BLOCKSIZE; x++)
@ -751,17 +766,17 @@ void MapBlock::updateMesh(u32 daynight_ratio)
= video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
if(dir == v3s16(0,-1,0))
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
g_irrlicht->getTexture("torch_on_floor.png"));
else if(dir == v3s16(0,1,0))
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_ceiling.png").c_str()));
g_irrlicht->getTexture("torch_on_ceiling.png"));
// For backwards compatibility
else if(dir == v3s16(0,0,0))
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch_on_floor.png").c_str()));
g_irrlicht->getTexture("torch_on_floor.png"));
else
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("torch.png").c_str()));
g_irrlicht->getTexture("torch.png"));
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
@ -947,19 +962,9 @@ void MapBlock::updateMesh(u32 daynight_ratio)
vertices[j].Pos += intToFloat(p + getPosRelative());
}
// Set material
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_FOG_ENABLE, true);
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
collector.append(material_w1, vertices, 4, indices, 6);
}
/*
@ -984,19 +989,9 @@ void MapBlock::updateMesh(u32 daynight_ratio)
vertices[i].Pos += intToFloat(p + getPosRelative());
}
// Set material
video::SMaterial material;
material.setFlag(video::EMF_LIGHTING, false);
material.setFlag(video::EMF_BACK_FACE_CULLING, false);
material.setFlag(video::EMF_BILINEAR_FILTER, false);
material.setFlag(video::EMF_FOG_ENABLE, true);
material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
material.setTexture(0,
g_irrlicht->getTexture(porting::getDataPath("water.png").c_str()));
u16 indices[] = {0,1,2,2,3,0};
// Add to mesh collector
collector.append(material, vertices, 4, indices, 6);
collector.append(material_w1, vertices, 4, indices, 6);
}
}
}

View File

@ -31,80 +31,85 @@ ContentFeatures::~ContentFeatures()
struct ContentFeatures g_content_features[256];
void init_mapnode()
ContentFeatures & content_features(u8 i)
{
return g_content_features[i];
}
void init_mapnode(IrrlichtWrapper *irrlicht)
{
u8 i;
ContentFeatures *f = NULL;
i = CONTENT_STONE;
f = &g_content_features[i];
f->setAllTextures("stone.png");
f->setAllTextures(irrlicht->getTextureId("stone.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_GRASS;
f = &g_content_features[i];
//f->setAllTextures("mud.png[[mod:sidegrass");
f->setAllTextures("mud.png[[mod:blitname:grass_side.png");
f->setTexture(0, "grass.png");
f->setTexture(1, "mud.png");
f->setInventoryImage("grass.png");
f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"),
irrlicht->getTextureId("grass_side.png")));
f->setTexture(0, irrlicht->getTextureId("grass.png"));
f->setTexture(1, irrlicht->getTextureId("mud.png"));
f->setInventoryTexture(irrlicht->getTextureId("grass.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_GRASS_FOOTSTEPS;
f = &g_content_features[i];
//f->setAllTextures("mud.png[[mod:sidegrass");
f->setAllTextures("mud.png[[mod:blitname:grass_side.png");
f->setTexture(0, "grass_footsteps.png");
f->setTexture(1, "mud.png");
f->setInventoryImage("grass_footsteps.png");
f->setAllTextures(TextureSpec(irrlicht->getTextureId("mud.png"),
irrlicht->getTextureId("grass_side.png")));
f->setTexture(0, irrlicht->getTextureId("grass_footsteps.png"));
f->setTexture(1, irrlicht->getTextureId("mud.png"));
f->setInventoryTexture(irrlicht->getTextureId("grass_footsteps.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_MUD;
f = &g_content_features[i];
f->setAllTextures("mud.png");
f->setAllTextures(irrlicht->getTextureId("mud.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_SAND;
f = &g_content_features[i];
f->setAllTextures("mud.png");
f->setAllTextures(irrlicht->getTextureId("mud.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_TREE;
f = &g_content_features[i];
f->setAllTextures("tree.png");
f->setAllTextures(irrlicht->getTextureId("tree.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_LEAVES;
f = &g_content_features[i];
f->setAllTextures("leaves.png");
f->setAllTextures(irrlicht->getTextureId("leaves.png"));
f->param_type = CPT_MINERAL;
f->is_ground_content = true;
i = CONTENT_COALSTONE;
f = &g_content_features[i];
f->translate_to = new MapNode(CONTENT_STONE, MINERAL_COAL);
/*f->setAllTextures("coalstone.png");
/*f->setAllTextures(irrlicht->getTextureId("coalstone.png"));
f->is_ground_content = true;*/
i = CONTENT_WOOD;
f = &g_content_features[i];
f->setAllTextures("wood.png");
f->setAllTextures(irrlicht->getTextureId("wood.png"));
f->is_ground_content = true;
i = CONTENT_MESE;
f = &g_content_features[i];
f->setAllTextures("mese.png");
f->setAllTextures(irrlicht->getTextureId("mese.png"));
f->is_ground_content = true;
i = CONTENT_CLOUD;
f = &g_content_features[i];
f->setAllTextures("cloud.png");
f->setAllTextures(irrlicht->getTextureId("cloud.png"));
f->is_ground_content = true;
i = CONTENT_AIR;
@ -120,7 +125,7 @@ void init_mapnode()
i = CONTENT_WATER;
f = &g_content_features[i];
f->setInventoryImage("water.png");
f->setInventoryTexture(irrlicht->getTextureId("water.png"));
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->solidness = 0; // Drawn separately, makes no faces
@ -132,8 +137,8 @@ void init_mapnode()
i = CONTENT_WATERSOURCE;
f = &g_content_features[i];
f->setTexture(0, "water.png", WATER_ALPHA);
f->setInventoryImage("water.png");
f->setTexture(0, irrlicht->getTextureId("water.png"), WATER_ALPHA);
f->setInventoryTexture(irrlicht->getTextureId("water.png"));
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->solidness = 1;
@ -145,7 +150,7 @@ void init_mapnode()
i = CONTENT_TORCH;
f = &g_content_features[i];
f->setInventoryImage("torch_on_floor.png");
f->setInventoryTexture(irrlicht->getTextureId("torch_on_floor.png"));
f->param_type = CPT_LIGHT;
f->light_propagates = true;
f->solidness = 0; // drawn separately, makes no faces
@ -184,12 +189,10 @@ TileSpec MapNode::getTile(v3s16 dir)
if(content_features(d).param_type == CPT_MINERAL)
{
u8 mineral = param & 0x1f;
const char *ts = mineral_block_texture(mineral);
if(ts[0] != 0)
{
spec.name += "[[mod:blitname:";
spec.name += ts;
}
// Add mineral block texture
textureid_t tid = mineral_block_texture(mineral);
if(tid != 0)
spec.spec.addTid(tid);
}
return spec;
@ -206,14 +209,15 @@ u8 MapNode::getMineral()
}
// Pointers to c_str()s g_content_features[i].inventory_image_path
const char * g_content_inventory_texture_paths[USEFUL_CONTENT_COUNT] = {0};
//const char * g_content_inventory_texture_paths[USEFUL_CONTENT_COUNT] = {0};
void init_content_inventory_texture_paths()
{
for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
dstream<<"DEPRECATED "<<__FUNCTION_NAME<<std::endl;
/*for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
{
g_content_inventory_texture_paths[i] =
g_content_features[i].inventory_image_path.c_str();
}
}*/
}

View File

@ -27,11 +27,15 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "exceptions.h"
#include "serialization.h"
#include "tile.h"
#include "irrlichtwrapper.h"
// Initializes all kind of stuff in here.
// Doesn't depend on anything else.
// Many things depend on this.
void init_mapnode();
/*
Initializes all kind of stuff in here.
Many things depend on this.
irrlicht: Used for getting texture ids.
*/
void init_mapnode(IrrlichtWrapper *irrlicht);
// Initializes g_content_inventory_texture_paths
void init_content_inventory_texture_paths();
@ -129,7 +133,8 @@ struct ContentFeatures
*/
TileSpec tiles[6];
std::string inventory_image_path;
//std::string inventory_image_path;
TextureSpec inventory_texture;
bool is_ground_content; //TODO: Remove, use walkable instead
bool light_propagates;
@ -162,39 +167,42 @@ struct ContentFeatures
~ContentFeatures();
void setAllTextures(std::string imgname, u8 alpha=255)
void setAllTextures(const TextureSpec &spec, u8 alpha=255)
{
for(u16 i=0; i<6; i++)
{
tiles[i].name = porting::getDataPath(imgname.c_str());
tiles[i].spec = spec;
tiles[i].alpha = alpha;
}
// Set this too so it can be left as is most times
if(inventory_image_path == "")
inventory_image_path = porting::getDataPath(imgname.c_str());
/*if(inventory_image_path == "")
inventory_image_path = porting::getDataPath(imgname.c_str());*/
if(inventory_texture.empty())
inventory_texture = spec;
}
void setTexture(u16 i, std::string imgname, u8 alpha=255)
void setTexture(u16 i, const TextureSpec &spec, u8 alpha=255)
{
tiles[i].name = porting::getDataPath(imgname.c_str());
tiles[i].spec = spec;
tiles[i].alpha = alpha;
}
void setInventoryImage(std::string imgname)
void setInventoryTexture(const TextureSpec &spec)
{
inventory_texture = spec;
}
/*void setInventoryImage(std::string imgname)
{
inventory_image_path = porting::getDataPath(imgname.c_str());
}
}*/
};
// Initialized by init_mapnode()
extern struct ContentFeatures g_content_features[256];
inline ContentFeatures & content_features(u8 i)
{
return g_content_features[i];
}
extern const char * g_content_inventory_texture_paths[USEFUL_CONTENT_COUNT];
/*
Call this to access the ContentFeature list
*/
ContentFeatures & content_features(u8 i);
/*
If true, the material allows light propagation and brightness is stored
@ -203,7 +211,7 @@ extern const char * g_content_inventory_texture_paths[USEFUL_CONTENT_COUNT];
*/
inline bool light_propagates_content(u8 m)
{
return g_content_features[m].light_propagates;
return content_features(m).light_propagates;
//return (m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
}
@ -214,7 +222,7 @@ inline bool light_propagates_content(u8 m)
*/
inline bool sunlight_propagates_content(u8 m)
{
return g_content_features[m].sunlight_propagates;
return content_features(m).sunlight_propagates;
//return (m == CONTENT_AIR || m == CONTENT_TORCH);
}
@ -228,7 +236,7 @@ inline bool sunlight_propagates_content(u8 m)
*/
inline u8 content_solidness(u8 m)
{
return g_content_features[m].solidness;
return content_features(m).solidness;
/*// As of now, every pseudo node like torches are added to this
if(m == CONTENT_AIR || m == CONTENT_TORCH || m == CONTENT_WATER)
return 0;
@ -241,28 +249,28 @@ inline u8 content_solidness(u8 m)
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_walkable(u8 m)
{
return g_content_features[m].walkable;
return content_features(m).walkable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE && m != CONTENT_TORCH);
}
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_liquid(u8 m)
{
return g_content_features[m].liquid_type != LIQUID_NONE;
return content_features(m).liquid_type != LIQUID_NONE;
//return (m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
}
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_flowing_liquid(u8 m)
{
return g_content_features[m].liquid_type == LIQUID_FLOWING;
return content_features(m).liquid_type == LIQUID_FLOWING;
//return (m == CONTENT_WATER);
}
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_liquid_source(u8 m)
{
return g_content_features[m].liquid_type == LIQUID_SOURCE;
return content_features(m).liquid_type == LIQUID_SOURCE;
//return (m == CONTENT_WATERSOURCE);
}
@ -279,21 +287,21 @@ inline u8 make_liquid_flowing(u8 m)
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_pointable(u8 m)
{
return g_content_features[m].pointable;
return content_features(m).pointable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
}
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_diggable(u8 m)
{
return g_content_features[m].diggable;
return content_features(m).diggable;
//return (m != CONTENT_AIR && m != CONTENT_WATER && m != CONTENT_WATERSOURCE);
}
// NOTE: Don't use, use "content_features(m).whatever" instead
inline bool content_buildable_to(u8 m)
{
return g_content_features[m].buildable_to;
return content_features(m).buildable_to;
//return (m == CONTENT_AIR || m == CONTENT_WATER || m == CONTENT_WATERSOURCE);
}
@ -303,7 +311,7 @@ inline bool content_buildable_to(u8 m)
*/
/*inline bool is_ground_content(u8 m)
{
return g_content_features[m].is_ground_content;
return content_features(m).is_ground_content;
}*/
/*
@ -622,7 +630,7 @@ struct MapNode
}
// Translate deprecated stuff
MapNode *translate_to = g_content_features[d].translate_to;
MapNode *translate_to = content_features(d).translate_to;
if(translate_to)
{
dstream<<"MapNode: WARNING: Translating "<<d<<" to "

49
src/mineral.cpp Normal file
View File

@ -0,0 +1,49 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "mineral.h"
const char *mineral_filenames[MINERAL_COUNT] =
{
NULL,
"mineral_coal.png",
"mineral_iron.png"
};
textureid_t mineral_textures[MINERAL_COUNT] = {0};
void init_mineral(IrrlichtWrapper *irrlicht)
{
for(u32 i=0; i<MINERAL_COUNT; i++)
{
if(mineral_filenames[i] == NULL)
continue;
mineral_textures[i] = irrlicht->getTextureId(mineral_filenames[i]);
}
}
textureid_t mineral_block_texture(u8 mineral)
{
if(mineral >= MINERAL_COUNT)
return 0;
return mineral_textures[mineral];
}

View File

@ -21,6 +21,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define MINERAL_HEADER
#include "inventory.h"
#include "texture.h"
#include "irrlichtwrapper.h"
/*
Minerals
@ -29,22 +31,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
type param.
*/
// Caches textures
void init_mineral(IrrlichtWrapper *irrlicht);
#define MINERAL_NONE 0
#define MINERAL_COAL 1
#define MINERAL_IRON 2
inline const char * mineral_block_texture(u8 mineral)
{
switch(mineral)
{
case MINERAL_COAL:
return "mineral_coal.png";
case MINERAL_IRON:
return "mineral_iron.png";
default:
return "";
}
}
#define MINERAL_COUNT 3
textureid_t mineral_block_texture(u8 mineral);
inline CraftItem * getDiggedMineralItem(u8 mineral)
{

View File

@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "map.h"
#include "connection.h"
#include "constants.h"
#include "utility.h"
Player::Player():
touching_ground(false),
@ -34,15 +35,21 @@ Player::Player():
m_position(0,0,0)
{
updateName("<not set>");
inventory.addList("main", PLAYER_INVENTORY_SIZE);
inventory.addList("craft", 9);
inventory.addList("craftresult", 1);
resetInventory();
}
Player::~Player()
{
}
void Player::resetInventory()
{
inventory.clear();
inventory.addList("main", PLAYER_INVENTORY_SIZE);
inventory.addList("craft", 9);
inventory.addList("craftresult", 1);
}
// Y direction is ignored
void Player::accelerate(v3f target_speed, f32 max_increase)
{
@ -80,6 +87,50 @@ void Player::accelerate(v3f target_speed, f32 max_increase)
#endif
}
void Player::serialize(std::ostream &os)
{
// Utilize a Settings object for storing values
Settings args;
args.setS32("version", 1);
args.set("name", m_name);
args.setFloat("pitch", m_pitch);
args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position);
args.writeLines(os);
os<<"PlayerArgsEnd\n";
inventory.serialize(os);
}
void Player::deSerialize(std::istream &is)
{
Settings args;
for(;;)
{
if(is.eof())
throw SerializationError
("Player::deSerialize(): PlayerArgsEnd not found");
std::string line;
std::getline(is, line);
std::string trimmedline = trim(line);
if(trimmedline == "PlayerArgsEnd")
break;
args.parseConfigLine(line);
}
//args.getS32("version");
std::string name = args.get("name");
updateName(name.c_str());
m_pitch = args.getFloat("pitch");
m_yaw = args.getFloat("yaw");
m_position = args.getV3F("position");
inventory.deSerialize(is);
}
/*
RemotePlayer
*/

View File

@ -29,6 +29,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PLAYERNAME_SIZE 20
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
class Map;
class Player
@ -37,6 +39,8 @@ public:
Player();
virtual ~Player();
void resetInventory();
//void move(f32 dtime, Map &map);
virtual void move(f32 dtime, Map &map) = 0;
@ -100,6 +104,14 @@ public:
// NOTE: Use peer_id == 0 for disconnected
/*virtual bool isClientConnected() { return false; }
virtual void setClientConnected(bool) {}*/
/*
serialize() writes a bunch of text that can contain
any characters except a '\0', and such an ending that
deSerialize stops reading exactly at the right point.
*/
void serialize(std::ostream &os);
void deSerialize(std::istream &is);
bool touching_ground;
bool in_water;
@ -119,8 +131,6 @@ protected:
class ServerRemotePlayer : public Player
{
public:
/*ServerRemotePlayer(bool client_connected):
m_client_connected(client_connected)*/
ServerRemotePlayer()
{
}
@ -137,18 +147,6 @@ public:
{
}
/*virtual bool isClientConnected()
{
return m_client_connected;
}
virtual void setClientConnected(bool client_connected)
{
m_client_connected = client_connected;
}
// This
bool m_client_connected;*/
private:
};
@ -252,7 +250,7 @@ private:
v3f m_showpos;
};
#endif
#endif // !SERVER
#ifndef SERVER
struct PlayerControl

View File

@ -1000,7 +1000,8 @@ Server::Server(
m_time_of_day(9000),
m_time_counter(0),
m_time_of_day_send_timer(0),
m_uptime(0)
m_uptime(0),
m_mapsavedir(mapsavedir)
{
//m_flowwater_timer = 0.0;
m_liquid_transform_timer = 0.0;
@ -1013,10 +1014,16 @@ Server::Server(
m_con_mutex.Init();
m_step_dtime_mutex.Init();
m_step_dtime = 0.0;
// Load players
m_env.deSerializePlayers(m_mapsavedir);
}
Server::~Server()
{
// Save players
m_env.serializePlayers(m_mapsavedir);
// Stop threads
stop();
@ -1222,82 +1229,6 @@ void Server::AsyncRunStep()
}
}
#if 0
/*
Update water
*/
if(g_settings.getBool("water_moves") == true)
{
float interval;
if(g_settings.getBool("endless_water") == false)
interval = 1.0;
else
interval = 0.25;
float &counter = m_flowwater_timer;
counter += dtime;
if(counter >= 0.25 && m_flow_active_nodes.size() > 0)
{
counter = 0.0;
core::map<v3s16, MapBlock*> modified_blocks;
{
JMutexAutoLock envlock(m_env_mutex);
MapVoxelManipulator v(&m_env.getMap());
v.m_disable_water_climb =
g_settings.getBool("disable_water_climb");
if(g_settings.getBool("endless_water") == false)
v.flowWater(m_flow_active_nodes, 0, false, 250);
else
v.flowWater(m_flow_active_nodes, 0, false, 50);
v.blitBack(modified_blocks);
ServerMap &map = ((ServerMap&)m_env.getMap());
// Update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
map.updateLighting(modified_blocks, lighting_modified_blocks);
// Add blocks modified by lighting to modified_blocks
for(core::map<v3s16, MapBlock*>::Iterator
i = lighting_modified_blocks.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
modified_blocks.insert(block->getPos(), block);
}
} // envlock
/*
Set the modified blocks unsent for all the clients
*/
JMutexAutoLock lock2(m_con_mutex);
for(core::map<u16, RemoteClient*>::Iterator
i = m_clients.getIterator();
i.atEnd() == false; i++)
{
RemoteClient *client = i.getNode()->getValue();
if(modified_blocks.size() > 0)
{
// Remove block from sent history
client->SetBlocksNotSent(modified_blocks);
}
}
} // interval counter
}
#endif
// Periodically print some info
{
float &counter = m_print_info_timer;
@ -1476,6 +1407,9 @@ void Server::AsyncRunStep()
dout_server<<"Server: Unloaded "<<deleted_count
<<" sectors from memory"<<std::endl;
}
// Save players
m_env.serializePlayers(m_mapsavedir);
}
}
}
@ -1601,6 +1535,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
Player *player = emergePlayer(playername, "", peer_id);
//Player *player = m_env.getPlayer(peer_id);
/*{
// DEBUG: Test serialization
std::ostringstream test_os;
player->serialize(test_os);
dstream<<"Player serialization test: \""<<test_os.str()
<<"\""<<std::endl;
std::istringstream test_is(test_os.str());
player->deSerialize(test_is);
}*/
// If failed, cancel
if(player == NULL)
{
@ -2950,7 +2894,7 @@ void Server::SendInventory(u16 peer_id)
if(!found)
{
ItemSpec specs[9];
specs[0] = ItemSpec(ITEM_CRAFT, "Coal");
specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
if(checkItemCombination(items, specs))
{
@ -3147,6 +3091,50 @@ RemoteClient* Server::getClient(u16 peer_id)
return n->getValue();
}
void setCreativeInventory(Player *player)
{
player->resetInventory();
// Give some good picks
{
InventoryItem *item = new ToolItem("STPick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("MesePick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
/*
Give materials
*/
assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
// add torch first
InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
player->inventory.addItem("main", item);
// Then others
for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
{
// Skip some materials
if(i == CONTENT_WATER || i == CONTENT_TORCH
|| i == CONTENT_COALSTONE)
continue;
InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem("main", item);
}
// Sign
{
InventoryItem *item = new MapBlockObjectItem("Sign Example text");
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
}
Player *Server::emergePlayer(const char *name, const char *password,
u16 peer_id)
{
@ -3162,8 +3150,16 @@ Player *Server::emergePlayer(const char *name, const char *password,
dstream<<"emergePlayer(): Player already connected"<<std::endl;
return NULL;
}
// Got one.
player->peer_id = peer_id;
// Reset inventory to creative if in creative mode
if(g_settings.getBool("creative_mode"))
{
setCreativeInventory(player);
}
return player;
}
@ -3271,51 +3267,15 @@ Player *Server::emergePlayer(const char *name, const char *password,
if(g_settings.getBool("creative_mode"))
{
// Give some good picks
{
InventoryItem *item = new ToolItem("STPick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
{
InventoryItem *item = new ToolItem("MesePick", 0);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
/*
Give materials
*/
assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
// add torch first
InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
player->inventory.addItem("main", item);
// Then others
for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
{
// Skip some materials
if(i == CONTENT_WATER || i == CONTENT_TORCH)
continue;
InventoryItem *item = new MaterialItem(i, 1);
player->inventory.addItem("main", item);
}
// Sign
{
InventoryItem *item = new MapBlockObjectItem("Sign Example text");
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
setCreativeInventory(player);
}
else
{
{
/*{
InventoryItem *item = new ToolItem("WPick", 32000);
void* r = player->inventory.addItem("main", item);
assert(r == NULL);
}
}*/
/*{
InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
void* r = player->inventory.addItem("main", item);

View File

@ -508,6 +508,8 @@ private:
Queue<PeerChange> m_peer_change_queue;
std::string m_mapsavedir;
friend class EmergeThread;
friend class RemoteClient;
};

124
src/texture.h Normal file
View File

@ -0,0 +1,124 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef TEXTURE_HEADER
#define TEXTURE_HEADER
#include "common_irrlicht.h"
//#include "utility.h"
#include "debug.h"
/*
All textures are given a "texture id".
0 = nothing (a NULL pointer texture)
*/
typedef u16 textureid_t;
/*
Every texture in the game can be specified by this.
It exists instead of specification strings because arbitary
texture combinations for map nodes are handled using this,
and strings are too slow for that purpose.
Plain texture pointers are not used because they don't contain
content information by themselves. A texture can be completely
reconstructed by just looking at this, while this also is a
fast unique key to containers.
*/
#define TEXTURE_SPEC_TEXTURE_COUNT 4
struct TextureSpec
{
TextureSpec()
{
clear();
}
TextureSpec(textureid_t id0)
{
clear();
tids[0] = id0;
}
TextureSpec(textureid_t id0, textureid_t id1)
{
clear();
tids[0] = id0;
tids[1] = id1;
}
void clear()
{
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
tids[i] = 0;
}
}
bool empty() const
{
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
if(tids[i] != 0)
return false;
}
return true;
}
void addTid(textureid_t tid)
{
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
if(tids[i] == 0)
{
tids[i] = tid;
return;
}
}
// Too many textures
assert(0);
}
bool operator==(const TextureSpec &other) const
{
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
if(tids[i] != other.tids[i])
return false;
}
return true;
}
bool operator<(const TextureSpec &other) const
{
for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
{
if(tids[i] >= other.tids[i])
return false;
}
return true;
}
// Ids of textures. They are blit on each other.
textureid_t tids[TEXTURE_SPEC_TEXTURE_COUNT];
};
#endif

View File

@ -22,8 +22,26 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "common_irrlicht.h"
//#include "utility.h"
#include "texture.h"
#include <string>
struct TileSpec
{
TileSpec():
alpha(255)
{
}
bool operator==(TileSpec &other)
{
return (spec == other.spec && alpha == other.alpha);
}
TextureSpec spec;
u8 alpha;
};
#if 0
struct TileSpec
{
TileSpec():
@ -52,5 +70,6 @@ struct TileSpec
std::string name;
u8 alpha;
};
#endif
#endif

View File

@ -760,17 +760,20 @@ class Settings
{
public:
// Returns false on EOF
bool parseConfigObject(std::istream &is)
void writeLines(std::ostream &os)
{
if(is.eof())
return false;
// NOTE: This function will be expanded to allow multi-line settings
std::string line;
std::getline(is, line);
//dstream<<"got line: \""<<line<<"\""<<std::endl;
for(core::map<std::string, std::string>::Iterator
i = m_settings.getIterator();
i.atEnd() == false; i++)
{
std::string name = i.getNode()->getKey();
std::string value = i.getNode()->getValue();
os<<name<<" = "<<value<<"\n";
}
}
bool parseConfigLine(const std::string &line)
{
std::string trimmedline = trim(line);
// Ignore comments
@ -798,6 +801,23 @@ public:
return true;
}
// Returns false on EOF
bool parseConfigObject(std::istream &is)
{
if(is.eof())
return false;
/*
NOTE: This function might be expanded to allow multi-line
settings.
*/
std::string line;
std::getline(is, line);
//dstream<<"got line: \""<<line<<"\""<<std::endl;
return parseConfigLine(line);
}
/*
Read configuration file
@ -1089,10 +1109,7 @@ public:
float getFloat(std::string name)
{
float f;
std::istringstream vis(get(name));
vis>>f;
return f;
return stof(get(name));
}
u16 getU16(std::string name)
@ -1128,6 +1145,34 @@ public:
return stoi(get(name));
}
v3f getV3F(std::string name)
{
v3f value;
Strfnd f(get(name));
f.next("(");
value.X = stof(f.next(","));
value.Y = stof(f.next(","));
value.Z = stof(f.next(")"));
return value;
}
void setS32(std::string name, s32 value)
{
set(name, itos(value));
}
void setFloat(std::string name, float value)
{
set(name, ftos(value));
}
void setV3F(std::string name, v3f value)
{
std::ostringstream os;
os<<"("<<value.X<<","<<value.Y<<","<<value.Z<<")";
set(name, os.str());
}
void clear()
{
m_settings.clear();
@ -1628,5 +1673,121 @@ private:
core::list<Value> m_list;
};
#if 0
template<typename Key, typename Value>
class MutexedCache
{
public:
MutexedCache()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
void set(const Key &name, const Value &value)
{
JMutexAutoLock lock(m_mutex);
m_values[name] = value;
}
bool get(const Key &name, Value *result)
{
JMutexAutoLock lock(m_mutex);
typename core::map<Key, Value>::Node *n;
n = m_values.find(name);
if(n == NULL)
return false;
*result = n->getValue();
return true;
}
private:
core::map<Key, Value> m_values;
JMutex m_mutex;
};
#endif
/*
Generates ids for comparable values.
Id=0 is reserved for "no value".
Is fast at:
- Returning value by id (very fast)
- Returning id by value
- Generating a new id for a value
Is not able to:
- Remove an id/value pair (is possible to implement but slow)
*/
template<typename T>
class MutexedIdGenerator
{
public:
MutexedIdGenerator()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
// Returns true if found
bool getValue(u32 id, T &value)
{
if(id == 0)
return false;
JMutexAutoLock lock(m_mutex);
if(m_id_to_value.size() < id)
return false;
value = m_id_to_value[id-1];
return true;
}
// If id exists for value, returns the id.
// Otherwise generates an id for the value.
u32 getId(const T &value)
{
JMutexAutoLock lock(m_mutex);
typename core::map<T, u32>::Node *n;
n = m_value_to_id.find(value);
if(n != NULL)
return n->getValue();
m_id_to_value.push_back(value);
u32 new_id = m_id_to_value.size();
m_value_to_id.insert(value, new_id);
return new_id;
}
private:
JMutex m_mutex;
// Values are stored here at id-1 position (id 1 = [0])
core::array<T> m_id_to_value;
core::map<T, u32> m_value_to_id;
};
/*
Checks if a string contains only supplied characters
*/
inline bool string_allowed(const std::string &s, const std::string &allowed_chars)
{
for(u32 i=0; i<s.size(); i++)
{
bool confirmed = false;
for(u32 j=0; j<allowed_chars.size(); j++)
{
if(s[i] == allowed_chars[j])
{
confirmed = true;
break;
}
}
if(confirmed == false)
return false;
}
return true;
}
#endif