]> git.lizzy.rs Git - minetest.git/blobdiff - src/utility.h
fine-tuning of map generator and server and stuff.
[minetest.git] / src / utility.h
index dabcce87cfd80f0f162dca388de21c4299b298d2..c4f45ba0f1822232379fe617493cd0980352ccd2 100644 (file)
@@ -1,18 +1,38 @@
 /*
-(c) 2010 Perttu Ahola <celeron55@gmail.com>
+Minetest-c55
+Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
+
+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 "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];
 
@@ -372,38 +392,25 @@ class MutexedVariable
        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);
+
 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
@@ -419,12 +426,12 @@ inline void getFacePositions(core::list<v3s16> &list, u16 d)
                /*
                        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
@@ -543,6 +550,41 @@ inline bool isInArea(v2s16 p, s16 d)
        );
 }
 
+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();
@@ -588,25 +630,28 @@ inline float wrapDegrees(float f)
        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")
                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)
@@ -621,10 +666,83 @@ inline s32 stoi(std::string s)
        return atoi(s.c_str());
 }
 
+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:
@@ -667,36 +785,255 @@ class Settings
                return true;
        }
 
-       // Returns true on success
+       /*
+               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();
        }
@@ -706,13 +1043,26 @@ class Settings
                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);
@@ -739,10 +1089,11 @@ class Settings
 
        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);
@@ -764,9 +1115,471 @@ class Settings
                return stoi(get(name));
        }
 
+       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
+
+/*
+       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
+       {
+               v3s16 p;
+               Attribute attr;
+       };
+
+public:
+       ~PointAttributeList()
+       {
+               /*for(core::list<PointWithAttr>::Iterator
+                               i = m_points.begin();
+                               i != m_points.end(); i++)
+               {
+                       PointWithAttr &pwa = *i;
+                       //delete pwa.attr;
+               }*/
+       }
+
+       Attribute getNearAttr(v3s16 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(v2s16 p)
+       {
+               return getNearAttr(v3s16(p.X, 0, p.Y));
+       }
+
+       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(v3s16 p);
+       
+       float getInterpolatedFloat(v2s16 p)
+       {
+               return getInterpolatedFloat(v3s16(p.X, 0, p.Y));
+       }
+       
+       //float getInterpolatedFloat(v3s16 p, s32 range);
+       /*float getInterpolatedFloat(v2s16 p, s32 range)
+       {
+               return getInterpolatedFloat(v3s16(p.X, 0, p.Y), range);
+       }*/
+       
+       void addPoint(v3s16 p, const Attribute &attr)
+       {
+               PointWithAttr pattr;
+               pattr.p = p;
+               pattr.attr = attr;
+               m_points.push_back(pattr);
+       }
+
+       void addPoint(v2s16 p, const Attribute &attr)
+       {
+               addPoint(v3s16(p.X, 0, p.Y), 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);
+
+
 #endif