/* 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 "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); 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 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(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)"< &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 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 IrrlichtWrapper; 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(); SharedBuffer 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 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; } 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; } 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<::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) { 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<<"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) { 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<::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()); } } Settings & operator=(Settings &other) { if(&other == this) return *this; clear(); (*this) += other; return *this; } private: core::map m_settings; core::map m_defaults; }; /* FIFO queue */ template 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::Iterator begin = m_list.begin(); T t = *begin; m_list.erase(begin); return t; } u32 size() { return m_list.size(); } protected: core::list m_list; }; /* Thread-safe FIFO queue */ template 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::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 & getList() { return m_list; } protected: JMutex m_mutex; core::list m_list; }; 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; }; /* Quickhands for typical request-result queues. Used for distributing work between threads. */ 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(min > 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 0 template 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::Node *n; n = m_values.find(name); if(n == NULL) return false; *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)) #endif