51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-/*
-(c) 2010 Perttu Ahola <celeron55@gmail.com>
-*/
-
#ifndef UTILITY_HEADER
#define UTILITY_HEADER
-#include "common_irrlicht.h"
-#include "debug.h"
-#include "strfnd.h"
-#include "exceptions.h"
#include <iostream>
#include <fstream>
#include <string>
#include <sstream>
+#include <jthread.h>
+#include <jmutex.h>
+#include <jmutexautolock.h>
+
+#include "common_irrlicht.h"
+#include "debug.h"
+#include "strfnd.h"
+#include "exceptions.h"
+#include "porting.h"
extern const v3s16 g_26dirs[26];
+// 26th is (0,0,0)
+extern const v3s16 g_27dirs[27];
+
inline void writeU32(u8 *data, u32 i)
{
data[0] = ((i>>24)&0xff);
{
return ptr == t;
}
+ T & operator[](unsigned int i)
+ {
+ return ptr[i];
+ }
private:
void drop()
{
TimeTaker
*/
+class IrrlichtWrapper;
+
class TimeTaker
{
public:
- TimeTaker(const char *name, IrrlichtDevice *dev)
- {
- m_name = name;
- m_dev = dev;
- m_time1 = m_dev->getTimer()->getRealTime();
- m_running = true;
- }
+ TimeTaker(const char *name, u32 *result=NULL);
+
~TimeTaker()
{
stop();
}
- u32 stop(bool quiet=false)
- {
- if(m_running)
- {
- u32 time2 = m_dev->getTimer()->getRealTime();
- u32 dtime = time2 - m_time1;
- if(quiet == false)
- std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
- m_running = false;
- return dtime;
- }
- return 0;
- }
+
+ u32 stop(bool quiet=false);
+
+ u32 getTime();
+
private:
const char *m_name;
- IrrlichtDevice *m_dev;
u32 m_time1;
bool m_running;
+ u32 *m_result;
};
// Calculates the borders of a "d-radius" cube
/*
This is an optimized sequence of coordinates.
*/
+ list.push_back(v3s16( 0, 1, 0)); // top
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
- list.push_back(v3s16( 0, 1, 0)); // top
// 6
list.push_back(v3s16(-1, 0, 1)); // back left
list.push_back(v3s16( 1, 0, 1)); // back right
);
}
+inline v2s16 getContainerPos(v2s16 p, v2s16 d)
+{
+ return v2s16(
+ getContainerPos(p.X, d.X),
+ getContainerPos(p.Y, d.Y)
+ );
+}
+
+inline v3s16 getContainerPos(v3s16 p, v3s16 d)
+{
+ return v3s16(
+ getContainerPos(p.X, d.X),
+ getContainerPos(p.Y, d.Y),
+ getContainerPos(p.Z, d.Z)
+ );
+}
+
inline bool isInArea(v3s16 p, s16 d)
{
return (
);
}
+inline bool isInArea(v3s16 p, v3s16 d)
+{
+ return (
+ p.X >= 0 && p.X < d.X &&
+ p.Y >= 0 && p.Y < d.Y &&
+ p.Z >= 0 && p.Z < d.Z
+ );
+}
+
+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;
+}
+
inline std::wstring narrow_to_wide(const std::string& mbs)
{
size_t wcl = mbs.size();
return f;
}
-inline std::string lowercase(std::string s)
+inline std::string lowercase(const std::string &s)
{
+ std::string s2;
for(size_t i=0; i<s.size(); i++)
{
- if(s[i] >= 'A' && s[i] <= 'Z')
- s[i] -= 'A' - 'a';
+ char c = s[i];
+ if(c >= 'A' && c <= 'Z')
+ c -= 'A' - 'a';
+ s2 += c;
}
- return s;
+ return s2;
}
-inline bool is_yes(std::string s)
+inline bool is_yes(const std::string &s)
{
- s = lowercase(trim(s));
- if(s == "y" || s == "yes" || s == "true")
+ std::string s2 = lowercase(trim(s));
+ if(s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "1")
return true;
return false;
}
-inline s32 stoi(std::string s, s32 min, s32 max)
+inline s32 stoi(const std::string &s, s32 min, s32 max)
{
s32 i = atoi(s.c_str());
if(i < min)
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();
+}
+
+/*
+ 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:
- // 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
std::string value = sf.next("\n");
value = trim(value);
- dstream<<"Config name=\""<<name<<"\" value=\""
- <<value<<"\""<<std::endl;
+ /*dstream<<"Config name=\""<<name<<"\" value=\""
+ <<value<<"\""<<std::endl;*/
m_settings[name] = value;
return true;
}
- // Returns true on success
+ // 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;
+ dstream<<"Error opening configuration file \""
+ <<filename<<"\""<<std::endl;
return false;
}
- dstream<<"Parsing configuration file: "
- <<filename<<std::endl;
+ 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)
- throw SettingNotFoundException("Setting not found");
+ {
+ n = m_defaults.find(name);
+ if(n == NULL)
+ {
+ throw SettingNotFoundException("Setting not found");
+ }
+ }
return n->getValue();
}
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)
{
- std::string s = get(name);
- if(s != "")
- return is_yes(s);
+ // 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);
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)
u16 getU16Ask(std::string name, std::string question, u16 def)
{
- std::string s = get(name);
- if(s != "")
- return stoi(s, 0, 65535);
+ // 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);
return stoi(get(name));
}
-private:
- core::map<std::string, std::string> m_settings;
-};
+ 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;
+};
+
+/*
+ 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
+*/
+
+template<typename T>
+class MutexedQueue
+{
+public:
+ MutexedQueue()
+ {
+ m_mutex.Init();
+ }
+ u32 size()
+ {
+ return m_list.size();
+ }
+ void push_back(T t)
+ {
+ JMutexAutoLock lock(m_mutex);
+ m_list.push_back(t);
+ }
+ T pop_front(u32 wait_time_max_ms=0)
+ {
+ u32 wait_time_ms = 0;
+
+ for(;;)
+ {
+ {
+ JMutexAutoLock lock(m_mutex);
+
+ if(m_list.size() > 0)
+ {
+ typename core::list<T>::Iterator begin = m_list.begin();
+ T t = *begin;
+ m_list.erase(begin);
+ return t;
+ }
+
+ if(wait_time_ms >= wait_time_max_ms)
+ throw ItemNotFoundException("MutexedQueue: queue is empty");
+ }
+
+ // Wait a while before trying again
+ sleep_ms(10);
+ wait_time_ms += 10;
+ }
+ }
+
+ JMutex & getMutex()
+ {
+ return m_mutex;
+ }
+
+ core::list<T> & getList()
+ {
+ return m_list;
+ }
+
+protected:
+ JMutex m_mutex;
+ core::list<T> m_list;
+};
+
+template<typename Caller, typename Data>
+class CallerInfo
+{
+public:
+ Caller caller;
+ Data data;
+};
+
+template<typename Key, typename T, typename Caller, typename CallerData>
+class GetResult
+{
+public:
+ Key key;
+ T item;
+ core::list<CallerInfo<Caller, CallerData> > callers;
+};
+
+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()
+ {
+ dest = NULL;
+ }
+ 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;
+};
+
+/*
+ Quickhands for typical request-result queues.
+ Used for distributing work between threads.
+*/
+
+template<typename Key, typename T, typename Caller, typename CallerData>
+class RequestQueue
+{
+public:
+ u32 size()
+ {
+ return m_queue.size();
+ }
+
+ void add(Key key, Caller caller, CallerData callerdata,
+ ResultQueue<Key, T, Caller, CallerData> *dest)
+ {
+ 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);
+ }
+
+ GetRequest<Key, T, Caller, CallerData> pop(bool wait_if_empty=false)
+ {
+ return m_queue.pop_front(wait_if_empty);
+ }
+
+private:
+ MutexedQueue< GetRequest<Key, T, Caller, CallerData> > m_queue;
+};
+
+/*
+ Pseudo-random (VC++ rand() sucks)
+*/
+int myrand(void);
+void mysrand(unsigned seed);
+#define MYRAND_MAX 32767
+
+inline int myrand_range(int min, int max)
+{
+ if(min > max)
+ {
+ assert(0);
+ return max;
+ }
+ return (myrand()%(max-min+1))+min;
+}
+
+/*
+ Some kind of a thing that stores attributes related to
+ coordinate points
+*/
+
+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
+ {
+ v2s16 p;
+ Attribute attr;
+ };
+
+public:
+ ~PointAttributeList()
+ {
+ }
+
+ 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;
+ }
+
+ Attribute getNearAttr(v3s16 p)
+ {
+ 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
+ */
+ float getInterpolatedFloat(v2s16 p);
+
+ float getInterpolatedFloat(v3s16 p)
+ {
+ return getInterpolatedFloat(v2s16(p.X, p.Z));
+ }
+
+ void addPoint(v2s16 p, const Attribute &attr)
+ {
+ PointWithAttr pattr;
+ pattr.p = p;
+ pattr.attr = attr;
+ m_points.push_back(pattr);
+ }
+
+ void addPoint(v3s16 p, const Attribute &attr)
+ {
+ 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;
+}
#endif