3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 SQLite format specification:
22 - Initially only replaces sectors/ and sectors2/
24 If map.sqlite does not exist in the save dir
25 or the block was not found in the database
26 the map will try to load from sectors folder.
27 In either case, map.sqlite will be created
28 and all future saves will save there.
30 Structure of map.sqlite:
38 #include "database-sqlite3.h"
41 #include "mapsector.h"
43 #include "serialization.h"
48 Database_SQLite3::Database_SQLite3(ServerMap *map, std::string savedir)
51 m_database_read = NULL;
52 m_database_write = NULL;
53 m_database_list = NULL;
58 int Database_SQLite3::Initialized(void)
60 return m_database ? 1 : 0;
63 void Database_SQLite3::beginSave() {
65 if(sqlite3_exec(m_database, "BEGIN;", NULL, NULL, NULL) != SQLITE_OK)
66 infostream<<"WARNING: beginSave() failed, saving might be slow.";
69 void Database_SQLite3::endSave() {
71 if(sqlite3_exec(m_database, "COMMIT;", NULL, NULL, NULL) != SQLITE_OK)
72 infostream<<"WARNING: endSave() failed, map might not have saved.";
75 void Database_SQLite3::createDirs(std::string path)
77 if(fs::CreateAllDirs(path) == false)
79 infostream<<DTIME<<"Database_SQLite3: Failed to create directory "
80 <<"\""<<path<<"\""<<std::endl;
81 throw BaseException("Database_SQLite3 failed to create directory");
85 void Database_SQLite3::verifyDatabase() {
89 std::string dbp = m_savedir + DIR_DELIM "map.sqlite";
90 bool needs_create = false;
93 // Open the database connection
95 createDirs(m_savedir); // ?
97 if(!fs::PathExists(dbp))
100 d = sqlite3_open_v2(dbp.c_str(), &m_database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
102 infostream<<"WARNING: SQLite3 database failed to open: "<<sqlite3_errmsg(m_database)<<std::endl;
103 throw FileNotGoodException("Cannot open database file");
109 std::string querystr = std::string("PRAGMA synchronous = ")
110 + itos(g_settings->getU16("sqlite_synchronous"));
111 d = sqlite3_exec(m_database, querystr.c_str(), NULL, NULL, NULL);
113 infostream<<"WARNING: Database pragma set failed: "
114 <<sqlite3_errmsg(m_database)<<std::endl;
115 throw FileNotGoodException("Cannot set pragma");
118 d = sqlite3_prepare(m_database, "SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1", -1, &m_database_read, NULL);
120 infostream<<"WARNING: SQLite3 database read statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
121 throw FileNotGoodException("Cannot prepare read statement");
124 d = sqlite3_prepare(m_database, "REPLACE INTO `blocks` VALUES(?, ?)", -1, &m_database_write, NULL);
126 infostream<<"WARNING: SQLite3 database write statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
127 throw FileNotGoodException("Cannot prepare write statement");
130 d = sqlite3_prepare(m_database, "SELECT `pos` FROM `blocks`", -1, &m_database_list, NULL);
132 infostream<<"WARNING: SQLite3 database list statment failed to prepare: "<<sqlite3_errmsg(m_database)<<std::endl;
133 throw FileNotGoodException("Cannot prepare read statement");
136 infostream<<"ServerMap: SQLite3 database opened"<<std::endl;
139 void Database_SQLite3::saveBlock(MapBlock *block)
141 DSTACK(__FUNCTION_NAME);
143 Dummy blocks are not written
147 /*v3s16 p = block->getPos();
148 infostream<<"Database_SQLite3::saveBlock(): WARNING: Not writing dummy block "
149 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
153 // Format used for writing
154 u8 version = SER_FMT_VER_HIGHEST_WRITE;
156 v3s16 p3d = block->getPos();
160 v2s16 p2d(p3d.X, p3d.Z);
161 std::string sectordir = getSectorDir(p2d);
163 createDirs(sectordir);
165 std::string fullpath = sectordir+DIR_DELIM+getBlockFilename(p3d);
166 std::ofstream o(fullpath.c_str(), std::ios_base::binary);
167 if(o.good() == false)
168 throw FileNotGoodException("Cannot open block data");
171 [0] u8 serialization version
177 std::ostringstream o(std::ios_base::binary);
179 o.write((char*)&version, 1);
182 block->serialize(o, version, true);
184 // Write block to database
186 std::string tmp = o.str();
187 const char *bytes = tmp.c_str();
189 if(sqlite3_bind_int64(m_database_write, 1, getBlockAsInteger(p3d)) != SQLITE_OK)
190 infostream<<"WARNING: Block position failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
191 if(sqlite3_bind_blob(m_database_write, 2, (void *)bytes, o.tellp(), NULL) != SQLITE_OK) // TODO this mught not be the right length
192 infostream<<"WARNING: Block data failed to bind: "<<sqlite3_errmsg(m_database)<<std::endl;
193 int written = sqlite3_step(m_database_write);
194 if(written != SQLITE_DONE)
195 infostream<<"WARNING: Block failed to save ("<<p3d.X<<", "<<p3d.Y<<", "<<p3d.Z<<") "
196 <<sqlite3_errmsg(m_database)<<std::endl;
197 // Make ready for later reuse
198 sqlite3_reset(m_database_write);
200 // We just wrote it to the disk so clear modified flag
201 block->resetModified();
204 MapBlock* Database_SQLite3::loadBlock(v3s16 blockpos)
206 v2s16 p2d(blockpos.X, blockpos.Z);
209 if (sqlite3_bind_int64(m_database_read, 1, getBlockAsInteger(blockpos)) != SQLITE_OK) {
210 infostream << "WARNING: Could not bind block position for load: "
211 << sqlite3_errmsg(m_database)<<std::endl;
214 if (sqlite3_step(m_database_read) == SQLITE_ROW) {
216 Make sure sector is loaded
218 MapSector *sector = srvmap->createSector(p2d);
223 const char *data = (const char *)sqlite3_column_blob(m_database_read, 0);
224 size_t len = sqlite3_column_bytes(m_database_read, 0);
225 if (data == NULL || len == 0) {
226 errorstream << "Blank block data in database (data == NULL || len"
227 " == 0) (" << blockpos.X << "," << blockpos.Y << ","
228 << blockpos.Z << ")" << std::endl;
230 if (g_settings->getBool("ignore_world_load_errors")) {
231 errorstream << "Ignoring block load error. Duck and cover! "
232 << "(ignore_world_load_errors)" << std::endl;
234 throw SerializationError("Blank block data in database");
239 std::string datastr(data, len);
241 //srvmap->loadBlock(&datastr, blockpos, sector, false);
244 std::istringstream is(datastr, std::ios_base::binary);
246 u8 version = SER_FMT_VER_INVALID;
247 is.read((char *)&version, 1);
250 throw SerializationError("ServerMap::loadBlock(): Failed"
251 " to read MapBlock version");
253 MapBlock *block = NULL;
254 bool created_new = false;
255 block = sector->getBlockNoCreateNoEx(blockpos.Y);
258 block = sector->createBlankBlockNoInsert(blockpos.Y);
263 block->deSerialize(is, version, true);
265 // If it's a new block, insert it to the map
267 sector->insertBlock(block);
270 Save blocks loaded in old format in new format
272 //if(version < SER_FMT_VER_HIGHEST || save_after_load)
273 // Only save if asked to; no need to update version
274 //if(save_after_load)
277 // We just loaded it from, so it's up-to-date.
278 block->resetModified();
280 catch (SerializationError &e)
282 errorstream << "Invalid block data in database"
283 << " (" << blockpos.X << "," << blockpos.Y << "," << blockpos.Z << ")"
284 << " (SerializationError): " << e.what() << std::endl;
286 // TODO: Block should be marked as invalid in memory so that it is
287 // not touched but the game can run
289 if (g_settings->getBool("ignore_world_load_errors")) {
290 errorstream << "Ignoring block load error. Duck and cover! "
291 << "(ignore_world_load_errors)" << std::endl;
293 throw SerializationError("Invalid block data in database");
298 sqlite3_step(m_database_read);
299 // We should never get more than 1 row, so ok to reset
300 sqlite3_reset(m_database_read);
302 return srvmap->getBlockNoCreateNoEx(blockpos); // should not be using this here
304 sqlite3_reset(m_database_read);
308 void Database_SQLite3::createDatabase()
312 e = sqlite3_exec(m_database,
313 "CREATE TABLE IF NOT EXISTS `blocks` ("
314 "`pos` INT NOT NULL PRIMARY KEY,"
318 if(e == SQLITE_ABORT)
319 throw FileNotGoodException("Could not create sqlite3 database structure");
321 infostream<<"ServerMap: SQLite3 database structure was created";
325 void Database_SQLite3::listAllLoadableBlocks(std::list<v3s16> &dst)
329 while(sqlite3_step(m_database_list) == SQLITE_ROW)
331 sqlite3_int64 block_i = sqlite3_column_int64(m_database_list, 0);
332 v3s16 p = getIntegerAsBlock(block_i);
333 //dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
339 #define FINALIZE_STATEMENT(statement) \
341 rc = sqlite3_finalize(statement); \
342 if ( rc != SQLITE_OK ) \
343 errorstream << "Database_SQLite3::~Database_SQLite3():" \
344 << "Failed to finalize: " << #statement << ": rc=" << rc << std::endl;
346 Database_SQLite3::~Database_SQLite3()
350 FINALIZE_STATEMENT(m_database_read)
351 FINALIZE_STATEMENT(m_database_write)
352 FINALIZE_STATEMENT(m_database_list)
355 rc = sqlite3_close(m_database);
357 if (rc != SQLITE_OK) {
358 errorstream << "Database_SQLite3::~Database_SQLite3(): "
359 << "Failed to close database: rc=" << rc << std::endl;