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 <vector>
+#include <jthread.h>
#include <jmutex.h>
#include <jmutexautolock.h>
+#include <cstring>
+
+#include "common_irrlicht.h"
+#include "debug.h"
+#include "strfnd.h"
+#include "exceptions.h"
+#include "porting.h"
+
+extern const v3s16 g_6dirs[6];
extern const v3s16 g_26dirs[26];
+// 26th is (0,0,0)
+extern const v3s16 g_27dirs[27];
+
+inline void writeU64(u8 *data, u64 i)
+{
+ data[0] = ((i>>56)&0xff);
+ data[1] = ((i>>48)&0xff);
+ data[2] = ((i>>40)&0xff);
+ data[3] = ((i>>32)&0xff);
+ data[4] = ((i>>24)&0xff);
+ data[5] = ((i>>16)&0xff);
+ data[6] = ((i>> 8)&0xff);
+ data[7] = ((i>> 0)&0xff);
+}
+
inline void writeU32(u8 *data, u32 i)
{
data[0] = ((i>>24)&0xff);
data[0] = ((i>> 0)&0xff);
}
+inline u64 readU64(u8 *data)
+{
+ return ((u64)data[0]<<56) | ((u64)data[1]<<48)
+ | ((u64)data[2]<<40) | ((u64)data[3]<<32)
+ | ((u64)data[4]<<24) | ((u64)data[5]<<16)
+ | ((u64)data[6]<<8) | ((u64)data[7]<<0);
+}
+
inline u32 readU32(u8 *data)
{
return (data[0]<<24) | (data[1]<<16) | (data[2]<<8) | (data[3]<<0);
return (data[0]<<0);
}
-// Signed variants of the above
-
inline void writeS32(u8 *data, s32 i){
writeU32(data, (u32)i);
}
return (s32)readU32(data);
}
+inline void writeF1000(u8 *data, f32 i){
+ writeS32(data, i*1000);
+}
+inline f32 readF1000(u8 *data){
+ return (f32)readS32(data)/1000.;
+}
+
inline void writeS16(u8 *data, s16 i){
writeU16(data, (u16)i);
}
writeS32(&data[4], p.Y);
writeS32(&data[8], p.Z);
}
-
inline v3s32 readV3S32(u8 *data)
{
v3s32 p;
return p;
}
+inline void writeV3F1000(u8 *data, v3f p)
+{
+ writeF1000(&data[0], p.X);
+ writeF1000(&data[4], p.Y);
+ writeF1000(&data[8], p.Z);
+}
+inline v3f readV3F1000(u8 *data)
+{
+ v3f p;
+ p.X = (float)readF1000(&data[0]);
+ p.Y = (float)readF1000(&data[4]);
+ p.Z = (float)readF1000(&data[8]);
+ return p;
+}
+
inline void writeV2S16(u8 *data, v2s16 p)
{
writeS16(&data[0], p.X);
return p;
}
+/*
+ The above stuff directly interfaced to iostream
+*/
+
+inline void writeU8(std::ostream &os, u8 p)
+{
+ char buf[1];
+ writeU8((u8*)buf, p);
+ os.write(buf, 1);
+}
+inline u8 readU8(std::istream &is)
+{
+ char buf[1];
+ is.read(buf, 1);
+ return readU8((u8*)buf);
+}
+
+inline void writeU16(std::ostream &os, u16 p)
+{
+ char buf[2];
+ writeU16((u8*)buf, p);
+ os.write(buf, 2);
+}
+inline u16 readU16(std::istream &is)
+{
+ char buf[2];
+ is.read(buf, 2);
+ return readU16((u8*)buf);
+}
+
+inline void writeU32(std::ostream &os, u32 p)
+{
+ char buf[4];
+ writeU32((u8*)buf, p);
+ os.write(buf, 4);
+}
+inline u32 readU32(std::istream &is)
+{
+ char buf[4];
+ is.read(buf, 4);
+ return readU32((u8*)buf);
+}
+
+inline void writeF1000(std::ostream &os, f32 p)
+{
+ char buf[4];
+ writeF1000((u8*)buf, p);
+ os.write(buf, 4);
+}
+inline f32 readF1000(std::istream &is)
+{
+ char buf[4];
+ is.read(buf, 4);
+ return readF1000((u8*)buf);
+}
+
+inline void writeV3F1000(std::ostream &os, v3f p)
+{
+ char buf[12];
+ writeV3F1000((u8*)buf, p);
+ os.write(buf, 12);
+}
+inline v3f readV3F1000(std::istream &is)
+{
+ char buf[12];
+ is.read(buf, 12);
+ return readV3F1000((u8*)buf);
+}
+
/*
None of these are used at the moment
*/
{
return ptr == t;
}
+ T & operator[](unsigned int i)
+ {
+ return ptr[i];
+ }
private:
void drop()
{
class Buffer
{
public:
+ Buffer()
+ {
+ m_size = 0;
+ data = NULL;
+ }
Buffer(unsigned int size)
{
m_size = size;
- data = new T[size];
+ if(size != 0)
+ data = new T[size];
+ else
+ data = NULL;
}
Buffer(const Buffer &buffer)
{
m_size = buffer.m_size;
- data = new T[buffer.m_size];
- memcpy(data, buffer.data, buffer.m_size);
+ if(m_size != 0)
+ {
+ data = new T[buffer.m_size];
+ memcpy(data, buffer.data, buffer.m_size);
+ }
+ else
+ data = NULL;
}
- Buffer(T *t, unsigned int size)
+ Buffer(const T *t, unsigned int size)
{
m_size = size;
- data = new T[size];
- memcpy(data, t, size);
+ if(size != 0)
+ {
+ data = new T[size];
+ memcpy(data, t, size);
+ }
+ else
+ data = NULL;
}
~Buffer()
{
- delete[] data;
+ drop();
+ }
+ Buffer& operator=(const Buffer &buffer)
+ {
+ if(this == &buffer)
+ return *this;
+ drop();
+ m_size = buffer.m_size;
+ if(m_size != 0)
+ {
+ data = new T[buffer.m_size];
+ memcpy(data, buffer.data, buffer.m_size);
+ }
+ else
+ data = NULL;
+ return *this;
}
T & operator[](unsigned int i) const
{
return m_size;
}
private:
+ void drop()
+ {
+ if(data)
+ delete[] data;
+ }
T *data;
unsigned int m_size;
};
class SharedBuffer
{
public:
+ SharedBuffer()
+ {
+ m_size = 0;
+ data = NULL;
+ refcount = new unsigned int;
+ (*refcount) = 1;
+ }
SharedBuffer(unsigned int size)
{
m_size = size;
- data = new T[size];
+ if(m_size != 0)
+ data = new T[m_size];
+ else
+ data = NULL;
refcount = new unsigned int;
(*refcount) = 1;
}
SharedBuffer(T *t, unsigned int size)
{
m_size = size;
- data = new T[size];
- memcpy(data, t, size);
+ if(m_size != 0)
+ {
+ data = new T[m_size];
+ memcpy(data, t, m_size);
+ }
+ else
+ data = NULL;
refcount = new unsigned int;
(*refcount) = 1;
}
*/
SharedBuffer(const Buffer<T> &buffer)
{
- m_size = buffer.m_size;
- data = new T[buffer.getSize()];
- memcpy(data, *buffer, buffer.getSize());
+ m_size = buffer.getSize();
+ if(m_size != 0)
+ {
+ data = new T[m_size];
+ memcpy(data, *buffer, buffer.getSize());
+ }
+ else
+ data = NULL;
refcount = new unsigned int;
(*refcount) = 1;
}
}
T & operator[](unsigned int i) const
{
+ //assert(i < m_size)
return data[i];
}
T * operator*() const
{
return m_size;
}
+ operator Buffer<T>() const
+ {
+ return Buffer<T>(data, m_size);
+ }
private:
void drop()
{
(*refcount)--;
if(*refcount == 0)
{
- delete[] data;
+ if(data)
+ delete[] data;
delete refcount;
}
}
class TimeTaker
{
public:
- TimeTaker(const char *name, IrrlichtDevice *dev, u32 *result=NULL)
- {
- m_name = name;
- m_dev = dev;
- m_result = result;
- m_running = true;
- if(dev == NULL)
- {
- m_time1 = 0;
- return;
- }
- m_time1 = m_dev->getTimer()->getRealTime();
- }
+ TimeTaker(const char *name, u32 *result=NULL);
+
~TimeTaker()
{
stop();
}
- u32 stop(bool quiet=false)
- {
- if(m_running)
- {
- if(m_dev == NULL)
- {
- /*if(quiet == false)
- std::cout<<"Couldn't measure time for "<<m_name
- <<": dev==NULL"<<std::endl;*/
- return 0;
- }
- u32 time2 = m_dev->getTimer()->getRealTime();
- u32 dtime = time2 - m_time1;
- if(m_result != NULL)
- {
- (*m_result) += dtime;
- }
- else
- {
- 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;
};
+#ifndef SERVER
+// Sets the color of all vertices in the mesh
+void setMeshVerticesColor(scene::IMesh* mesh, video::SColor& color);
+#endif
+
// Calculates the borders of a "d-radius" cube
inline void getFacePositions(core::list<v3s16> &list, u16 d)
{
);
}
+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 max)
+{
+ if(i < 0)
+ return 0;
+ if(i > max)
+ return max;
+ return i;
+}
+
+#define rangelim(d, min, max) ((d) < (min) ? (min) : ((d)>(max)?(max):(d)))
+
+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();
- SharedBuffer<wchar_t> wcs(wcl+1);
+ Buffer<wchar_t> wcs(wcl+1);
size_t l = mbstowcs(*wcs, mbs.c_str(), wcl);
+ if(l == (size_t)(-1))
+ return L"<invalid multibyte string>";
wcs[l] = 0;
return *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)
+ if(l == (size_t)(-1))
mbs[0] = 0;
else
mbs[l] = 0;
return *mbs;
}
+// Split a string using the given delimiter. Returns a vector containing
+// the component parts.
+inline std::vector<std::wstring> str_split(const std::wstring &str, wchar_t delimiter)
+{
+ std::vector<std::wstring> parts;
+ std::wstringstream sstr(str);
+ std::wstring part;
+ while(std::getline(sstr, part, delimiter))
+ parts.push_back(part);
+ return parts;
+}
+
+
/*
See test.cpp for example cases.
wraps degrees to the range of -360...360
return f;
}
-inline std::string lowercase(std::string s)
+/* Wrap to 0...360 */
+inline float wrapDegrees_0_360(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;
+ // Wrap 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;
+}
+
+/* Wrap to -180...180 */
+inline float wrapDegrees_180(float f)
+{
+ f += 180;
+ f = wrapDegrees_0_360(f);
+ f -= 180;
+ return f;
+}
+
+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 i;
}
+
+// MSVC2010 includes it's own versions of these
+#if !defined(_MSC_VER) || _MSC_VER < 1600
+
inline s32 stoi(std::string s)
{
return atoi(s.c_str());
}
+inline s32 stoi(std::wstring s)
+{
+ return atoi(wide_to_narrow(s).c_str());
+}
+
+inline float stof(std::string s)
+{
+ float f;
+ std::istringstream ss(s);
+ ss>>f;
+ return f;
+}
+
+#endif
+
+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();
+}
+
+inline void str_replace(std::string & str, std::string const & pattern,
+ std::string const & replacement)
+{
+ std::string::size_type start = str.find(pattern, 0);
+ while(start != str.npos)
+ {
+ str.replace(start, pattern.size(), replacement);
+ start = str.find(pattern, start+replacement.size());
+ }
+}
+
+inline void str_replace_char(std::string & str, char from, char to)
+{
+ for(unsigned int i=0; i<str.size(); i++)
+ {
+ if(str[i] == from)
+ str[i] = to;
+ }
+}
+
/*
- Config stuff
+ A base class for simple background thread implementation
*/
-class Settings
+class SimpleThread : public JThread
{
+ bool run;
+ JMutex run_mutex;
+
public:
- // Returns false on EOF
- bool parseConfigObject(std::istream &is)
+ SimpleThread():
+ JThread(),
+ run(true)
{
- 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;
+ run_mutex.Init();
+ }
- std::string trimmedline = trim(line);
-
- // Ignore comments
- if(trimmedline[0] == '#')
- return true;
+ virtual ~SimpleThread()
+ {}
- //dstream<<"trimmedline=\""<<trimmedline<<"\""<<std::endl;
+ virtual void * Thread() = 0;
- Strfnd sf(trim(line));
+ bool getRun()
+ {
+ JMutexAutoLock lock(run_mutex);
+ return run;
+ }
+ void setRun(bool a_run)
+ {
+ JMutexAutoLock lock(run_mutex);
+ run = a_run;
+ }
- std::string name = sf.next("=");
- name = trim(name);
+ void stop()
+ {
+ setRun(false);
+ while(IsRunning())
+ sleep_ms(100);
+ }
+};
- if(name == "")
- return true;
-
- std::string value = sf.next("\n");
- value = trim(value);
+/*
+ FIFO queue (well, actually a FILO also)
+*/
+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("Queue: queue is empty");
- dstream<<"Config name=\""<<name<<"\" value=\""
- <<value<<"\""<<std::endl;
-
- m_settings[name] = value;
-
- return true;
+ typename core::list<T>::Iterator begin = m_list.begin();
+ T t = *begin;
+ m_list.erase(begin);
+ return t;
+ }
+ T pop_back()
+ {
+ if(m_list.size() == 0)
+ throw ItemNotFoundException("Queue: queue is empty");
+
+ typename core::list<T>::Iterator last = m_list.getLast();
+ T t = *last;
+ m_list.erase(last);
+ return t;
}
- // Returns true on success
- bool readConfigFile(const char *filename)
+ u32 size()
{
- std::ifstream is(filename);
- if(is.good() == false)
+ return m_list.size();
+ }
+
+protected:
+ core::list<T> m_list;
+};
+
+/*
+ Thread-safe FIFO queue (well, actually a FILO also)
+*/
+
+template<typename T>
+class MutexedQueue
+{
+public:
+ MutexedQueue()
+ {
+ m_mutex.Init();
+ }
+ u32 size()
+ {
+ JMutexAutoLock lock(m_mutex);
+ 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(;;)
{
- dstream<<"Error opening configuration file: "
- <<filename<<std::endl;
- return false;
+ {
+ 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;
}
+ }
+ T pop_back(u32 wait_time_max_ms=0)
+ {
+ u32 wait_time_ms = 0;
- dstream<<"Parsing configuration file: "
- <<filename<<std::endl;
-
- while(parseConfigObject(is));
-
- return true;
+ for(;;)
+ {
+ {
+ JMutexAutoLock lock(m_mutex);
+
+ if(m_list.size() > 0)
+ {
+ typename core::list<T>::Iterator last = m_list.getLast();
+ T t = *last;
+ m_list.erase(last);
+ 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;
+ }
}
- void set(std::string name, std::string value)
+ JMutex & getMutex()
{
- m_settings[name] = value;
+ return m_mutex;
}
- std::string get(std::string name)
+ core::list<T> & getList()
{
- core::map<std::string, std::string>::Node *n;
- n = m_settings.find(name);
- if(n == NULL)
- throw SettingNotFoundException("Setting not found");
-
- return n->getValue();
+ return m_list;
}
- bool getBool(std::string name)
+protected:
+ JMutex m_mutex;
+ core::list<T> m_list;
+};
+
+/*
+ A single worker thread - multiple client threads queue framework.
+*/
+
+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()
{
- return is_yes(get(name));
}
- // Asks if empty
- bool getBoolAsk(std::string name, std::string question, bool def)
+ Key key;
+ ResultQueue<Key, T, Caller, CallerData> *dest;
+ core::list<CallerInfo<Caller, CallerData> > callers;
+};
+
+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)
{
- std::string s = get(name);
- if(s != "")
- return is_yes(s);
+ JMutexAutoLock lock(m_queue.getMutex());
- char templine[10];
- std::cout<<question<<" [y/N]: ";
- std::cin.getline(templine, 10);
- s = templine;
+ /*
+ 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(s == "")
- return def;
+ 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;
+ }
+ }
- return is_yes(s);
- }
+ /*
+ Else add a new request to the queue
+ */
- float getFloat(std::string name)
- {
- float f;
- std::istringstream vis(get(name));
- vis>>f;
- return f;
+ 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);
}
- u16 getU16(std::string name)
+ GetRequest<Key, T, Caller, CallerData> pop(bool wait_if_empty=false)
{
- return stoi(get(name), 0, 65535);
+ return m_queue.pop_front(wait_if_empty);
}
- u16 getU16Ask(std::string name, std::string question, u16 def)
+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
+
+int myrand_range(int min, int max);
+
+/*
+ Miscellaneous functions
+*/
+
+bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
+ f32 camera_fov, f32 range, f32 *distance_ptr=NULL);
+
+/*
+ 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)
{
- std::string s = get(name);
- if(s != "")
- return stoi(s, 0, 65535);
+ // Check if already exists
+ if(m_map.find(value) != NULL)
+ return false;
+
+ // Add
+ m_map.insert(value, 0);
+ m_list.push_back(value);
- char templine[10];
- std::cout<<question<<" ["<<def<<"]: ";
- std::cin.getline(templine, 10);
- s = templine;
+ return true;
+ }
- if(s == "")
- return def;
+ 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;
+ }
- return stoi(s, 0, 65535);
+ u32 size()
+ {
+ assert(m_list.size() == m_map.size());
+ return m_list.size();
}
- s16 getS16(std::string name)
+private:
+ core::map<Value, u8> m_map;
+ core::list<Value> m_list;
+};
+
+#if 1
+template<typename Key, typename Value>
+class MutexedMap
+{
+public:
+ MutexedMap()
{
- return stoi(get(name), -32768, 32767);
+ m_mutex.Init();
+ assert(m_mutex.IsInitialized());
}
+
+ void set(const Key &name, const Value &value)
+ {
+ JMutexAutoLock lock(m_mutex);
- s32 getS32(std::string name)
+ m_values[name] = value;
+ }
+
+ bool get(const Key &name, Value *result)
{
- return stoi(get(name));
+ JMutexAutoLock lock(m_mutex);
+
+ typename core::map<Key, Value>::Node *n;
+ n = m_values.find(name);
+
+ if(n == NULL)
+ return false;
+
+ if(result != NULL)
+ *result = n->getValue();
+
+ return true;
}
private:
- core::map<std::string, std::string> m_settings;
+ core::map<Key, Value> m_values;
+ JMutex m_mutex;
};
+#endif
/*
- A thread-safe texture cache.
+ Generates ids for comparable values.
+ Id=0 is reserved for "no value".
- This is used so that irrlicht doesn't get called from many threads
-*/
+ Is fast at:
+ - Returning value by id (very fast)
+ - Returning id by value
+ - Generating a new id for a value
-class TextureCache
+ Is not able to:
+ - Remove an id/value pair (is possible to implement but slow)
+*/
+template<typename T>
+class MutexedIdGenerator
{
public:
- TextureCache()
+ MutexedIdGenerator()
{
m_mutex.Init();
assert(m_mutex.IsInitialized());
}
- void set(std::string name, video::ITexture *texture)
+ // Returns true if found
+ bool getValue(u32 id, T &value)
{
+ if(id == 0)
+ return false;
JMutexAutoLock lock(m_mutex);
-
- m_textures[name] = texture;
+ if(m_id_to_value.size() < id)
+ return false;
+ value = m_id_to_value[id-1];
+ return true;
}
- video::ITexture* get(std::string name)
+ // If id exists for value, returns the id.
+ // Otherwise generates an id for the value.
+ u32 getId(const T &value)
{
JMutexAutoLock lock(m_mutex);
-
- core::map<std::string, video::ITexture*>::Node *n;
- n = m_textures.find(name);
-
+ typename core::map<T, u32>::Node *n;
+ n = m_value_to_id.find(value);
if(n != NULL)
return n->getValue();
-
- return NULL;
+ 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:
- core::map<std::string, video::ITexture*> m_textures;
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;
+}
+
+/*
+ Forcefully wraps string into rows using \n
+ (no word wrap, used for showing paths in gui)
+*/
+inline std::string wrap_rows(const std::string &from, u32 rowlen)
+{
+ std::string to;
+ for(u32 i=0; i<from.size(); i++)
+ {
+ if(i != 0 && i%rowlen == 0)
+ to += '\n';
+ to += from[i];
+ }
+ return to;
+}
+
+/*
+ Some helper stuff
+*/
+#define MYMIN(a,b) ((a)<(b)?(a):(b))
+#define MYMAX(a,b) ((a)>(b)?(a):(b))
+
+/*
+ Returns integer position of node in given floating point position
+*/
+inline v3s16 floatToInt(v3f p, f32 d)
+{
+ v3s16 p2(
+ (p.X + (p.X>0 ? d/2 : -d/2))/d,
+ (p.Y + (p.Y>0 ? d/2 : -d/2))/d,
+ (p.Z + (p.Z>0 ? d/2 : -d/2))/d);
+ return p2;
+}
+
+/*
+ Returns floating point position of node in given integer position
+*/
+inline v3f intToFloat(v3s16 p, f32 d)
+{
+ v3f p2(
+ (f32)p.X * d,
+ (f32)p.Y * d,
+ (f32)p.Z * d
+ );
+ return p2;
+}
+
+/*
+ More serialization stuff
+*/
+
+// Creates a string with the length as the first two bytes
+inline std::string serializeString(const std::string &plain)
+{
+ //assert(plain.size() <= 65535);
+ if(plain.size() > 65535)
+ throw SerializationError("String too long for serializeString");
+ char buf[2];
+ writeU16((u8*)&buf[0], plain.size());
+ std::string s;
+ s.append(buf, 2);
+ s.append(plain);
+ return s;
+}
+
+// Creates a string with the length as the first two bytes from wide string
+inline std::string serializeWideString(const std::wstring &plain)
+{
+ //assert(plain.size() <= 65535);
+ if(plain.size() > 65535)
+ throw SerializationError("String too long for serializeString");
+ char buf[2];
+ writeU16((u8*)buf, plain.size());
+ std::string s;
+ s.append(buf, 2);
+ for(u32 i=0; i<plain.size(); i++)
+ {
+ writeU16((u8*)buf, plain[i]);
+ s.append(buf, 2);
+ }
+ return s;
+}
+
+// Reads a string with the length as the first two bytes
+inline std::string deSerializeString(std::istream &is)
+{
+ char buf[2];
+ is.read(buf, 2);
+ if(is.gcount() != 2)
+ throw SerializationError("deSerializeString: size not read");
+ u16 s_size = readU16((u8*)buf);
+ if(s_size == 0)
+ return "";
+ Buffer<char> buf2(s_size);
+ is.read(&buf2[0], s_size);
+ std::string s;
+ s.reserve(s_size);
+ s.append(&buf2[0], s_size);
+ return s;
+}
+
+// Reads a wide string with the length as the first two bytes
+inline std::wstring deSerializeWideString(std::istream &is)
+{
+ char buf[2];
+ is.read(buf, 2);
+ if(is.gcount() != 2)
+ throw SerializationError("deSerializeString: size not read");
+ u16 s_size = readU16((u8*)buf);
+ if(s_size == 0)
+ return L"";
+ std::wstring s;
+ s.reserve(s_size);
+ for(u32 i=0; i<s_size; i++)
+ {
+ is.read(&buf[0], 2);
+ wchar_t c16 = readU16((u8*)buf);
+ s.append(&c16, 1);
+ }
+ return s;
+}
+
+// Creates a string with the length as the first four bytes
+inline std::string serializeLongString(const std::string &plain)
+{
+ char buf[4];
+ writeU32((u8*)&buf[0], plain.size());
+ std::string s;
+ s.append(buf, 4);
+ s.append(plain);
+ return s;
+}
+
+// Reads a string with the length as the first four bytes
+inline std::string deSerializeLongString(std::istream &is)
+{
+ char buf[4];
+ is.read(buf, 4);
+ if(is.gcount() != 4)
+ throw SerializationError("deSerializeLongString: size not read");
+ u32 s_size = readU32((u8*)buf);
+ if(s_size == 0)
+ return "";
+ Buffer<char> buf2(s_size);
+ is.read(&buf2[0], s_size);
+ std::string s;
+ s.reserve(s_size);
+ s.append(&buf2[0], s_size);
+ return s;
+}
+
+//
+
+inline u32 time_to_daynight_ratio(u32 time_of_day)
+{
+ const s32 daylength = 16;
+ const s32 nightlength = 6;
+ const s32 daytimelength = 8;
+ s32 d = daylength;
+ s32 t = (((time_of_day)%24000)/(24000/d));
+ if(t < nightlength/2 || t >= d - nightlength/2)
+ //return 300;
+ return 350;
+ else if(t >= d/2 - daytimelength/2 && t < d/2 + daytimelength/2)
+ return 1000;
+ else
+ return 750;
+}
+
+// Random helper. Usually d=BS
+inline core::aabbox3d<f32> getNodeBox(v3s16 p, float d)
+{
+ return core::aabbox3d<f32>(
+ (float)p.X * d - 0.5*d,
+ (float)p.Y * d - 0.5*d,
+ (float)p.Z * d - 0.5*d,
+ (float)p.X * d + 0.5*d,
+ (float)p.Y * d + 0.5*d,
+ (float)p.Z * d + 0.5*d
+ );
+}
+
+class IntervalLimiter
+{
+public:
+ IntervalLimiter():
+ m_accumulator(0)
+ {
+ }
+ /*
+ dtime: time from last call to this method
+ wanted_interval: interval wanted
+ return value:
+ true: action should be skipped
+ false: action should be done
+ */
+ bool step(float dtime, float wanted_interval)
+ {
+ m_accumulator += dtime;
+ if(m_accumulator < wanted_interval)
+ return false;
+ m_accumulator -= wanted_interval;
+ return true;
+ }
+protected:
+ float m_accumulator;
};
+std::string translatePassword(std::string playername, std::wstring password);
+
#endif