Improve GUIMessageMenu (move and resize elements based on size of text)
[oweals/minetest.git] / src / utility.cpp
index 8b2b78b447f7290f7515acaf7dfd6147863a0925..7c87b9ae4f06e326279c20ac0d31a030964ba724 100644 (file)
@@ -22,8 +22,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "utility.h"
-#include "irrlichtwrapper.h"
 #include "gettime.h"
+#include "sha1.h"
+#include "base64.h"
+#include "log.h"
+#include <iomanip>
 
 TimeTaker::TimeTaker(const char *name, u32 *result)
 {
@@ -46,7 +49,7 @@ u32 TimeTaker::stop(bool quiet)
                else
                {
                        if(quiet == false)
-                               std::cout<<m_name<<" took "<<dtime<<"ms"<<std::endl;
+                               infostream<<m_name<<" took "<<dtime<<"ms"<<std::endl;
                }
                m_running = false;
                return dtime;
@@ -61,6 +64,17 @@ u32 TimeTaker::getTime()
        return dtime;
 }
 
+const v3s16 g_6dirs[6] =
+{
+       // +right, +top, +back
+       v3s16( 0, 0, 1), // back
+       v3s16( 0, 1, 0), // top
+       v3s16( 1, 0, 0), // right
+       v3s16( 0, 0,-1), // front
+       v3s16( 0,-1, 0), // bottom
+       v3s16(-1, 0, 0) // left
+};
+
 const v3s16 g_26dirs[26] =
 {
        // +right, +top, +back
@@ -144,235 +158,20 @@ void mysrand(unsigned seed)
    next = seed;
 }
 
-/*
-       PointAttributeList
-*/
-
-// Float with distance
-struct DFloat
-{
-       float v;
-       u32 d;
-};
-
-float PointAttributeList::getInterpolatedFloat(v2s16 p)
-{
-       const u32 near_wanted_count = 5;
-       // Last is nearest, first is farthest
-       core::list<DFloat> near_list;
-
-       for(core::list<PointWithAttr>::Iterator
-                       i = m_points.begin();
-                       i != m_points.end(); i++)
-       {
-               PointWithAttr &pwa = *i;
-               u32 d = pwa.p.getDistanceFrom(p);
-               
-               DFloat df;
-               df.v = pwa.attr.getFloat();
-               df.d = d;
-                               
-               // If near list is empty, add directly and continue
-               if(near_list.size() == 0)
-               {
-                       near_list.push_back(df);
-                       continue;
-               }
-               
-               // Get distance of farthest in near list
-               u32 near_d = 100000;
-               if(near_list.size() > 0)
-               {
-                       core::list<DFloat>::Iterator i = near_list.begin();
-                       near_d = i->d;
-               }
-               
-               /*
-                       If point is closer than the farthest in the near list or
-                       there are not yet enough points on the list
-               */
-               if(d < near_d || near_list.size() < near_wanted_count)
-               {
-                       // Find the right place in the near list and put it there
-                       
-                       // Go from farthest to near in the near list
-                       core::list<DFloat>::Iterator i = near_list.begin();
-                       for(; i != near_list.end(); i++)
-                       {
-                               // Stop when i is at the first nearer node
-                               if(i->d < d)
-                                       break;
-                       }
-                       // Add df to before i
-                       if(i == near_list.end())
-                               near_list.push_back(df);
-                       else
-                               near_list.insert_before(i, df);
-
-                       // Keep near list at right size
-                       if(near_list.size() > near_wanted_count)
-                       {
-                               core::list<DFloat>::Iterator j = near_list.begin();
-                               near_list.erase(j);
-                       }
-               }
-       }
-       
-       // Return if no values found
-       if(near_list.size() == 0)
-               return 0.0;
-       
-       /*
-20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja 
-lopuks sit otetaan a/b
-       */
-       
-       float a = 0;
-       float b = 0;
-       for(core::list<DFloat>::Iterator i = near_list.begin();
-                       i != near_list.end(); i++)
-       {
-               if(i->d == 0)
-                       return i->v;
-               
-               //float dd = pow((float)i->d, 6);
-               float dd = pow((float)i->d, 5);
-               float v = i->v;
-               //dstream<<"dd="<<dd<<", v="<<v<<std::endl;
-               a += v / dd;
-               b += 1 / dd;
-       }
-
-       return a / b;
-}
-
-#if 0
-float PointAttributeList::getInterpolatedFloat(v3s16 p)
+int myrand_range(int min, int max)
 {
-       const u32 near_wanted_count = 2;
-       const u32 nearest_wanted_count = 2;
-       // Last is near
-       core::list<DFloat> near;
-
-       for(core::list<PointWithAttr>::Iterator
-                       i = m_points.begin();
-                       i != m_points.end(); i++)
+       if(max-min > MYRAND_MAX)
        {
-               PointWithAttr &pwa = *i;
-               u32 d = pwa.p.getDistanceFrom(p);
-               
-               DFloat df;
-               df.v = pwa.attr.getFloat();
-               df.d = d;
-                               
-               // If near list is empty, add directly and continue
-               if(near_list.size() == 0)
-               {
-                       near_list.push_back(df);
-                       continue;
-               }
-               
-               // Get distance of farthest in near list
-               u32 near_d = 100000;
-               if(near_list.size() > 0)
-               {
-                       core::list<DFloat>::Iterator i = near_list.begin();
-                       near_d = i->d;
-               }
-               
-               /*
-                       If point is closer than the farthest in the near list or
-                       there are not yet enough points on the list
-               */
-               if(d < near_d || near_list.size() < near_wanted_count)
-               {
-                       // Find the right place in the near list and put it there
-                       
-                       // Go from farthest to near in the near list
-                       core::list<DFloat>::Iterator i = near_list.begin();
-                       for(; i != near_list.end(); i++)
-                       {
-                               // Stop when i is at the first nearer node
-                               if(i->d < d)
-                                       break;
-                       }
-                       // Add df to before i
-                       if(i == near_list.end())
-                               near_list.push_back(df);
-                       else
-                               near_list.insert_before(i, df);
-
-                       // Keep near list at right size
-                       if(near_list.size() > near_wanted_count)
-                       {
-                               core::list<DFloat>::Iterator j = near_list.begin();
-                               near_list.erase(j);
-                       }
-               }
+               errorstream<<"WARNING: myrand_range: max-min > MYRAND_MAX"<<std::endl;
+               assert(0);
        }
-       
-       // Return if no values found
-       if(near_list.size() == 0)
-               return 0.0;
-       
-       /*
-               Get nearest ones
-       */
-
-       u32 nearest_count = nearest_wanted_count;
-       if(nearest_count > near_list.size())
-               nearest_count = near_list.size();
-       core::list<DFloat> nearest;
+       if(min > max)
        {
-               core::list<DFloat>::Iterator i = near_list.getLast();
-               for(u32 j=0; j<nearest_count; j++)
-               {
-                       nearest.push_front(*i);
-                       i--;
-               }
+               assert(0);
+               return max;
        }
-
-       /*
-               TODO: Try this:
-20:58:29 < tejeez> joka pisteelle a += arvo / etäisyys^6; b += 1 / etäisyys^6; ja 
-lopuks sit otetaan a/b
-       */
-
-       /*
-               Get total distance to nearest points
-       */
-       
-       float nearest_d_sum = 0;
-       for(core::list<DFloat>::Iterator i = nearest.begin();
-                       i != nearest.end(); i++)
-       {
-               nearest_d_sum += (float)i->d;
-       }
-
-       /*
-               Interpolate a value between the first ones
-       */
-
-       dstream<<"nearest.size()="<<nearest.size()<<std::endl;
-
-       float interpolated = 0;
-       
-       for(core::list<DFloat>::Iterator i = nearest.begin();
-                       i != nearest.end(); i++)
-       {
-               float weight;
-               if(nearest_d_sum > 0.001)
-                       weight = (float)i->d / nearest_d_sum;
-               else
-                       weight = 1. / nearest.size();
-               /*dstream<<"i->d="<<i->d<<" nearest_d_sum="<<nearest_d_sum
-                               <<" weight="<<weight<<std::endl;*/
-               interpolated += weight * i->v;
-       }
-
-       return interpolated;
+       return (myrand()%(max-min+1))+min;
 }
-#endif
 
 /*
        blockpos: position of block in block coordinates
@@ -380,7 +179,8 @@ lopuks sit otetaan a/b
        camera_dir: an unit vector pointing to camera direction
        range: viewing range
 */
-bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range)
+bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir,
+               f32 camera_fov, f32 range, f32 *distance_ptr)
 {
        v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE;
        
@@ -399,9 +199,16 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range)
 
        // Total distance
        f32 d = blockpos_relative.getLength();
+
+       if(distance_ptr)
+               *distance_ptr = d;
        
+       // If block is very close, it is always in sight
+       if(d < 1.44*1.44*MAP_BLOCKSIZE*BS/2)
+               return true;
+
        // If block is far away, it's not in sight
-       if(d > range * BS)
+       if(d > range)
                return false;
 
        // Maximum radius of a block
@@ -409,23 +216,237 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 range)
        
        // If block is (nearly) touching the camera, don't
        // bother validating further (that is, render it anyway)
-       if(d > block_max_radius * 1.5)
+       if(d < block_max_radius)
+               return true;
+       
+       // Cosine of the angle between the camera direction
+       // and the block direction (camera_dir is an unit vector)
+       f32 cosangle = dforward / d;
+       
+       // Compensate for the size of the block
+       // (as the block has to be shown even if it's a bit off FOV)
+       // This is an estimate, plus an arbitary factor
+       cosangle += block_max_radius / d * 0.5;
+
+       // If block is not in the field of view, skip it
+       if(cosangle < cos(camera_fov / 2))
+               return false;
+
+       return true;
+}
+
+// Creates a string encoded in JSON format (almost equivalent to a C string literal)
+std::string serializeJsonString(const std::string &plain)
+{
+       std::ostringstream os(std::ios::binary);
+       os<<"\"";
+       for(size_t i = 0; i < plain.size(); i++)
        {
-               // Cosine of the angle between the camera direction
-               // and the block direction (camera_dir is an unit vector)
-               f32 cosangle = dforward / d;
-               
-               // Compensate for the size of the block
-               // (as the block has to be shown even if it's a bit off FOV)
-               // This is an estimate.
-               cosangle += block_max_radius / dforward;
-
-               // If block is not in the field of view, skip it
-               //if(cosangle < cos(FOV_ANGLE/2))
-               if(cosangle < cos(FOV_ANGLE/2. * 4./3.))
-                       return false;
+               char c = plain[i];
+               switch(c)
+               {
+                       case '"': os<<"\\\""; break;
+                       case '\\': os<<"\\\\"; break;
+                       case '/': os<<"\\/"; break;
+                       case '\b': os<<"\\b"; break;
+                       case '\f': os<<"\\f"; break;
+                       case '\n': os<<"\\n"; break;
+                       case '\r': os<<"\\r"; break;
+                       case '\t': os<<"\\t"; break;
+                       default:
+                       {
+                               if(c >= 32 && c <= 126)
+                               {
+                                       os<<c;
+                               }
+                               else
+                               {
+                                       u32 cnum = (u32) (u8) c;
+                                       os<<"\\u"<<std::hex<<std::setw(4)<<std::setfill('0')<<cnum;
+                               }
+                               break;
+                       }
+               }
+       }
+       os<<"\"";
+       return os.str();
+}
+
+// Reads a string encoded in JSON format
+std::string deSerializeJsonString(std::istream &is)
+{
+       std::ostringstream os(std::ios::binary);
+       char c, c2;
+
+       // Parse initial doublequote
+       is >> c;
+       if(c != '"')
+               throw SerializationError("JSON string must start with doublequote");
+
+       // Parse characters
+       for(;;)
+       {
+               c = is.get();
+               if(is.eof())
+                       throw SerializationError("JSON string ended prematurely");
+               if(c == '"')
+               {
+                       return os.str();
+               }
+               else if(c == '\\')
+               {
+                       c2 = is.get();
+                       if(is.eof())
+                               throw SerializationError("JSON string ended prematurely");
+                       switch(c2)
+                       {
+                               default:  os<<c2; break;
+                               case 'b': os<<'\b'; break;
+                               case 'f': os<<'\f'; break;
+                               case 'n': os<<'\n'; break;
+                               case 'r': os<<'\r'; break;
+                               case 't': os<<'\t'; break;
+                               case 'u':
+                               {
+                                       char hexdigits[4+1];
+                                       is.read(hexdigits, 4);
+                                       if(is.eof())
+                                               throw SerializationError("JSON string ended prematurely");
+                                       hexdigits[4] = 0;
+                                       std::istringstream tmp_is(hexdigits, std::ios::binary);
+                                       int hexnumber;
+                                       tmp_is >> std::hex >> hexnumber;
+                                       os<<((char)hexnumber);
+                                       break;
+                               }
+                       }
+               }
+               else
+               {
+                       os<<c;
+               }
+       }
+       return os.str();
+}
+
+// Get an sha-1 hash of the player's name combined with
+// the password entered. That's what the server uses as
+// their password. (Exception : if the password field is
+// blank, we send a blank password - this is for backwards
+// compatibility with password-less players).
+std::string translatePassword(std::string playername, std::wstring password)
+{
+       if(password.length() == 0)
+               return "";
+
+       std::string slt = playername + wide_to_narrow(password);
+       SHA1 sha1;
+       sha1.addBytes(slt.c_str(), slt.length());
+       unsigned char *digest = sha1.getDigest();
+       std::string pwd = base64_encode(digest, 20);
+       free(digest);
+       return pwd;
+}
+
+
+
+PointedThing::PointedThing():
+       type(POINTEDTHING_NOTHING),
+       node_undersurface(0,0,0),
+       node_abovesurface(0,0,0),
+       object_id(-1)
+{}
+
+std::string PointedThing::dump() const
+{
+       std::ostringstream os(std::ios::binary);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               os<<"[nothing]";
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               const v3s16 &u = node_undersurface;
+               const v3s16 &a = node_abovesurface;
+               os<<"[node under="<<u.X<<","<<u.Y<<","<<u.Z
+                       << " above="<<a.X<<","<<a.Y<<","<<a.Z<<"]";
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               os<<"[object "<<object_id<<"]";
+       }
+       else
+       {
+               os<<"[unknown PointedThing]";
        }
+       return os.str();
+}
 
+void PointedThing::serialize(std::ostream &os) const
+{
+       writeU8(os, 0); // version
+       writeU8(os, (u8)type);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               // nothing
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               writeV3S16(os, node_undersurface);
+               writeV3S16(os, node_abovesurface);
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               writeS16(os, object_id);
+       }
+}
+
+void PointedThing::deSerialize(std::istream &is)
+{
+       int version = readU8(is);
+       if(version != 0) throw SerializationError(
+                       "unsupported PointedThing version");
+       type = (PointedThingType) readU8(is);
+       if(type == POINTEDTHING_NOTHING)
+       {
+               // nothing
+       }
+       else if(type == POINTEDTHING_NODE)
+       {
+               node_undersurface = readV3S16(is);
+               node_abovesurface = readV3S16(is);
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               object_id = readS16(is);
+       }
+       else
+       {
+               throw SerializationError(
+                       "unsupported PointedThingType");
+       }
+}
+
+bool PointedThing::operator==(const PointedThing &pt2) const
+{
+       if(type != pt2.type)
+               return false;
+       if(type == POINTEDTHING_NODE)
+       {
+               if(node_undersurface != pt2.node_undersurface)
+                       return false;
+               if(node_abovesurface != pt2.node_abovesurface)
+                       return false;
+       }
+       else if(type == POINTEDTHING_OBJECT)
+       {
+               if(object_id != pt2.object_id)
+                       return false;
+       }
        return true;
 }
 
+bool PointedThing::operator!=(const PointedThing &pt2) const
+{
+       return !(*this == pt2);
+}