/* 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 #include "common_irrlicht.h" #include "debug.h" #include "exceptions.h" #include "porting.h" #include "strfnd.h" // For trim() 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 writeS16(u8 *data, s16 i){ writeU16(data, (u16)i); } inline s16 readS16(u8 *data){ return (s16)readU16(data); } inline void writeS8(u8 *data, s8 i){ writeU8(data, (u8)i); } inline s8 readS8(u8 *data){ return (s8)readU8(data); } inline void writeF1000(u8 *data, f32 i){ writeS32(data, i*1000); } inline f32 readF1000(u8 *data){ return (f32)readS32(data)/1000.; } 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 writeV2F1000(u8 *data, v2f p) { writeF1000(&data[0], p.X); writeF1000(&data[4], p.Y); } inline v2f readV2F1000(u8 *data) { v2f p; p.X = (float)readF1000(&data[0]); p.Y = (float)readF1000(&data[4]); 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, u32 p) { char buf[4]; writeU32((u8*)buf, p); os.write(buf, 4); } inline u32 readU32(std::istream &is) { char buf[4]; is.read(buf, 4); return readU32((u8*)buf); } inline void writeS32(std::ostream &os, s32 p) { char buf[4]; writeS32((u8*)buf, p); os.write(buf, 4); } inline s32 readS32(std::istream &is) { char buf[4]; is.read(buf, 4); return readS32((u8*)buf); } inline void writeS16(std::ostream &os, s16 p) { char buf[2]; writeS16((u8*)buf, p); os.write(buf, 2); } inline s16 readS16(std::istream &is) { char buf[2]; is.read(buf, 2); return readS16((u8*)buf); } inline void writeS8(std::ostream &os, s8 p) { char buf[1]; writeS8((u8*)buf, p); os.write(buf, 1); } inline s8 readS8(std::istream &is) { char buf[1]; is.read(buf, 1); return readS8((u8*)buf); } inline void writeF1000(std::ostream &os, f32 p) { char buf[4]; writeF1000((u8*)buf, p); os.write(buf, 4); } inline f32 readF1000(std::istream &is) { char buf[4]; is.read(buf, 4); 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); } inline void writeV2F1000(std::ostream &os, v2f p) { char buf[8]; writeV2F1000((u8*)buf, p); os.write(buf, 8); } inline v2f readV2F1000(std::istream &is) { char buf[8]; is.read(buf, 8); return readV2F1000((u8*)buf); } inline void writeV2S16(std::ostream &os, v2s16 p) { char buf[4]; writeV2S16((u8*)buf, p); os.write(buf, 4); } inline v2s16 readV2S16(std::istream &is) { char buf[4]; is.read(buf, 4); return readV2S16((u8*)buf); } inline void writeV3S16(std::ostream &os, v3s16 p) { char buf[6]; writeV3S16((u8*)buf, p); os.write(buf, 6); } inline v3s16 readV3S16(std::istream &is) { char buf[6]; is.read(buf, 6); return readV3S16((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() { m_size = 0; data = NULL; } Buffer(unsigned int size) { m_size = size; if(size != 0) data = new T[size]; else data = NULL; } Buffer(const Buffer &buffer) { m_size = buffer.m_size; if(m_size != 0) { data = new T[buffer.m_size]; memcpy(data, buffer.data, buffer.m_size); } else data = NULL; } Buffer(const T *t, unsigned int size) { m_size = size; if(size != 0) { data = new T[size]; memcpy(data, t, size); } else data = NULL; } ~Buffer() { drop(); } Buffer& operator=(const Buffer &buffer) { if(this == &buffer) return *this; drop(); m_size = buffer.m_size; if(m_size != 0) { data = new T[buffer.m_size]; memcpy(data, buffer.data, buffer.m_size); } else data = NULL; return *this; } T & operator[](unsigned int i) const { return data[i]; } T * operator*() const { return data; } unsigned int getSize() const { return m_size; } private: void drop() { if(data) delete[] data; } T *data; unsigned int m_size; }; template void push_RollbackNode(lua_State *L, RollbackNode &node) { lua_createtable(L, 0, 3); lua_pushstring(L, node.name.c_str()); lua_setfield(L, -2, "name"); lua_pushnumber(L, node.param1); lua_setfield(L, -2, "param1"); lua_pushnumber(L, node.param2); lua_setfield(L, -2, "param2"); } // rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...} int ModApiRollback::l_rollback_get_node_actions(lua_State *L) { v3s16 pos = read_v3s16(L, 1); int range = luaL_checknumber(L, 2); time_t seconds = (time_t) luaL_checknumber(L, 3); int limit = luaL_checknumber(L, 4); Server *server = getServer(L); IRollbackManager *rollback = server->getRollbackManager(); if (rollback == NULL) { return 0; } std::list<RollbackAction> actions = rollback->getNodeActors(pos, range, seconds, limit); std::list<RollbackAction>::iterator iter = actions.begin(); lua_createtable(L, actions.size(), 0); for (unsigned int i = 1; iter != actions.end(); ++iter, ++i) { lua_createtable(L, 0, 5); // Make a table with enough space pre-allocated lua_pushstring(L, iter->actor.c_str()); lua_setfield(L, -2, "actor"); push_v3s16(L, iter->p); lua_setfield(L, -2, "pos"); lua_pushnumber(L, iter->unix_time); lua_setfield(L, -2, "time"); push_RollbackNode(L, iter->n_old); lua_setfield(L, -2, "oldnode"); push_RollbackNode(L, iter->n_new); lua_setfield(L, -2, "newnode"); lua_rawseti(L, -2, i); // Add action table to main table } return 1; } // rollback_revert_actions_by(actor, seconds) -> bool, log messages int ModApiRollback::l_rollback_revert_actions_by(lua_State *L) { std::string actor = luaL_checkstring(L, 1); int seconds = luaL_checknumber(L, 2); Server *server = getServer(L); IRollbackManager *rollback = server->getRollbackManager(); // If rollback is disabled, tell it's not a success. if (rollback == NULL) { lua_pushboolean(L, false); lua_newtable(L); return 2; } std::list<RollbackAction> actions = rollback->getRevertActions(actor, seconds); std::list<std::string> log; bool success = server->rollbackRevertActions(actions, &log); // Push boolean result lua_pushboolean(L, success); lua_createtable(L, log.size(), 0); unsigned long i = 0; for(std::list<std::string>::const_iterator iter = log.begin(); iter != log.end(); ++i, ++iter) { lua_pushnumber(L, i); lua_pushstring(L, iter->c_str()); lua_settable(L, -3); } return 2; } void ModApiRollback::Initialize(lua_State *L, int top) { API_FCT(rollback_get_node_actions); API_FCT(rollback_revert_actions_by); } ate 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 int myrand_range(int min, int max); /* Miscellaneous functions */ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 camera_fov, 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; } // Creates a string encoded in JSON format (almost equivalent to a C string literal) std::string serializeJsonString(const std::string &plain); // Reads a string encoded in JSON format std::string deSerializeJsonString(std::istream &is); // 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); enum PointedThingType { POINTEDTHING_NOTHING, POINTEDTHING_NODE, POINTEDTHING_OBJECT }; struct PointedThing { PointedThingType type; v3s16 node_undersurface; v3s16 node_abovesurface; s16 object_id; PointedThing(); std::string dump() const; void serialize(std::ostream &os) const; void deSerialize(std::istream &is); bool operator==(const PointedThing &pt2) const; bool operator!=(const PointedThing &pt2) const; }; #endif