+ core::map<std::string, std::string> 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<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");
+
+ 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;
+ }
+
+ u32 size()
+ {
+ 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(;;)
+ {
+ {
+ 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;
+
+ 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;
+ }
+ }
+
+ JMutex & getMutex()
+ {
+ return m_mutex;
+ }
+
+ core::list<T> & getList()
+ {
+ return m_list;
+ }
+
+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()
+ {
+ }
+
+ 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)
+ {
+ 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;
+}
+
+/*
+ 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<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;
+}
+
+/*
+ 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;
+}
+
+/*// Reads a string with the length as the first two bytes
+inline std::string deSerializeString(const std::string encoded)
+{
+ u16 s_size = readU16((u8*)&encoded.c_str()[0]);
+ if(s_size > encoded.length() - 2)
+ return "";
+ std::string s;
+ s.reserve(s_size);
+ s.append(&encoded.c_str()[2], s_size);
+ 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;
+}
+
+// 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(const std::string encoded)
+{
+ u32 s_size = readU32((u8*)&encoded.c_str()[0]);
+ if(s_size > encoded.length() - 4)
+ return "";
+ std::string s;
+ s.reserve(s_size);
+ s.append(&encoded.c_str()[4], s_size);
+ 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;