1
0
mirror of https://github.com/moparisthebest/minetest synced 2024-11-17 06:45:18 -05:00
minetest/src/utility.h

1794 lines
30 KiB
C
Raw Normal View History

/*
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.
*/
2010-11-26 18:02:21 -05:00
#ifndef UTILITY_HEADER
#define UTILITY_HEADER
#include <iostream>
#include <fstream>
2010-11-26 18:02:21 -05:00
#include <string>
#include <sstream>
2010-12-19 09:51:45 -05:00
#include <jthread.h>
2010-12-12 20:19:12 -05:00
#include <jmutex.h>
#include <jmutexautolock.h>
2010-11-26 18:02:21 -05:00
#include "common_irrlicht.h"
#include "debug.h"
#include "strfnd.h"
#include "exceptions.h"
2010-12-21 11:08:24 -05:00
#include "porting.h"
2010-11-26 18:02:21 -05:00
extern const v3s16 g_26dirs[26];
// 26th is (0,0,0)
extern const v3s16 g_27dirs[27];
2010-11-26 18:02:21 -05:00
inline void writeU32(u8 *data, u32 i)
{
data[0] = ((i>>24)&0xff);
data[1] = ((i>>16)&0xff);
data[2] = ((i>> 8)&0xff);
data[3] = ((i>> 0)&0xff);
}
inline void writeU16(u8 *data, u16 i)
{
data[0] = ((i>> 8)&0xff);
data[1] = ((i>> 0)&0xff);
}
inline void writeU8(u8 *data, u8 i)
{
data[0] = ((i>> 0)&0xff);
}
inline u32 readU32(u8 *data)
{
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
}
inline u16 readU16(u8 *data)
{
return (data[0]<<8) | (data[1]<<0);
}
inline u8 readU8(u8 *data)
{
return (data[0]<<0);
}
// Signed variants of the above
inline void writeS32(u8 *data, s32 i){
writeU32(data, (u32)i);
}
inline s32 readS32(u8 *data){
return (s32)readU32(data);
}
inline void writeS16(u8 *data, s16 i){
writeU16(data, (u16)i);
}
inline s16 readS16(u8 *data){
return (s16)readU16(data);
}
inline void writeV3S32(u8 *data, v3s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[4], p.Y);
writeS32(&data[8], p.Z);
}
inline v3s32 readV3S32(u8 *data)
{
v3s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[4]);
p.Z = readS32(&data[8]);
return p;
}
inline void writeV2S16(u8 *data, v2s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
}
inline v2s16 readV2S16(u8 *data)
{
v2s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
return p;
}
inline void writeV2S32(u8 *data, v2s32 p)
{
writeS32(&data[0], p.X);
writeS32(&data[2], p.Y);
}
inline v2s32 readV2S32(u8 *data)
{
v2s32 p;
p.X = readS32(&data[0]);
p.Y = readS32(&data[2]);
return p;
}
inline void writeV3S16(u8 *data, v3s16 p)
{
writeS16(&data[0], p.X);
writeS16(&data[2], p.Y);
writeS16(&data[4], p.Z);
}
inline v3s16 readV3S16(u8 *data)
{
v3s16 p;
p.X = readS16(&data[0]);
p.Y = readS16(&data[2]);
p.Z = readS16(&data[4]);
return p;
}
/*
None of these are used at the moment
*/
template <typename T>
class SharedPtr
{
public:
SharedPtr(T *t=NULL)
{
refcount = new int;
*refcount = 1;
ptr = t;
}
SharedPtr(SharedPtr<T> &t)
{
//*this = t;
drop();
refcount = t.refcount;
(*refcount)++;
ptr = t.ptr;
}
~SharedPtr()
{
drop();
}
SharedPtr<T> & operator=(T *t)
{
drop();
refcount = new int;
*refcount = 1;
ptr = t;
return *this;
}
SharedPtr<T> & operator=(SharedPtr<T> &t)
{
drop();
refcount = t.refcount;
(*refcount)++;
ptr = t.ptr;
return *this;
}
T* operator->()
{
return ptr;
}
T & operator*()
{
return *ptr;
}
bool operator!=(T *t)
{
return ptr != t;
}
bool operator==(T *t)
{
return ptr == t;
}
private:
void drop()
{
assert((*refcount) > 0);
(*refcount)--;
if(*refcount == 0)
{
delete refcount;
if(ptr != NULL)
delete ptr;
}
}
T *ptr;
int *refcount;
};
template <typename T>
class Buffer
{
public:
Buffer(unsigned int size)
{
m_size = size;
data = new T[size];
}
Buffer(const Buffer &buffer)
{
m_size = buffer.m_size;
data = new T[buffer.m_size];
memcpy(data, buffer.data, buffer.m_size);
}
Buffer(T *t, unsigned int size)
{
m_size = size;
data = new T[size];
memcpy(data, t, size);
}
~Buffer()
{
delete[] data;
}
T & operator[](unsigned int i) const
{
return data[i];
}
T * operator*() const
{
return data;
}
unsigned int getSize() const
{
return m_size;
}
private:
T *data;
unsigned int m_size;
};
template <typename T>
class SharedBuffer
{
public:
SharedBuffer(unsigned int size)
{
m_size = size;
data = new T[size];
refcount = new unsigned int;
(*refcount) = 1;
}
SharedBuffer(const SharedBuffer &buffer)
{
//std::cout<<"SharedBuffer(const SharedBuffer &buffer)"<<std::endl;
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
}
SharedBuffer & operator=(const SharedBuffer & buffer)
{
//std::cout<<"SharedBuffer & operator=(const SharedBuffer & buffer)"<<std::endl;
if(this == &buffer)
return *this;
drop();
m_size = buffer.m_size;
data = buffer.data;
refcount = buffer.refcount;
(*refcount)++;
return *this;
}
/*
Copies whole buffer
*/
SharedBuffer(T *t, unsigned int size)
{
m_size = size;
data = new T[size];
memcpy(data, t, size);
refcount = new unsigned int;
(*refcount) = 1;
}
/*
Copies whole buffer
*/
SharedBuffer(const Buffer<T> &buffer)
{
m_size = buffer.m_size;
data = new T[buffer.getSize()];
memcpy(data, *buffer, buffer.getSize());
refcount = new unsigned int;
(*refcount) = 1;
}
~SharedBuffer()
{
drop();
}
T & operator[](unsigned int i) const
{
return data[i];
}
T * operator*() const
{
return data;
}
unsigned int getSize() const
{
return m_size;
}
private:
void drop()
{
assert((*refcount) > 0);
(*refcount)--;
if(*refcount == 0)
{
delete[] data;
delete refcount;
}
}
T *data;
unsigned int m_size;
unsigned int *refcount;
};
inline SharedBuffer<u8> SharedBufferFromString(const char *string)
{
SharedBuffer<u8> b((u8*)string, strlen(string)+1);
return b;
}
template<typename T>
class MutexedVariable
{
public:
MutexedVariable(T value):
m_value(value)
{
m_mutex.Init();
}
T get()
{
JMutexAutoLock lock(m_mutex);
return m_value;
}
void set(T value)
{
JMutexAutoLock lock(m_mutex);
m_value = value;
}
// You'll want to grab this in a SharedPtr
JMutexAutoLock * getLock()
{
return new JMutexAutoLock(m_mutex);
}
// You pretty surely want to grab the lock when accessing this
T m_value;
private:
JMutex m_mutex;
};
/*
TimeTaker
*/
2010-12-20 15:03:49 -05:00
class IrrlichtWrapper;
2010-11-26 18:02:21 -05:00
class TimeTaker
{
public:
2010-12-21 11:08:24 -05:00
TimeTaker(const char *name, u32 *result=NULL);
2010-12-20 15:03:49 -05:00
2010-11-26 18:02:21 -05:00
~TimeTaker()
{
stop();
}
2010-12-20 15:03:49 -05:00
u32 stop(bool quiet=false);
2011-01-26 10:13:19 -05:00
u32 getTime();
2010-11-26 18:02:21 -05:00
private:
const char *m_name;
u32 m_time1;
bool m_running;
u32 *m_result;
2010-11-26 18:02:21 -05:00
};
// Calculates the borders of a "d-radius" cube
inline void getFacePositions(core::list<v3s16> &list, u16 d)
{
if(d == 0)
{
list.push_back(v3s16(0,0,0));
return;
}
if(d == 1)
{
/*
This is an optimized sequence of coordinates.
*/
2010-12-13 14:32:35 -05:00
list.push_back(v3s16( 0, 1, 0)); // top
2010-11-26 18:02:21 -05:00
list.push_back(v3s16( 0, 0, 1)); // back
list.push_back(v3s16(-1, 0, 0)); // left
list.push_back(v3s16( 1, 0, 0)); // right
list.push_back(v3s16( 0, 0,-1)); // front
list.push_back(v3s16( 0,-1, 0)); // bottom
// 6
list.push_back(v3s16(-1, 0, 1)); // back left
list.push_back(v3s16( 1, 0, 1)); // back right
list.push_back(v3s16(-1, 0,-1)); // front left
list.push_back(v3s16( 1, 0,-1)); // front right
list.push_back(v3s16(-1,-1, 0)); // bottom left
list.push_back(v3s16( 1,-1, 0)); // bottom right
list.push_back(v3s16( 0,-1, 1)); // bottom back
list.push_back(v3s16( 0,-1,-1)); // bottom front
list.push_back(v3s16(-1, 1, 0)); // top left
list.push_back(v3s16( 1, 1, 0)); // top right
list.push_back(v3s16( 0, 1, 1)); // top back
list.push_back(v3s16( 0, 1,-1)); // top front
// 18
list.push_back(v3s16(-1, 1, 1)); // top back-left
list.push_back(v3s16( 1, 1, 1)); // top back-right
list.push_back(v3s16(-1, 1,-1)); // top front-left
list.push_back(v3s16( 1, 1,-1)); // top front-right
list.push_back(v3s16(-1,-1, 1)); // bottom back-left
list.push_back(v3s16( 1,-1, 1)); // bottom back-right
list.push_back(v3s16(-1,-1,-1)); // bottom front-left
list.push_back(v3s16( 1,-1,-1)); // bottom front-right
// 26
return;
}
// Take blocks in all sides, starting from y=0 and going +-y
for(s16 y=0; y<=d-1; y++)
{
// Left and right side, including borders
for(s16 z=-d; z<=d; z++)
{
list.push_back(v3s16(d,y,z));
list.push_back(v3s16(-d,y,z));
if(y != 0)
{
list.push_back(v3s16(d,-y,z));
list.push_back(v3s16(-d,-y,z));
}
}
// Back and front side, excluding borders
for(s16 x=-d+1; x<=d-1; x++)
{
list.push_back(v3s16(x,y,d));
list.push_back(v3s16(x,y,-d));
if(y != 0)
{
list.push_back(v3s16(x,-y,d));
list.push_back(v3s16(x,-y,-d));
}
}
}
// Take the bottom and top face with borders
// -d<x<d, y=+-d, -d<z<d
for(s16 x=-d; x<=d; x++)
for(s16 z=-d; z<=d; z++)
{
list.push_back(v3s16(x,-d,z));
list.push_back(v3s16(x,d,z));
}
}
class IndentationRaiser
{
public:
IndentationRaiser(u16 *indentation)
{
m_indentation = indentation;
(*m_indentation)++;
}
~IndentationRaiser()
{
(*m_indentation)--;
}
private:
u16 *m_indentation;
};
inline s16 getContainerPos(s16 p, s16 d)
{
return (p>=0 ? p : p-d+1) / d;
}
inline v2s16 getContainerPos(v2s16 p, s16 d)
{
return v2s16(
getContainerPos(p.X, d),
getContainerPos(p.Y, d)
);
}
inline v3s16 getContainerPos(v3s16 p, s16 d)
{
return v3s16(
getContainerPos(p.X, d),
getContainerPos(p.Y, d),
getContainerPos(p.Z, d)
);
}
inline bool isInArea(v3s16 p, s16 d)
{
return (
p.X >= 0 && p.X < d &&
p.Y >= 0 && p.Y < d &&
p.Z >= 0 && p.Z < d
);
}
inline bool isInArea(v2s16 p, s16 d)
{
return (
p.X >= 0 && p.X < d &&
p.Y >= 0 && p.Y < d
);
}
2010-12-25 09:04:51 -05:00
inline s16 rangelim(s16 i, s16 min, s16 max)
{
if(i < min)
return min;
if(i > max)
return max;
return i;
}
inline s16 rangelim(s16 i, s16 max)
{
if(i < 0)
return 0;
if(i > max)
return max;
return i;
}
inline v3s16 arealim(v3s16 p, s16 d)
{
if(p.X < 0)
p.X = 0;
if(p.Y < 0)
p.Y = 0;
if(p.Z < 0)
p.Z = 0;
if(p.X > d-1)
p.X = d-1;
if(p.Y > d-1)
p.Y = d-1;
if(p.Z > d-1)
p.Z = d-1;
return p;
}
2010-11-26 18:02:21 -05:00
inline std::wstring narrow_to_wide(const std::string& mbs)
{
size_t wcl = mbs.size();
SharedBuffer<wchar_t> wcs(wcl+1);
size_t l = mbstowcs(*wcs, mbs.c_str(), wcl);
wcs[l] = 0;
return *wcs;
}
inline std::string wide_to_narrow(const std::wstring& wcs)
{
size_t mbl = wcs.size()*4;
SharedBuffer<char> mbs(mbl+1);
size_t l = wcstombs(*mbs, wcs.c_str(), mbl);
if((int)l == -1)
mbs[0] = 0;
else
mbs[l] = 0;
return *mbs;
}
/*
See test.cpp for example cases.
wraps degrees to the range of -360...360
NOTE: Wrapping to 0...360 is not used because pitch needs negative values.
*/
inline float wrapDegrees(float f)
{
// Take examples of f=10, f=720.5, f=-0.5, f=-360.5
// This results in
// 10, 720, -1, -361
int i = floor(f);
// 0, 2, 0, -1
int l = i / 360;
// NOTE: This would be used for wrapping to 0...360
// 0, 2, -1, -2
/*if(i < 0)
l -= 1;*/
// 0, 720, 0, -360
int k = l * 360;
// 10, 0.5, -0.5, -0.5
f -= float(k);
return f;
}
2010-12-22 09:30:23 -05:00
inline std::string lowercase(const std::string &s)
2010-11-26 18:02:21 -05:00
{
2010-12-22 09:30:23 -05:00
std::string s2;
2010-11-26 18:02:21 -05:00
for(size_t i=0; i<s.size(); i++)
{
2010-12-22 09:30:23 -05:00
char c = s[i];
if(c >= 'A' && c <= 'Z')
c -= 'A' - 'a';
s2 += c;
2010-11-26 18:02:21 -05:00
}
2010-12-22 09:30:23 -05:00
return s2;
2010-11-26 18:02:21 -05:00
}
2010-12-22 09:30:23 -05:00
inline bool is_yes(const std::string &s)
2010-11-26 18:02:21 -05:00
{
2010-12-22 09:30:23 -05:00
std::string s2 = lowercase(trim(s));
2011-01-23 10:29:15 -05:00
if(s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "1")
2010-11-26 18:02:21 -05:00
return true;
return false;
}
2010-12-22 09:30:23 -05:00
inline s32 stoi(const std::string &s, s32 min, s32 max)
{
s32 i = atoi(s.c_str());
if(i < min)
i = min;
if(i > max)
i = max;
return i;
}
inline s32 stoi(std::string s)
{
return atoi(s.c_str());
}
inline float stof(std::string s)
{
float f;
std::istringstream ss(s);
ss>>f;
return f;
}
inline std::string itos(s32 i)
{
std::ostringstream o;
o<<i;
return o.str();
}
inline std::string ftos(float f)
{
std::ostringstream o;
o<<f;
return o.str();
}
2010-12-20 15:03:49 -05:00
/*
A base class for simple background thread implementation
*/
class SimpleThread : public JThread
{
bool run;
JMutex run_mutex;
public:
SimpleThread():
JThread(),
run(true)
{
run_mutex.Init();
}
virtual ~SimpleThread()
{}
virtual void * Thread() = 0;
bool getRun()
{
JMutexAutoLock lock(run_mutex);
return run;
}
void setRun(bool a_run)
{
JMutexAutoLock lock(run_mutex);
run = a_run;
}
void stop()
{
setRun(false);
while(IsRunning())
sleep_ms(100);
}
};
/*
Config stuff
*/
enum ValueType
{
VALUETYPE_STRING,
VALUETYPE_FLAG // Doesn't take any arguments
};
struct ValueSpec
{
ValueSpec(ValueType a_type, const char *a_help=NULL)
{
type = a_type;
help = a_help;
}
ValueType type;
const char *help;
};
class Settings
{
public:
void writeLines(std::ostream &os)
{
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
if(trimmedline[0] == '#')
return true;
//dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
Strfnd sf(trim(line));
std::string name = sf.next("=");
name = trim(name);
if(name == "")
return true;
std::string value = sf.next("\n");
value = trim(value);
dstream<<"Config name=\""<<name<<"\" value=\""
<<value<<"\""<<std::endl;
m_settings[name] = value;
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
Returns true on success
*/
bool readConfigFile(const char *filename)
{
std::ifstream is(filename);
if(is.good() == false)
{
dstream<<"Error opening configuration file \""
<<filename<<"\""<<std::endl;
return false;
}
dstream<<"Parsing configuration file: \""
<<filename<<"\""<<std::endl;
while(parseConfigObject(is));
return true;
}
/*
Reads a configuration object from stream (usually a single line)
and adds it to dst.
Preserves comments and empty lines.
Settings that were added to dst are also added to updated.
key of updated is setting name, value of updated is dummy.
Returns false on EOF
*/
bool getUpdatedConfigObject(std::istream &is,
core::list<std::string> &dst,
core::map<std::string, bool> &updated)
{
if(is.eof())
return false;
// NOTE: This function will be expanded to allow multi-line settings
std::string line;
std::getline(is, line);
std::string trimmedline = trim(line);
std::string line_end = "";
if(is.eof() == false)
line_end = "\n";
// Ignore comments
if(trimmedline[0] == '#')
{
dst.push_back(line+line_end);
return true;
}
Strfnd sf(trim(line));
std::string name = sf.next("=");
name = trim(name);
if(name == "")
{
dst.push_back(line+line_end);
return true;
}
std::string value = sf.next("\n");
value = trim(value);
if(m_settings.find(name))
{
std::string newvalue = m_settings[name];
if(newvalue != value)
{
dstream<<"Changing value of \""<<name<<"\" = \""
<<value<<"\" -> \""<<newvalue<<"\""
<<std::endl;
}
dst.push_back(name + " = " + newvalue + line_end);
updated[name] = true;
}
return true;
}
/*
Updates configuration file
Returns true on success
*/
bool updateConfigFile(const char *filename)
{
dstream<<"Updating configuration file: \""
<<filename<<"\""<<std::endl;
core::list<std::string> objects;
core::map<std::string, bool> updated;
// Read and modify stuff
{
std::ifstream is(filename);
if(is.good() == false)
{
dstream<<"Error opening configuration file"
" for reading: \""
<<filename<<"\""<<std::endl;
return false;
}
while(getUpdatedConfigObject(is, objects, updated));
}
// Write stuff back
{
std::ofstream os(filename);
if(os.good() == false)
{
dstream<<"Error opening configuration file"
" for writing: \""
<<filename<<"\""<<std::endl;
return false;
}
/*
Write updated stuff
*/
for(core::list<std::string>::Iterator
i = objects.begin();
i != objects.end(); i++)
{
os<<(*i);
}
/*
Write stuff that was not already in the file
*/
for(core::map<std::string, std::string>::Iterator
i = m_settings.getIterator();
i.atEnd() == false; i++)
{
if(updated.find(i.getNode()->getKey()))
continue;
std::string name = i.getNode()->getKey();
std::string value = i.getNode()->getValue();
dstream<<"Adding \""<<name<<"\" = \""<<value<<"\""
<<std::endl;
os<<name<<" = "<<value<<"\n";
}
}
return true;
}
/*
NOTE: Types of allowed_options are ignored
returns true on success
*/
bool parseCommandLine(int argc, char *argv[],
core::map<std::string, ValueSpec> &allowed_options)
{
int i=1;
for(;;)
{
if(i >= argc)
break;
std::string argname = argv[i];
if(argname.substr(0, 2) != "--")
{
dstream<<"Invalid command-line parameter \""
<<argname<<"\": --<option> expected."<<std::endl;
return false;
}
i++;
std::string name = argname.substr(2);
core::map<std::string, ValueSpec>::Node *n;
n = allowed_options.find(name);
if(n == NULL)
{
dstream<<"Unknown command-line parameter \""
<<argname<<"\""<<std::endl;
return false;
}
ValueType type = n->getValue().type;
std::string value = "";
if(type == VALUETYPE_FLAG)
{
value = "true";
}
else
{
if(i >= argc)
{
dstream<<"Invalid command-line parameter \""
<<name<<"\": missing value"<<std::endl;
return false;
}
value = argv[i];
i++;
}
dstream<<"Valid command-line parameter: \""
<<name<<"\" = \""<<value<<"\""
<<std::endl;
set(name, value);
}
return true;
}
void set(std::string name, std::string value)
{
m_settings[name] = value;
}
void setDefault(std::string name, std::string value)
{
m_defaults[name] = value;
}
bool exists(std::string name)
{
return (m_settings.find(name) || m_defaults.find(name));
}
std::string get(std::string name)
{
core::map<std::string, std::string>::Node *n;
n = m_settings.find(name);
if(n == NULL)
{
n = m_defaults.find(name);
if(n == NULL)
{
throw SettingNotFoundException("Setting not found");
}
}
return n->getValue();
}
bool getBool(std::string name)
{
return is_yes(get(name));
}
bool getFlag(std::string name)
{
try
{
return getBool(name);
}
catch(SettingNotFoundException &e)
{
return false;
}
}
// Asks if empty
bool getBoolAsk(std::string name, std::string question, bool def)
{
// If it is in settings
if(m_settings.find(name))
return getBool(name);
std::string s;
char templine[10];
std::cout<<question<<" [y/N]: ";
std::cin.getline(templine, 10);
s = templine;
if(s == "")
return def;
return is_yes(s);
}
float getFloat(std::string name)
{
return stof(get(name));
}
u16 getU16(std::string name)
{
return stoi(get(name), 0, 65535);
}
u16 getU16Ask(std::string name, std::string question, u16 def)
{
// If it is in settings
if(m_settings.find(name))
return getU16(name);
std::string s;
char templine[10];
std::cout<<question<<" ["<<def<<"]: ";
std::cin.getline(templine, 10);
s = templine;
if(s == "")
return def;
return stoi(s, 0, 65535);
}
s16 getS16(std::string name)
{
return stoi(get(name), -32768, 32767);
}
s32 getS32(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();
m_defaults.clear();
}
Settings & operator+=(Settings &other)
{
if(&other == this)
return *this;
for(core::map<std::string, std::string>::Iterator
i = other.m_settings.getIterator();
i.atEnd() == false; i++)
{
m_settings.insert(i.getNode()->getKey(),
i.getNode()->getValue());
}
for(core::map<std::string, std::string>::Iterator
i = other.m_defaults.getIterator();
i.atEnd() == false; i++)
{
m_defaults.insert(i.getNode()->getKey(),
i.getNode()->getValue());
}
}
Settings & operator=(Settings &other)
{
if(&other == this)
return *this;
clear();
(*this) += other;
return *this;
}
private:
core::map<std::string, std::string> m_settings;
core::map<std::string, std::string> m_defaults;
};
2010-12-12 20:19:12 -05:00
/*
2010-12-22 09:30:23 -05:00
FIFO queue
*/
template<typename T>
class Queue
{
public:
void push_back(T t)
{
m_list.push_back(t);
}
T pop_front()
{
if(m_list.size() == 0)
throw ItemNotFoundException("MutexedQueue: queue is empty");
typename core::list<T>::Iterator begin = m_list.begin();
T t = *begin;
m_list.erase(begin);
return t;
}
u32 size()
{
return m_list.size();
}
protected:
core::list<T> m_list;
};
/*
Thread-safe FIFO queue
2010-12-12 20:19:12 -05:00
*/
2010-12-20 15:03:49 -05:00
template<typename T>
class MutexedQueue
2010-12-12 20:19:12 -05:00
{
public:
2010-12-20 15:03:49 -05:00
MutexedQueue()
2010-12-12 20:19:12 -05:00
{
m_mutex.Init();
}
2010-12-20 15:03:49 -05:00
u32 size()
2010-12-12 20:19:12 -05:00
{
2010-12-20 15:03:49 -05:00
return m_list.size();
2010-12-12 20:19:12 -05:00
}
2010-12-20 15:03:49 -05:00
void push_back(T t)
2010-12-12 20:19:12 -05:00
{
JMutexAutoLock lock(m_mutex);
2010-12-20 15:03:49 -05:00
m_list.push_back(t);
}
2010-12-20 19:25:47 -05:00
T pop_front(u32 wait_time_max_ms=0)
2010-12-20 15:03:49 -05:00
{
2010-12-20 19:25:47 -05:00
u32 wait_time_ms = 0;
2010-12-20 15:03:49 -05:00
for(;;)
{
{
JMutexAutoLock lock(m_mutex);
2010-12-12 20:19:12 -05:00
2010-12-20 15:03:49 -05:00
if(m_list.size() > 0)
{
typename core::list<T>::Iterator begin = m_list.begin();
T t = *begin;
m_list.erase(begin);
return t;
}
2010-12-12 20:19:12 -05:00
2010-12-20 19:25:47 -05:00
if(wait_time_ms >= wait_time_max_ms)
throw ItemNotFoundException("MutexedQueue: queue is empty");
2010-12-20 15:03:49 -05:00
}
2010-12-12 20:19:12 -05:00
2010-12-20 19:25:47 -05:00
// Wait a while before trying again
sleep_ms(10);
wait_time_ms += 10;
2010-12-20 15:03:49 -05:00
}
2010-12-12 20:19:12 -05:00
}
2010-12-20 15:03:49 -05:00
JMutex & getMutex()
{
return m_mutex;
}
core::list<T> & getList()
{
return m_list;
}
protected:
2010-12-12 20:19:12 -05:00
JMutex m_mutex;
2010-12-20 15:03:49 -05:00
core::list<T> m_list;
2010-12-12 20:19:12 -05:00
};
2010-12-20 15:03:49 -05:00
template<typename Caller, typename Data>
class CallerInfo
2010-12-19 09:51:45 -05:00
{
2010-12-20 15:03:49 -05:00
public:
Caller caller;
Data data;
};
2010-12-19 09:51:45 -05:00
2010-12-20 15:03:49 -05:00
template<typename Key, typename T, typename Caller, typename CallerData>
class GetResult
{
2010-12-19 09:51:45 -05:00
public:
2010-12-20 15:03:49 -05:00
Key key;
T item;
core::list<CallerInfo<Caller, CallerData> > callers;
};
2010-12-19 09:51:45 -05:00
2010-12-20 15:03:49 -05:00
template<typename Key, typename T, typename Caller, typename CallerData>
class ResultQueue: public MutexedQueue< GetResult<Key, T, Caller, CallerData> >
{
};
template<typename Key, typename T, typename Caller, typename CallerData>
class GetRequest
{
public:
GetRequest()
2010-12-19 09:51:45 -05:00
{
2010-12-20 15:03:49 -05:00
dest = NULL;
2010-12-19 09:51:45 -05:00
}
2010-12-20 15:03:49 -05:00
GetRequest(ResultQueue<Key,T, Caller, CallerData> *a_dest)
{
dest = a_dest;
}
GetRequest(ResultQueue<Key,T, Caller, CallerData> *a_dest,
Key a_key)
{
dest = a_dest;
key = a_key;
}
~GetRequest()
{
}
Key key;
ResultQueue<Key, T, Caller, CallerData> *dest;
core::list<CallerInfo<Caller, CallerData> > callers;
};
2010-12-19 09:51:45 -05:00
2010-12-20 15:03:49 -05:00
/*
Quickhands for typical request-result queues.
Used for distributing work between threads.
*/
2010-12-19 09:51:45 -05:00
2010-12-20 15:03:49 -05:00
template<typename Key, typename T, typename Caller, typename CallerData>
class RequestQueue
{
public:
u32 size()
2010-12-19 09:51:45 -05:00
{
2010-12-20 15:03:49 -05:00
return m_queue.size();
2010-12-19 09:51:45 -05:00
}
2010-12-20 15:03:49 -05:00
void add(Key key, Caller caller, CallerData callerdata,
ResultQueue<Key, T, Caller, CallerData> *dest)
2010-12-19 09:51:45 -05:00
{
2010-12-20 15:03:49 -05:00
JMutexAutoLock lock(m_queue.getMutex());
/*
If the caller is already on the list, only update CallerData
*/
for(typename core::list< GetRequest<Key, T, Caller, CallerData> >::Iterator
i = m_queue.getList().begin();
i != m_queue.getList().end(); i++)
{
GetRequest<Key, T, Caller, CallerData> &request = *i;
if(request.key == key)
{
for(typename core::list< CallerInfo<Caller, CallerData> >::Iterator
i = request.callers.begin();
i != request.callers.end(); i++)
{
CallerInfo<Caller, CallerData> &ca = *i;
if(ca.caller == caller)
{
ca.data = callerdata;
return;
}
}
CallerInfo<Caller, CallerData> ca;
ca.caller = caller;
ca.data = callerdata;
request.callers.push_back(ca);
return;
}
}
/*
Else add a new request to the queue
*/
GetRequest<Key, T, Caller, CallerData> request;
request.key = key;
CallerInfo<Caller, CallerData> ca;
ca.caller = caller;
ca.data = callerdata;
request.callers.push_back(ca);
request.dest = dest;
m_queue.getList().push_back(request);
2010-12-19 09:51:45 -05:00
}
2010-12-20 15:03:49 -05:00
GetRequest<Key, T, Caller, CallerData> pop(bool wait_if_empty=false)
2010-12-19 09:51:45 -05:00
{
2010-12-20 15:03:49 -05:00
return m_queue.pop_front(wait_if_empty);
2010-12-19 09:51:45 -05:00
}
2010-12-20 15:03:49 -05:00
private:
MutexedQueue< GetRequest<Key, T, Caller, CallerData> > m_queue;
2010-12-19 09:51:45 -05:00
};
2011-01-15 06:50:13 -05:00
/*
Pseudo-random (VC++ rand() sucks)
*/
int myrand(void);
void mysrand(unsigned seed);
#define MYRAND_MAX 32767
2011-01-15 06:50:13 -05:00
/*
Some kind of a thing that stores attributes related to
coordinate points
2011-01-15 06:50:13 -05:00
*/
struct Attribute
{
Attribute()
{
}
Attribute(const std::string &value):
m_value(value)
{
}
Attribute(float value)
{
m_value = ftos(value);
}
void set(const std::string &value)
{
m_value = value;
}
std::string get()
{
return m_value;
}
bool getBool()
{
return is_yes(get());
}
float getFloat()
{
float f;
std::istringstream vis(get());
vis>>f;
return f;
}
u16 getU16()
{
return stoi(get(), 0, 65535);
}
s16 getS16()
{
return stoi(get(), -32768, 32767);
}
s32 getS32()
{
return stoi(get());
}
std::string m_value;
};
class PointAttributeList
{
struct PointWithAttr
{
2011-01-25 02:30:49 -05:00
v2s16 p;
Attribute attr;
};
public:
~PointAttributeList()
{
}
2011-01-25 02:30:49 -05:00
Attribute getNearAttr(v2s16 p)
{
core::list<PointWithAttr>::Iterator
nearest_i = m_points.end();
s16 nearest_d = 32767;
for(core::list<PointWithAttr>::Iterator
i = m_points.begin();
i != m_points.end(); i++)
{
PointWithAttr &pwa = *i;
s16 d = pwa.p.getDistanceFrom(p);
if(d < nearest_d)
{
nearest_i = i;
nearest_d = d;
}
}
if(nearest_i == m_points.end())
Attribute();
return nearest_i->attr;
}
2011-01-25 02:30:49 -05:00
Attribute getNearAttr(v3s16 p)
{
2011-01-25 02:30:49 -05:00
return getNearAttr(v2s16(p.X, p.Z));
}
bool empty()
{
return (m_points.size() == 0);
}
/*
Take all points in range, or at least the nearest point,
and interpolate the values as floats
*/
2011-01-25 02:30:49 -05:00
float getInterpolatedFloat(v2s16 p);
2011-01-25 02:30:49 -05:00
float getInterpolatedFloat(v3s16 p)
{
2011-01-25 02:30:49 -05:00
return getInterpolatedFloat(v2s16(p.X, p.Z));
}
2011-01-25 02:30:49 -05:00
void addPoint(v2s16 p, const Attribute &attr)
{
PointWithAttr pattr;
pattr.p = p;
pattr.attr = attr;
m_points.push_back(pattr);
}
2011-01-25 02:30:49 -05:00
void addPoint(v3s16 p, const Attribute &attr)
{
2011-01-25 02:30:49 -05:00
addPoint(v2s16(p.X, p.Z), attr);
}
private:
core::list<PointWithAttr> m_points;
};
/*
Basically just a wrapper to core::map<PointAttributeList*>
*/
class PointAttributeDatabase
{
public:
~PointAttributeDatabase()
{
for(core::map<std::string, PointAttributeList*>::Iterator
i = m_lists.getIterator();
i.atEnd() == false; i++)
{
delete i.getNode()->getValue();
}
}
PointAttributeList *getList(const std::string &name)
{
PointAttributeList *list = NULL;
core::map<std::string, PointAttributeList*>::Node *n;
n = m_lists.find(name);
if(n == NULL)
{
list = new PointAttributeList();
m_lists.insert(name, list);
}
else
{
list = n->getValue();
}
return list;
}
private:
core::map<std::string, PointAttributeList*> m_lists;
};
/*
Miscellaneous functions
*/
bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range);
/*
Queue with unique values with fast checking of value existence
*/
template<typename Value>
class UniqueQueue
{
public:
/*
Does nothing if value is already queued.
Return value:
true: value added
false: value already exists
*/
bool push_back(Value value)
{
// Check if already exists
if(m_map.find(value) != NULL)
return false;
// Add
m_map.insert(value, 0);
m_list.push_back(value);
return true;
}
Value pop_front()
{
typename core::list<Value>::Iterator i = m_list.begin();
Value value = *i;
m_map.remove(value);
m_list.erase(i);
return value;
}
u32 size()
{
assert(m_list.size() == m_map.size());
return m_list.size();
}
private:
core::map<Value, u8> m_map;
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;
}
2010-11-26 18:02:21 -05:00
#endif