diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d64ae17..6c7fa0a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -128,6 +128,7 @@ set(common_SRCS base64.cpp ban.cpp clans.cpp + teleports.cpp ) # This gives us the icon @@ -175,6 +176,81 @@ set(minetestserver_SRCS servermain.cpp ) +# Want to see header files in solution explorer +if(MSVC) + set(common_HDRS + log.h + content_sao.h + mapgen.h + content_inventory.h + content_nodemeta.h + content_craft.h + content_mapnode.h + auth.h + collision.h + nodemetadata.h + serverobject.h + noise.h + mineral.h + porting.h + materials.h + defaultsettings.h + mapnode.h + voxel.h + inventory.h + debug.h + serialization.h + light.h + filesys.h + connection.h + environment.h + server.h + servercommand.h + socket.h + mapblock.h + mapsector.h + map.h + player.h + utility.h + test.h + sha1.h + base64.h + ban.h + clans.h + teleports.h + ) + set(minetest_HDRS + ${common_HDRS} + MyBillboardSceneNode.h + content_mapblock.h + content_cao.h + mapblock_mesh.h + farmesh.h + keycode.h + camera.h + clouds.h + clientobject.h + guiMainMenu.h + guiKeyChangeMenu.h + guiMessageMenu.h + guiTextInputMenu.h + guiInventoryMenu.h + guiPauseMenu.h + guiPasswordChange.h + guiDeathScreen.h + client.h + tile.h + game.h + main.h + ) + set(minetestserver_HDRS + ${common_HDRS} + ) + set(common_SRCS ${common_SRCS} ${common_HDRS}) + set(minetest_SRCS ${minetest_SRCS} ${minetest_HDRS}) + set(minetestserver_SRCS ${minetestserver_SRCS} ${minetestserver_HDRS}) +endif() + include_directories( ${PROJECT_BINARY_DIR} ${IRRLICHT_INCLUDE_DIR} diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 3e1c98e..bfc50db 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -111,5 +111,7 @@ void set_default_settings(Settings *settings) settings->setDefault("enable_experimental", "false"); settings->setDefault("crafted_teleports", "4"); settings->setDefault("build_on_borders", "false"); + settings->setDefault("teleport_allow_coords", "false"); + settings->setDefault("teleport_allow_oneway", "true"); } diff --git a/src/environment.h b/src/environment.h index cb076ae..09cd63f 100644 --- a/src/environment.h +++ b/src/environment.h @@ -38,6 +38,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "utility.h" #include "activeobject.h" #include "clans.h" +#include "teleports.h" class Server; class ActiveBlockModifier; @@ -86,6 +87,7 @@ public: //j ClansManager clansManager; + protected: // peer_ids in here should be unique, except that there may be many 0s core::list m_players; diff --git a/src/game.cpp b/src/game.cpp index 1b2b5dc..4482a49 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1809,24 +1809,24 @@ void the_game( { // meta/infotext contains text inside "" quotes. // find 3rd comma - int icomma=infotext.find(L','); + /*int icomma=infotext.find(L','); if(icomma>0) icomma=infotext.find(L',',icomma+1); if(icomma>0) - icomma=infotext.find(L',',icomma+1); + icomma=infotext.find(L',',icomma+1);*/ - if(!canModify) - { - if(icomma<0) - infotext = L"Unnamed teleport"; - else - infotext=L"Teleport: "+infotext.substr(icomma+1,infotext.length()-icomma-2); - } - else - { - if(icomma<0) - infotext = infotext.substr(0,infotext.length()-1)+L",Unnamed\""; - } + //if(!canModify) + //{ + // if(icomma<0) + // infotext = L"Unnamed teleport"; + // else + // infotext=L"Teleport: "+infotext.substr(icomma+1,infotext.length()-icomma-2); + //} + //else + //{ + // if(icomma<0) + // infotext = infotext.substr(0,infotext.length()-1)+L",Unnamed\""; + //} } else if(content == CONTENT_BORDERSTONE) diff --git a/src/map.cpp b/src/map.cpp index a47a877..92c1694 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -30,6 +30,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "mapgen.h" #include "nodemetadata.h" +#include "content_nodemeta.h" #include "content_mapnode.h" #ifndef SERVER #include @@ -1177,6 +1178,19 @@ void Map::removeNodeAndUpdate(v3s16 p, light_sources, modified_blocks); } + + //j + //If removing a teleport -> remove its name from manager + try{ + ServerMap& smap = dynamic_cast(*this); + if(getNode(p).getContent() == CONTENT_TELEPORT){ + SignNodeMetadata* meta = (SignNodeMetadata*)getNodeMetadata(p); + TeleportInfo ti; + if(meta && getTeleportInfo(ti,meta->getText(),true,true,true) && !ti.thisName.empty()) + smap.teleportsManager.removeNoEx(ti.thisName,p); + } + }catch(std::bad_cast&){} + /* Remove node metadata */ @@ -1923,7 +1937,7 @@ void Map::nodeMetadataStep(float dtime, ServerMap::ServerMap(std::string savedir): Map(dout_server), m_seed(0), - m_map_metadata_changed(true), + /*m_map_metadata_changed(true),*/ m_database(NULL), m_database_read(NULL), m_database_write(NULL) @@ -2853,10 +2867,10 @@ void ServerMap::save(bool only_changed) infostream<<"ServerMap: Saving whole map, this can take time." <getMap().getNode(where).getContent() == CONTENT_TELEPORT) - meta = (SignNodeMetadata*)m_env->getMap().getNodeMetadata(where); + if(map.getNode(where).getContent() == CONTENT_TELEPORT) + meta = (SignNodeMetadata*)map.getNodeMetadata(where); else { // check player "head block" where.Y++; if(where.YgetMap().getNode(where).getContent() == CONTENT_TELEPORT) - meta = (SignNodeMetadata*)m_env->getMap().getNodeMetadata(where); + if(map.getNode(where).getContent() == CONTENT_TELEPORT) + meta = (SignNodeMetadata*)map.getNodeMetadata(where); } if(meta){ std::string text = meta->getText(); if(text == "") return false; - str_replace_char(text,',',' '); - std::istringstream is(text); - is >> tgt.X >> tgt.Y >> tgt.Z; - if( tgt.X >= MAP_GENERATION_LIMIT || tgt.X <= -MAP_GENERATION_LIMIT || - tgt.Y >= MAP_GENERATION_LIMIT || tgt.Y <= -MAP_GENERATION_LIMIT || - tgt.Z >= MAP_GENERATION_LIMIT || tgt.Z <= -MAP_GENERATION_LIMIT || - ( tgt.X == 0 && tgt.Y == 0 && tgt.Z == 0 && text.substr(0,5) != "0 0 0" ) - ) return false; - // actionstream<<"It points to: "<<"("<getBool("teleport_allow_coords"); + static const bool allowOneWay = g_settings->getBool("teleport_allow_oneway"); - tgt.X = core::round_(tgt.X); - tgt.Y = core::round_(tgt.Y); - tgt.Z = core::round_(tgt.Z); + TeleportInfo ti; + if( !getTeleportInfo(ti, text, allowCoords, true, true) ) return false; + + try{ + //first, check if teleport has coords target (if allowed) + if(allowCoords && ti.targetLocation.X != TELEPORT_IGNORE){ + tgt.X = ti.targetLocation.X; + tgt.Y = ti.targetLocation.Y; + tgt.Z = ti.targetLocation.Z; + return true; + } + + //next, check if it is one-way teleport (if allowed) + if(allowOneWay && !ti.targetName.empty()){ + //one way teleport + v3s16 target = map.teleportsManager.getTarget(ti.targetName,where); + tgt.X = target.X; + tgt.Y = target.Y; + tgt.Z = target.Z; + return true; + } - return true; + //last, check if it is two-way teleport + if(!ti.thisName.empty()){ + //one way teleport + v3s16 target = map.teleportsManager.getTarget(ti.thisName,where); + tgt.X = target.X; + tgt.Y = target.Y; + tgt.Z = target.Z; + return true; + } + + //if we are here, that's bad... + return false; + + }catch(TeleportManagerException&){ + return false; + } } return false; } @@ -1860,13 +1890,6 @@ bool getTeleportTarget(/*const*/ ServerEnvironment *m_env,/*in+out*/ v3s16 &wher static void getTeleportDirection(const MapNode& in, const MapNode& out, Player& player) { -#if 1 -#define JLOG(x) std::cout << x << std::endl -#define JV3(x) '[' << x.X << ',' << x.Y << ',' << x.Z << ']' -#else -#define JLOG(x) -#endif - JLOG("---------------"); JLOG("old pitch: " << player.getPitch()); @@ -2330,7 +2353,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) v3s16 posi_temp = tele_posi; try{ v3f tgtf; - if(getTeleportTarget(&m_env,posi_temp,tgtf)){ + if(getTeleportTarget(m_env,posi_temp,tgtf)){ if(player->lastTeleportPos == tgtf*BS) return; //already checked... and failed, so skip checks. player->lastTeleportPos=tgtf*BS; @@ -2352,27 +2375,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(content_features(c1).walkable || content_features(c2).walkable) return; - if(teleport_option > 1){ - posi_temp=floatToInt(tgtf, 1); - bool loop=false, first=true; - while(!loop && teleport_option>1) - { //check if there is teleport at primary teleport target, and it loops back. - v3f tgtfnext; - if(getTeleportTarget(&m_env,posi_temp,tgtfnext)){ - if(first){ - JLOG("wyjscie: " << JV3(posi_temp)); - getTeleportDirection(entry,m_env.getMap().getNode(posi_temp),*player); - } - posi_temp=floatToInt(tgtfnext, 1); - loop=(tele_posi.getDistanceFrom(posi_temp)<=1); // tile away - //actionstream<<" D:"< 1){ + // posi_temp=floatToInt(tgtf, 1); + // bool loop=false, first=true; + // while(!loop && teleport_option>1) + // { //check if there is teleport at primary teleport target, and it loops back. + // v3f tgtfnext; + // if(getTeleportTarget(m_env,posi_temp,tgtfnext)){ + // if(first){ + // JLOG("wyjscie: " << JV3(posi_temp)); + // getTeleportDirection(entry,m_env.getMap().getNode(posi_temp),*player); + // } + // posi_temp=floatToInt(tgtfnext, 1); + // loop=(tele_posi.getDistanceFrom(posi_temp)<=1); // tile away + // //actionstream<<" D:"<1 && text[0]=='#'){ if( node.getContent() == CONTENT_BORDERSTONE ){ @@ -3267,20 +3301,35 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) //j teleport - //std::string st = text; - //str_replace_char(st,',',' '); - //float f[3]; - //std::istringstream is(st); - //is >> f[0] >> f[1] >> f[2]; + static const bool allowCoords = g_settings->getBool("teleport_allow_coords"); + static const bool allowOneWay = g_settings->getBool("teleport_allow_oneway"); - ////TODO: map limits! - //for(int i=0; i<3; i++) - // if(f[i]>32000 || f[i]<-32000) f[i]=0; + //delete old name if exists + TeleportInfo tl; + if(getTeleportInfo(tl,signmeta->getText(),false,false,true) && !tl.thisName.empty()){ + m_env.getServerMap().teleportsManager.removeNoEx(tl.thisName,p); + } - //text = ftos(f[0]) + "," + ftos(f[1]) + "," + ftos(f[2]); + //add new name if possible + TeleportInfo ti; + if(getTeleportInfo(ti,text,allowCoords,allowOneWay,true)){ + //if(allowCoords && ti.targetLocation.X != TELEPORT_IGNORE){ + // //target = coordinates + // //don't add anything + //} + //if(allowOneWay && !ti.targetName.empty()){ + // //target = name (one-way) + // //don't add anything too + //} + if(!ti.thisName.empty()){ + //this node will have a name + //add this + m_env.getServerMap().teleportsManager.addNoEx(ti.thisName,p); + } + + } } - SignNodeMetadata *signmeta = (SignNodeMetadata*)meta; signmeta->setText(text); actionstream<getName()<<" writes \""< -std::string trim(const std::string &str); +inline std::string trim(const std::string& source, const char* delims = " \t\r\n", const int delims_cnt = 4) { + + std::string::size_type first = source.find_first_not_of(delims,0,delims_cnt); + + if(first==std::string::npos) return ""; + + std::string::size_type last = source.find_last_not_of(delims,std::string::npos,delims_cnt); + + return source.substr(first,last-first+1); +} class Strfnd{ std::string tek; @@ -124,43 +133,5 @@ public: } }; -inline std::string trim(const std::string &s) -{ - std::string str = s; - while( - str.length()>0 - && - ( - str.substr(0, 1)==" " || - str.substr(0, 1)=="\t" || - str.substr(0, 1)=="\r" || - str.substr(0, 1)=="\n" || - str.substr(str.length()-1, 1)==" " || - str.substr(str.length()-1, 1)=="\t" || - str.substr(str.length()-1, 1)=="\r" || - str.substr(str.length()-1, 1)=="\n" - ) - ) - { - if (str.substr(0, 1)==" ") - str = str.substr(1,str.length()-1); - else if (str.substr(0, 1)=="\t") - str = str.substr(1,str.length()-1); - else if (str.substr(0, 1)=="\r") - str = str.substr(1,str.length()-1); - else if (str.substr(0, 1)=="\n") - str = str.substr(1,str.length()-1); - else if (str.substr(str.length()-1, 1)==" ") - str = str.substr(0,str.length()-1); - else if (str.substr(str.length()-1, 1)=="\t") - str = str.substr(0,str.length()-1); - else if (str.substr(str.length()-1, 1)=="\r") - str = str.substr(0,str.length()-1); - else if (str.substr(str.length()-1, 1)=="\n") - str = str.substr(0,str.length()-1); - } - return str; -} - #endif diff --git a/src/teleports.cpp b/src/teleports.cpp new file mode 100644 index 0000000..598cbb1 --- /dev/null +++ b/src/teleports.cpp @@ -0,0 +1,218 @@ +/* +Minetest-c55 +Copyright (C) 2011 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. + +Author: Jan Cychnerski +*/ + +#include "teleports.h" +#include "settings.h" + +bool TeleportsManager::addNoEx(const std::string& name, const v3s16& coords) +{ + JLOG("Probuje dodac nazwe " << name << " na " << JV3(coords) ); + + if(coords.X == TELEPORT_IGNORE) return false; + + TeleportLink& link = m_links[name]; + + int i; + for(i=0;link.coords[i].X != TELEPORT_IGNORE;i++); //find first empty slot for coords + if(i>=2) return false; + + for(int j=0;j<2;j++) //if already exists -> report success + if(link.coords[j] == coords) return true; + + JLOG("Dodaje nazwe " << name << " na " << JV3(coords) ); + + link.coords[i] = coords; + return true; +} + +bool TeleportsManager::removeNoEx(const std::string& name, const v3s16& coords) +{ + JLOG("Usuwam " << name << " z " << JV3(coords) << "..."); + links_t::iterator it = m_links.find(name); + if(it==m_links.end()) return false; + + TeleportLink& link = it->second; + + if(link.coords[0] == coords){ + if(link.coords[1].X == TELEPORT_IGNORE){ + //remove entire link + m_links.erase(name); + return true; + } + link.coords[0] = link.coords[1]; + link.coords[1].X = TELEPORT_IGNORE; + }else if(link.coords[1] == coords){ + link.coords[1].X = TELEPORT_IGNORE; + }else if(link.coords[0].X == TELEPORT_IGNORE && link.coords[1].X == TELEPORT_IGNORE){ + m_links.erase(name); + }else return false; + + return true; +} + +bool TeleportsManager::renameNoEx(const std::string& oldName, const v3s16& coords, const std::string& newName) +{ + JLOG("Zmieniam nazwe " << oldName << " na " << newName); + removeNoEx(oldName,coords); + if(!addNoEx(newName,coords)) return false; + return true; +} + +const v3s16& TeleportsManager::getTarget(const std::string& name, const v3s16& not_this) const +{ + JLOG("Odczytuje cel " << name); + links_t::const_iterator it = m_links.find(name); + if(it==m_links.end()) throw TeleportManagerException(); + + const TeleportLink& link = it->second; + + for(int i=0; i<2; i++) + if( link.coords[i].X != TELEPORT_IGNORE && link.coords[i] != not_this) + return link.coords[i]; + + throw TeleportManagerException(); +} + +const v3s16& TeleportsManager::getTarget(const std::string& name) const +{ + static const v3s16 nowhere(-32768,-32768,-32768); + return getTarget(name,nowhere); +} + + +void TeleportsManager::save(Settings& args) const +{ + std::ostringstream os; + for(links_t::const_iterator it=m_links.begin(); it!=m_links.end(); it++){ + const TeleportLink& t = it->second; + os << it->first << ' '; //name + for(int i=0; i<2; i++) //coords + if(t.coords[i].X == TELEPORT_IGNORE) //if coord ignored - don't save Y and Z + os << TELEPORT_IGNORE << ' '; + else os << t.coords[i].X << ' ' << t.coords[i].Y << ' ' << t.coords[i].Z << ' '; + } + args.set("teleports",os.str()); +} + +void TeleportsManager::load(Settings& args) +{ + try{ + std::string s = args.get("teleports"); + std::istringstream is(s); + while(is.good() && !is.eof()){ + std::string name; + is >> name; + if(name.length() == 0) continue; + if(is.bad() || is.eof()) break; + for(int i=0; i<2; i++){ + v3s16 v; + is >> v.X; + if(v.X != TELEPORT_IGNORE){ + is >> v.Y >> v.Z; + addNoEx(name,v); + } + } + } + }catch(...){ + //TODO: what to do? + } +} + + + +bool getTeleportInfo(TeleportInfo& ti, const std::string& text, bool allowCoords, bool allowUnnamed, bool ignoreDescription) +{ + //possible values: + //a. X,Y,Z + //b. X,Y,Z,description + //c. name + //d. name,description + //e. name->targetname + //f. name->targetname,description + //g. ->targetname + //h. ->targetname,description + //SPACES allowed EVERYWHERE! (but first char maybe) + //COMMAS allowed in DESCRIPTION + //(so may be "myhouse->mine,Some very, very, very strange description") + //a,b correct only if allowCoords=true + //g,h correct only if allowUnnamed=true + + JLOG("getTeleportInfo(" << text << ")"); +#define JLOGTI(x) JLOG( "ti: " << x.thisName << " -> " << x.targetName << " " << JV3(x.targetLocation) << " - " << x.description) + + if(text.empty())return false; + + const int maxCnt = allowCoords ? 4 : 2; + std::vector parts = str_split(text,',',maxCnt); + + //if(parts.size()==0) return false; //impossible? + if(!allowUnnamed && parts[0].empty()) return false; + + if(allowCoords && parts.size() >= 3){ //possibly coords (a or b) + try{ + v3s16 tgt; + + tgt.X = core::round_( stof_ex(parts[0]) ); + tgt.Y = core::round_( stof_ex(parts[1]) ); + tgt.Z = core::round_( stof_ex(parts[2]) ); + + if( tgt.X >= MAP_GENERATION_LIMIT || tgt.X <= -MAP_GENERATION_LIMIT || + tgt.Y >= MAP_GENERATION_LIMIT || tgt.Y <= -MAP_GENERATION_LIMIT || + tgt.Z >= MAP_GENERATION_LIMIT || tgt.Z <= -MAP_GENERATION_LIMIT + ) return false; //bad coords! + + ti.targetLocation = tgt; //good coords + + if(!ignoreDescription && parts.size()==4) //description is given? + ti.description = parts[3]; + + JLOG("Koordynaty: " << JV3(tgt)); + + JLOGTI(ti); + return true; + }catch(std::ios::failure&){ //bad number format + JLOG("Bad number format"); + } + } + + //find if parts[0] has target name + int pos = parts[0].find("->"); + + if(!allowUnnamed && pos==0) JLOGAND("no 'thisname'", return false;) //no 'thisname' specified + + if(pos != std::string::npos){ //yes, split it to 'thisname' and 'targetname' + if(pos>0) ti.thisName = trim(parts[0].substr(0,pos)); + if(pos+21){ + //yes, desc is the rest of 'parts' + for(unsigned i=1; i + +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. +*/ + +/* + teleports.h - author: Jan Cychnerski +*/ + +#ifndef TELEPORTS_HEADER +#define TELEPORTS_HEADER + +#include "common_irrlicht.h" +#include "utility.h" + +#include +#include +#include + +#define TELEPORT_IGNORE -32768 + +class Settings; + +class TeleportManagerException : public BaseException { +public: + TeleportManagerException():BaseException("Teleport manager exception"){} +}; + +struct TeleportLink { + v3s16 coords[2]; //if X == TELEPORT_IGNORE then ignore it! + //std::string name; + + TeleportLink(){ + coords[0].X = TELEPORT_IGNORE; + coords[1].X = TELEPORT_IGNORE; + } +}; + +class TeleportsManager { +public: + + //NOTE: all methods (except *NoEx) throw if name is not defined! + + //returns false if error + bool addNoEx(const std::string& name, const v3s16& coords); + + bool removeNoEx(const std::string& name, const v3s16& coords); + bool renameNoEx(const std::string& oldName, const v3s16& coords, const std::string& newName); + + //returns coords of teleport, but not equal to not_this (if many, returns first) + const v3s16& getTarget(const std::string& name, const v3s16& not_this) const; + + //returns coords of this name + const v3s16& getTarget(const std::string& name) const; + + void save(Settings& args) const; + void load(Settings& args); + +private: + typedef std::map links_t; + links_t m_links; +}; + + + +#if 1 +#define JLOG(x) std::cout << x << std::endl +#define JV3(x) '[' << x.X << ',' << x.Y << ',' << x.Z << ']' +#define JLOGAND(x,y) { JLOG(x); y } +#else +#define JLOG(x) +#define JLOGAND(x,y) y +#endif + +struct TeleportInfo { + std::string thisName, targetName, description; + v3s16 /*thisLocation,*/ targetLocation; + + TeleportInfo(): targetLocation(TELEPORT_IGNORE,TELEPORT_IGNORE,TELEPORT_IGNORE) {} +}; + +bool getTeleportInfo(TeleportInfo& ti, const std::string& text, bool allowCoords, bool allowUnnamed, bool ignoreDescription); + +#endif + diff --git a/src/utility.h b/src/utility.h index c741ab0..143a7a0 100644 --- a/src/utility.h +++ b/src/utility.h @@ -775,17 +775,28 @@ inline std::string wide_to_narrow(const std::wstring& wcs) // Split a string using the given delimiter. Returns a vector containing // the component parts. -inline std::vector str_split(const std::wstring &str, wchar_t delimiter) +// Optionally limits output strings (0 = unlimited) +template +inline std::vector str_split(const T& str, const D& delimiter, int limit = 0) { - std::vector parts; - std::wstringstream sstr(str); - std::wstring part; - while(std::getline(sstr, part, delimiter)) - parts.push_back(part); + std::vector parts; + + int pos = 0, lpos = 0; + + while( --limit && pos != T::npos ){ + pos = str.find(delimiter,lpos); + parts.push_back( str.substr(lpos, pos-lpos) ); + lpos = pos+1; + } + + if(limit==0 && pos != T::npos) + parts.push_back( str.substr(lpos) ); + return parts; } + /* See test.cpp for example cases. wraps degrees to the range of -360...360 @@ -894,6 +905,27 @@ inline float stof(std::string s) #endif +//throws if error +template +inline T stonum_ex(const typename Str& s) +{ + typedef typename Str::value_type char_t; + T f; + std::basic_istringstream ss(s); + ss.exceptions(std::ios::failbit); + ss>>f; + return f; +} + +template +inline float stof_ex(const typename Str& s) { return stonum_ex(s); } + +template +inline double stod_ex(const typename Str& s) { return stonum_ex(s); } + +template +inline double stoi_ex(const typename Str& s) { return stonum_ex(s); } + inline std::string itos(s32 i) { std::ostringstream o;