RelativePath=".\src\materials.cpp"\r
>\r
</File>\r
+ <File\r
+ RelativePath=".\src\mineral.cpp"\r
+ >\r
+ </File>\r
<File\r
RelativePath=".\src\player.cpp"\r
>\r
"${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
filesys.cpp
connection.cpp
environment.cpp
- client.cpp
server.cpp
socket.cpp
mapblock.cpp
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(
*/
#include "environment.h"
+#include "filesys.h"
Environment::Environment(Map *map, std::ostream &dout):
m_dout(dout)
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
*/
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);
}
}
}
+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)
{
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);
os<<"\n";
}
- os<<"end\n";
+ os<<"EndInventoryList\n";
}
void InventoryList::deSerialize(std::istream &is)
std::string name;
std::getline(iss, name, ' ');
- if(name == "end")
+ if(name == "EndInventoryList")
{
break;
}
list->serialize(os);
}
- os<<"end\n";
+ os<<"EndInventory\n";
}
void Inventory::deSerialize(std::istream &is)
std::string name;
std::getline(iss, name, ' ');
- if(name == "end")
+ if(name == "EndInventory")
{
break;
}
#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()
#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()
{
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;
*/
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;
}
}
-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);
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
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);
-
- video::IImage *baseimage = driver->createImage(original, pos_base, dim);
- assert(baseimage);
-
- video::IImage *blittedimage = driver->createImageFromFile(porting::getDataPath(blitted_name.c_str()).c_str());
- assert(blittedimage);
-
- // 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);
+ // 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;
+ }
- blittedimage->drop();
-
- // Create texture from resulting image
-
- video::ITexture *newtexture = driver->addTexture(newname, baseimage);
+ video::IVideoDriver* driver = m_device->getVideoDriver();
- baseimage->drop();
+ /*
+ An image will be built from files and then converted into a texture.
+ */
+ video::IImage *baseimg = NULL;
- return newtexture;
-}
+ /*
+ Irrlicht requires a name for every texture, with which it
+ will be stored internally in irrlicht.
+ */
+ std::string texture_name;
-video::ITexture * make_crack(u16 progression, 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);
- // 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();
+ for(u32 i=0; i<TEXTURE_SPEC_TEXTURE_COUNT; i++)
+ {
+ textureid_t tid = spec.tids[i];
+ if(tid == 0)
+ continue;
- // Create texture from resulting image
+ std::string name = getTextureName(tid);
+
+ // Add something to the name so that it is a unique identifier.
+ texture_name += "[";
+ texture_name += name;
+ texture_name += "]";
- video::ITexture *newtexture = driver->addTexture(newname, baseimage);
+ 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());
- baseimage->drop();
+ if(image == NULL)
+ {
+ dstream<<"WARNING: Could not load image \""<<name
+ <<"\" from path \""<<path<<"\""
+ <<" while building texture"<<std::endl;
+ continue;
+ }
- return newtexture;
-}
+ // 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 0
-video::ITexture * make_sidegrass(video::ITexture *original,
- const char *newname, video::IVideoDriver* driver)
-{
- if(original == NULL)
+ // If no resulting image, return NULL
+ if(baseimg == NULL)
+ {
+ dstream<<"getTextureDirect(): baseimg is NULL (attempted to"
+ " create texture \""<<texture_name<<"\""<<std::endl;
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();
+ /*// 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;
}
-#endif
-video::ITexture * make_progressbar(float value, video::ITexture *original,
- const char *newname, video::IVideoDriver* driver)
+void make_progressbar(float value, video::IImage *image)
{
- 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);
+ if(image == NULL)
+ return;
- core::dimension2d<u32> size = baseimage->getDimension();
+ core::dimension2d<u32> size = image->getDimension();
u32 barheight = 1;
u32 barpad_x = 1;
u32 x = x0 + barpos.X;
for(u32 y=barpos.Y; y<barpos.Y+barheight; y++)
{
- baseimage->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;
+ image->setPixel(x,y, *c);
}
-
- 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
}
#include "common_irrlicht.h"
#include "debug.h"
#include "utility.h"
+#include "texture.h"
#include <jmutex.h>
#include <jmutexautolock.h>
threads, because texture pointers have to be handled in
background threads.
*/
-
+#if 0
class TextureCache
{
public:
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
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)
+ */
+ /*
+ 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.
*/
- video::ITexture* getTexture(const std::string &spec);
+ 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;
+
+ // 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;
-
- RequestQueue<std::string, video::ITexture*, u8, u8> m_get_texture_queue;
+
+ // A mapping from texture id to string spec
+ MutexedIdGenerator<std::string> m_namecache;
};
#endif
Gaming ideas:\r
-------------\r
\r
-- Aim for something like controlling a single dwarf in Dwarf Fortress.\r
+- Aim for something like controlling a single dwarf in Dwarf Fortress\r
\r
+- The player could go faster by a crafting a boat, or riding an animal\r
+\r
+- Random NPC traders. what else?\r
\r
Documentation:\r
--------------\r
\r
TODO: Flowing water animation\r
\r
+FIXME: The new texture stuff is slow on wine\r
+ - A basic grassy ground block takes 20-40ms\r
+ - A bit more complicated block can take 270ms\r
+ - On linux, a similar one doesn't take long at all (14ms)\r
+ - It is NOT a bad std::string implementation of MSVC.\r
+ - Can take up to 200ms? Is it when loading textures or always?\r
+ - Updating excess amount of meshes when making footprints is too\r
+ slow. It has to be fixed.\r
+ -> implement Map::updateNodeMeshes()\r
+ The fix:\r
+ * Optimize TileSpec to only contain a reference number that\r
+ is fast to compare, which refers to a cached string, or\r
+ * Make TextureSpec for using instead of strings\r
+\r
Configuration:\r
--------------\r
\r
TODO: Remove duplicate lighting implementation from Map (leave\r
VoxelManipulator, which is faster)\r
\r
-FIXME: The new texture stuff is slow on wine\r
- - A basic grassy ground block takes 20-40ms\r
- - A bit more complicated block can take 270ms\r
- - On linux, a similar one doesn't take long at all (14ms)\r
- - Is it a bad std::string implementation of MSVC?\r
- - Can take up to 200ms? Is it when loading textures or always?\r
- - Updating excess amount of meshes when making footprints is too\r
- slow. It has to be fixed.\r
- -> implement Map::updateNodeMeshes()\r
- TODO: Optimize TileSpec to only contain a reference number that\r
- is fast to compare, which refers to a cached string\r
-\r
Doing now:\r
----------\r
\r
#include "filesys.h"\r
#include "config.h"\r
#include "guiMainMenu.h"\r
+#include "mineral.h"\r
\r
IrrlichtWrapper *g_irrlicht;\r
\r
\r
// C-style stuff initialization\r
initializeMaterialProperties();\r
- init_mapnode();\r
\r
// Debug handler\r
BEGIN_DEBUG_EXCEPTION_HANDLER\r
*/\r
\r
init_content_inventory_texture_paths();\r
- //init_tile_textures();\r
+ init_mapnode(g_irrlicht);\r
+ init_mineral(g_irrlicht);\r
\r
/*\r
GUI stuff\r
bool nodefound = false;\r
v3s16 nodepos;\r
v3s16 neighbourpos;\r
- core::aabbox3d<f32> nodefacebox;\r
+ core::aabbox3d<f32> nodehilightbox;\r
f32 mindistance = BS * 1001;\r
\r
v3s16 pos_i = floatToInt(player_position);\r
nodepos = np;\r
neighbourpos = np;\r
mindistance = distance;\r
- nodefacebox = box;\r
+ nodehilightbox = box;\r
}\r
}\r
}\r
nodepos = np;\r
neighbourpos = np + dirs[i];\r
mindistance = distance;\r
- nodefacebox = facebox;\r
+\r
+ //nodehilightbox = facebox;\r
+\r
+ const float d = 0.502;\r
+ core::aabbox3d<f32> nodebox\r
+ (-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
+ v3f nodepos_f = intToFloat(nodepos);\r
+ nodebox.MinEdge += nodepos_f;\r
+ nodebox.MaxEdge += nodepos_f;\r
+ nodehilightbox = nodebox;\r
}\r
} // if distance < mindistance\r
} // for dirs\r
\r
// Visualize selection\r
\r
- const float d = 0.502;\r
- core::aabbox3d<f32> nodebox(-BS*d, -BS*d, -BS*d, BS*d, BS*d, BS*d);\r
- v3f nodepos_f = intToFloat(nodepos);\r
- //v3f nodepos_f(nodepos.X*BS, nodepos.Y*BS, nodepos.Z*BS);\r
- nodebox.MinEdge += nodepos_f;\r
- nodebox.MaxEdge += nodepos_f;\r
- hilightboxes.push_back(nodebox);\r
- \r
- //hilightboxes.push_back(nodefacebox);\r
+ hilightboxes.push_back(nodehilightbox);\r
\r
// Handle digging\r
\r
//u8 li = decode_light(light);
u8 li = light;
+ //u8 li = 255; //DEBUG
u8 alpha = tile.alpha;
/*u8 alpha = 255;
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);
}
}
*/
{
- 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);
// 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);
}
/*
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++)
= 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
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);
}
/*
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);
}
}
}
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;
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
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;
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
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;
}
// 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();
- }
+ }*/
}
#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();
*/
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;
~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_image_path = porting::getDataPath(imgname.c_str());
+ inventory_texture = spec;
}
-};
-// Initialized by init_mapnode()
-extern struct ContentFeatures g_content_features[256];
-
-inline ContentFeatures & content_features(u8 i)
-{
- return g_content_features[i];
-}
+ /*void setInventoryImage(std::string imgname)
+ {
+ inventory_image_path = porting::getDataPath(imgname.c_str());
+ }*/
+};
-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
*/
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);
}
*/
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);
}
*/
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;
// 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);
}
// 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);
}
*/
/*inline bool is_ground_content(u8 m)
{
- return g_content_features[m].is_ground_content;
+ return content_features(m).is_ground_content;
}*/
/*
}
// 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 "
--- /dev/null
+/*
+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];
+}
+
+
#define MINERAL_HEADER
#include "inventory.h"
+#include "texture.h"
+#include "irrlichtwrapper.h"
/*
Minerals
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)
{
#include "map.h"
#include "connection.h"
#include "constants.h"
+#include "utility.h"
Player::Player():
touching_ground(false),
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)
{
#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
*/
#define PLAYERNAME_SIZE 20
+#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
+
class Map;
class Player
Player();
virtual ~Player();
+ void resetInventory();
+
//void move(f32 dtime, Map &map);
virtual void move(f32 dtime, Map &map) = 0;
// 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;
class ServerRemotePlayer : public Player
{
public:
- /*ServerRemotePlayer(bool client_connected):
- m_client_connected(client_connected)*/
ServerRemotePlayer()
{
}
{
}
- /*virtual bool isClientConnected()
- {
- return m_client_connected;
- }
- virtual void setClientConnected(bool client_connected)
- {
- m_client_connected = client_connected;
- }
-
- // This
- bool m_client_connected;*/
-
private:
};
v3f m_showpos;
};
-#endif
+#endif // !SERVER
#ifndef SERVER
struct PlayerControl
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;
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();
}
}
-#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;
dout_server<<"Server: Unloaded "<<deleted_count
<<" sectors from memory"<<std::endl;
}
+
+ // Save players
+ m_env.serializePlayers(m_mapsavedir);
}
}
}
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)
{
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))
{
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)
{
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;
}
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);
Queue<PeerChange> m_peer_change_queue;
+ std::string m_mapsavedir;
+
friend class EmergeThread;
friend class RemoteClient;
};
--- /dev/null
+/*
+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
#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():
std::string name;
u8 alpha;
};
+#endif
#endif
{
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
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
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)
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();
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