/* Minetest-c55 Copyright (C) 2010 celeron55, Perttu Ahola 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 UTILITY_HEADER #define UTILITY_HEADER #include #include #include #include #include #include #include #include #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[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 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); } inline u16 readU16(u8 *data) { return (data[0]<<8) | (data[1]<<0); } inline u8 readU8(u8 *data) { return (data[0]<<0); } inline void writeS32(u8 *data, s32 i){ writeU32(data, (u32)i); } inline s32 readS32(u8 *data){ 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); } 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 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); 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; } /* 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, u16 p) { char buf[4]; writeU16((u8*)buf, p); os.write(buf, 4); } inline u16 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[2]; writeF1000((u8*)buf, p); os.write(buf, 2); } inline f32 readF1000(std::istream &is) { char buf[2]; is.read(buf, 2); // TODO: verify if this gets rid of the valgrind warning //if(is.gcount() != 2) // return 0; 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 */ template class SharedPtr { public: SharedPtr(T *t=NULL) { refcount = new int; *refcount = 1; ptr = t; } SharedPtr(SharedPtr &t) { //*this = t; drop(); refcount = t.refcount; (*refcount)++; ptr = t.ptr; } ~SharedPtr() { drop(); } SharedPtr & operator=(T *t) { drop(); refcount = new int; *refcount = 1; ptr = t; return *this; } SharedPtr & operator=(SharedPtr &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; } T & operator[](unsigned int i) { return ptr[i]; } private: void drop() { assert((*refcount) > 0); (*refcount)--; if(*refcount == 0) { delete refcount; if(ptr != NULL) delete ptr; } } T *ptr; int *refcount; }; template 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 class SharedBuffer { public: SharedBuffer() { m_size = 0; data = NULL; refcount = new unsigned int; (*refcount) = 1; } SharedBuffer(unsigned int size) { m_size = size; if(m_size != 0) data = new T[m_size]; else data = NULL; refcount = new unsigned int; (*refcount) = 1; } SharedBuffer(const SharedBuffer &buffer) { //std::cout<<"SharedBuffer(const SharedBuffer &buffer)"< &buffer) { 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; } ~SharedBuffer() { drop(); } T & operator[](unsigned int i) const { //assert(i < m_size) 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) { if(data) delete[] data; delete refcount; } } T *data; unsigned int m_size; unsigned int *refcount; }; inline SharedBuffer SharedBufferFromString(const char *string) { SharedBuffer b((u8*)string, strlen(string)+1); return b; } template 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 */ class TimeTaker { public: TimeTaker(const char *name, u32 *result=NULL); ~TimeTaker() { stop(); } u32 stop(bool quiet=false); u32 getTime(); private: const char *m_name; u32 m_time1; bool m_running; u32 *m_result; }; // Calculates the borders of a "d-radius" cube inline void getFacePositions(core::list &list, u16 d) { if(d == 0) { list.push_back(v3s16(0,0,0)); return; } if(d == 1) { /* 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 // 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=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 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 ( 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 ); } 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(); Buffer wcs(wcl+1); size_t l = mbstowcs(*wcs, mbs.c_str(), wcl); if(l == (size_t)(-1)) return L""; wcs[l] = 0; return *wcs; } inline std::string wide_to_narrow(const std::wstring& wcs) { size_t mbl = wcs.size()*4; SharedBuffer mbs(mbl+1); size_t l = wcstombs(*mbs, wcs.c_str(), mbl); 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 str_split(const std::wstring &str, wchar_t delimiter) { std::vector 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 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; } inline std::string lowercase(const std::string &s) { std::string s2; for(size_t i=0; i= 'A' && c <= 'Z') c -= 'A' - 'a'; s2 += c; } return s2; } inline bool is_yes(const std::string &s) { std::string s2 = lowercase(trim(s)); if(s2 == "y" || s2 == "yes" || s2 == "true" || s2 == "1") return true; return false; } 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; } // 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<::Iterator i = m_settings.getIterator(); i.atEnd() == false; i++) { std::string name = i.getNode()->getKey(); std::string value = i.getNode()->getValue(); os< &dst, core::map &updated) { JMutexAutoLock lock(m_mutex); 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 \""< \""< objects; core::map updated; // Read and modify stuff { std::ifstream is(filename); if(is.good() == false) { dstream<<"INFO: updateConfigFile():" " Error opening configuration file" " for reading: \"" <::Iterator i = objects.begin(); i != objects.end(); i++) { os<<(*i); } /* Write stuff that was not already in the file */ for(core::map::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 \""< &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 \"" < expected."<::Node *n; n = allowed_options.find(name); if(n == NULL) { dstream<<"Unknown command-line parameter \"" <getValue().type; std::string value = ""; if(type == VALUETYPE_FLAG) { value = "true"; } else { if(i >= argc) { dstream<<"Invalid command-line parameter \"" <::Node *n; n = m_settings.find(name); if(n == NULL) { n = m_defaults.find(name); if(n == NULL) { dstream<<"INFO: Settings: Setting not found: \"" <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(exists(name)) return getBool(name); std::string s; char templine[10]; std::cout<>value; return value; } void setBool(std::string name, bool value) { if(value) set(name, "true"); else set(name, "false"); } 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<<"("<::Iterator i = other.m_settings.getIterator(); i.atEnd() == false; i++) { m_settings.insert(i.getNode()->getKey(), i.getNode()->getValue()); } for(core::map::Iterator i = other.m_defaults.getIterator(); i.atEnd() == false; i++) { m_defaults.insert(i.getNode()->getKey(), i.getNode()->getValue()); } return *this; } Settings & operator=(Settings &other) { JMutexAutoLock lock(m_mutex); JMutexAutoLock lock2(other.m_mutex); if(&other == this) return *this; clear(); (*this) += other; return *this; } private: core::map m_settings; core::map m_defaults; // All methods that access m_settings/m_defaults directly should lock this. JMutex m_mutex; }; /* FIFO queue (well, actually a FILO also) */ template 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"); typename core::list::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::Iterator last = m_list.getLast(); T t = *last; m_list.erase(last); return t; } u32 size() { return m_list.size(); } protected: core::list m_list; }; /* Thread-safe FIFO queue (well, actually a FILO also) */ template 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(;;) { { JMutexAutoLock lock(m_mutex); if(m_list.size() > 0) { typename core::list::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; for(;;) { { JMutexAutoLock lock(m_mutex); if(m_list.size() > 0) { typename core::list::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; } } JMutex & getMutex() { return m_mutex; } core::list & getList() { return m_list; } protected: JMutex m_mutex; core::list m_list; }; /* A single worker thread - multiple client threads queue framework. */ template class CallerInfo { public: Caller caller; Data data; }; template class GetResult { public: Key key; T item; core::list > callers; }; template class ResultQueue: public MutexedQueue< GetResult > { }; template class GetRequest { public: GetRequest() { dest = NULL; } GetRequest(ResultQueue *a_dest) { dest = a_dest; } GetRequest(ResultQueue *a_dest, Key a_key) { dest = a_dest; key = a_key; } ~GetRequest() { } Key key; ResultQueue *dest; core::list > callers; }; template class RequestQueue { public: u32 size() { return m_queue.size(); } void add(Key key, Caller caller, CallerData callerdata, ResultQueue *dest) { JMutexAutoLock lock(m_queue.getMutex()); /* If the caller is already on the list, only update CallerData */ for(typename core::list< GetRequest >::Iterator i = m_queue.getList().begin(); i != m_queue.getList().end(); i++) { GetRequest &request = *i; if(request.key == key) { for(typename core::list< CallerInfo >::Iterator i = request.callers.begin(); i != request.callers.end(); i++) { CallerInfo &ca = *i; if(ca.caller == caller) { ca.data = callerdata; return; } } CallerInfo ca; ca.caller = caller; ca.data = callerdata; request.callers.push_back(ca); return; } } /* Else add a new request to the queue */ GetRequest request; request.key = key; CallerInfo ca; ca.caller = caller; ca.data = callerdata; request.callers.push_back(ca); request.dest = dest; m_queue.getList().push_back(request); } GetRequest pop(bool wait_if_empty=false) { return m_queue.pop_front(wait_if_empty); } private: MutexedQueue< GetRequest > 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(max-min > MYRAND_MAX) { dstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"< max) { assert(0); return max; } return (myrand()%(max-min+1))+min; } /* Miscellaneous functions */ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range, f32 *distance_ptr=NULL); /* Queue with unique values with fast checking of value existence */ template 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::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 m_map; core::list m_list; }; #if 1 template class MutexedMap { public: MutexedMap() { 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::Node *n; n = m_values.find(name); if(n == NULL) return false; if(result != NULL) *result = n->getValue(); return true; } private: core::map 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 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::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 m_id_to_value; core::map 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(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 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 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 getNodeBox(v3s16 p, float d) { return core::aabbox3d( (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