Skip to content
Snippets Groups Projects
Commit 1fd9a11e authored by Mario Barrera's avatar Mario Barrera Committed by ShadowNinja
Browse files

SQLite rollback

parent 4f246f0e
Branches
Tags
No related merge requests found
......@@ -29,11 +29,876 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h"
#include "util/numeric.h"
#include "inventorymanager.h" // deserializing InventoryLocations
#include "sqlite3.h"
#include "filesys.h"
#define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
#define POINTS_PER_NODE (16.0)
std::string dbp;
sqlite3* dbh;
sqlite3_stmt* dbs_insert;
sqlite3_stmt* dbs_replace;
sqlite3_stmt* dbs_select;
sqlite3_stmt* dbs_select_range;
sqlite3_stmt* dbs_select_withActor;
sqlite3_stmt* dbs_knownActor_select;
sqlite3_stmt* dbs_knownActor_insert;
sqlite3_stmt* dbs_knownNode_select;
sqlite3_stmt* dbs_knownNode_insert;
struct Stack
{
int node;
int quantity;
};
struct ActionRow
{
int id;
int actor;
int timestamp;
int type;
std::string location, list;
int index, add;
Stack stack;
int nodeMeta;
int x, y, z;
int oldNode;
int oldParam1, oldParam2;
std::string oldMeta;
int newNode;
int newParam1, newParam2;
std::string newMeta;
int guessed;
};
struct Entity
{
int id;
std::string name;
};
typedef std::vector<Entity> Entities;
Entities KnownActors;
Entities KnownNodes;
void registerNewActor (int id, std::string name)
{
Entity newActor;
newActor.id = id;
newActor.name = name;
KnownActors.push_back(newActor);
//std::cout << "New actor registered: " << id << " | " << name << std::endl;
}
void registerNewNode (int id, std::string name)
{
Entity newNode;
newNode.id = id;
newNode.name = name;
KnownNodes.push_back(newNode);
//std::cout << "New node registered: " << id << " | " << name << std::endl;
}
int getActorId (std::string name)
{
Entities::const_iterator iter;
for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
if (iter->name == name)
return iter->id;
sqlite3_reset (dbs_knownActor_insert);
sqlite3_bind_text (dbs_knownActor_insert, 1, name.c_str(), -1, NULL);
sqlite3_step (dbs_knownActor_insert);
int id = sqlite3_last_insert_rowid(dbh);
//std::cout << "Actor ID insert returns " << insert << std::endl;
registerNewActor(id, name);
return id;
}
int getNodeId (std::string name)
{
Entities::const_iterator iter;
for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter)
if (iter->name == name)
return iter->id;
sqlite3_reset (dbs_knownNode_insert);
sqlite3_bind_text (dbs_knownNode_insert, 1, name.c_str(), -1, NULL);
sqlite3_step (dbs_knownNode_insert);
int id = sqlite3_last_insert_rowid(dbh);
registerNewNode(id, name);
return id;
}
const char * getActorName (int id)
{
Entities::const_iterator iter;
//std::cout << "getActorName of id " << id << std::endl;
for (iter = KnownActors.begin(); iter != KnownActors.end(); ++iter)
if (iter->id == id)
return iter->name.c_str();
return "";
}
const char * getNodeName (int id)
{
Entities::const_iterator iter;
//std::cout << "getNodeName of id " << id << std::endl;
for (iter = KnownNodes.begin(); iter != KnownNodes.end(); ++iter)
if (iter->id == id)
return iter->name.c_str();
return "";
}
Stack getStackFromString (std::string text)
{
Stack stack;
size_t off = text.find_last_of(" ");
stack.node = getNodeId(text.substr(0, off));
stack.quantity = atoi(text.substr(off + 1).c_str());
return stack;
}
std::string getStringFromStack (Stack stack)
{
std::string text;
text.append(getNodeName(stack.node));
text.append(" ");
text.append(itos(stack.quantity));
return text;
}
bool SQL_createDatabase (void)
{
infostream << "CreateDB:" << dbp << std::endl;
int dbs = sqlite3_exec(
dbh
, "CREATE TABLE IF NOT EXISTS `actor` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"`name` TEXT NOT NULL);"
"CREATE TABLE IF NOT EXISTS `node` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,"
"`name` TEXT NOT NULL);"
"CREATE TABLE IF NOT EXISTS `action` ("
"`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
"`actor` INTEGER NOT NULL,"
"`timestamp` INTEGER NOT NULL,"
"`type` INTEGER NOT NULL,"
"`list` TEXT,"
"`index` INTEGER,"
"`add` INTEGER,"
"`stackNode` INTEGER,"
"`stackQuantity` INTEGER,"
"`nodeMeta` INTEGER,"
"`x` INT,"
"`y` INT,"
"`z` INT,"
"`oldNode` INTEGER,"
"`oldParam1` INTEGER,"
"`oldParam2` INTEGER,"
"`oldMeta` TEXT,"
"`newNode` INTEGER,"
"`newParam1` INTEGER,"
"`newParam2` INTEGER,"
"`newMeta` TEXT,"
"`guessedActor` INTEGER,"
"FOREIGN KEY (`actor`) REFERENCES `actor`(`id`),"
"FOREIGN KEY (`oldNode`) REFERENCES `node`(`id`),"
"FOREIGN KEY (`newNode`) REFERENCES `node`(`id`));"
"CREATE INDEX IF NOT EXISTS `actionActor` ON `action`(`actor`);"
"CREATE INDEX IF NOT EXISTS `actionTimestamp` ON `action`(`timestamp`);"
, NULL, NULL, NULL
);
if (dbs == SQLITE_ABORT)
throw FileNotGoodException("Could not create sqlite3 database structure");
else
if (dbs != 0)
throw FileNotGoodException("SQL Rollback: Exec statement to create table structure returned a non-zero value");
else
infostream << "SQL Rollback: SQLite3 database structure was created" << std::endl;
return true;
}
void SQL_databaseCheck (void)
{
if (dbh) return;
infostream << "Database connection setup" << std::endl;
bool needsCreate = !fs::PathExists(dbp);
int dbo = sqlite3_open_v2(dbp.c_str(), &dbh, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
if (dbo != SQLITE_OK)
{
infostream
<< "SQLROLLBACK: SQLite3 database failed to open: "
<< sqlite3_errmsg(dbh)
<< std::endl;
throw FileNotGoodException("Cannot open database file");
}
if (needsCreate) SQL_createDatabase();
int dbr;
dbr = sqlite3_prepare_v2(
dbh
, "INSERT INTO `action`"
" ( `actor`, `timestamp`, `type`,"
" `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,"
" `x`, `y`, `z`,"
" `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,"
" `newNode`, `newParam1`, `newParam2`, `newMeta`,"
" `guessedActor`"
" )"
"VALUES"
" ( ?, ?, ?,"
" ?, ?, ?, ?, ?, ?,"
" ?, ?, ?,"
" ?, ?, ?, ?,"
" ?, ?, ?, ?,"
" ?"
" );"
, -1, &dbs_insert, NULL
);
if (dbr != SQLITE_OK)
throw FileNotGoodException(sqlite3_errmsg(dbh));
dbr = sqlite3_prepare_v2(
dbh
, "REPLACE INTO `action`"
" ( `actor`, `timestamp`, `type`,"
" `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodeMeta`,"
" `x`, `y`, `z`,"
" `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`,"
" `newNode`, `newParam1`, `newParam2`, `newMeta`,"
" `guessedActor`, `id`"
" )"
"VALUES"
" ( ?, ?, ?,"
" ?, ?, ?, ?, ?, ?,"
" ?, ?, ?,"
" ?, ?, ?, ?,"
" ?, ?, ?, ?,"
" ?, ?"
" );"
, -1, &dbs_replace, NULL
);
if (dbr != SQLITE_OK)
throw FileNotGoodException(sqlite3_errmsg(dbh));
dbr = sqlite3_prepare_v2(dbh
, "SELECT "
" `actor`, `timestamp`, `type`"
" , `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
" , `x`, `y`, `z`"
" , `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
" , `newNode`, `newParam1`, `newParam2`, `newMeta`"
" , `guessedActor`"
" FROM `action`"
" WHERE `timestamp` >= ?"
" ORDER BY `timestamp` DESC, `id` DESC"
, -1, &dbs_select, NULL
);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh
, "SELECT "
" `actor`, `timestamp`, `type`"
" , `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
" , `x`, `y`, `z`"
" , `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
" , `newNode`, `newParam1`, `newParam2`, `newMeta`"
" , `guessedActor`"
" FROM `action`"
" WHERE `timestamp` >= ?"
" AND `x` IS NOT NULL"
" AND `y` IS NOT NULL"
" AND `z` IS NOT NULL"
" AND (ABS(`x`) - ABS(?)) <= ?"
" AND (ABS(`y`) - ABS(?)) <= ?"
" AND (ABS(`z`) - ABS(?)) <= ?"
" ORDER BY `timestamp` DESC, `id` DESC"
" LIMIT 0,5"
, -1, &dbs_select_range, NULL
);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh
, "SELECT "
" `actor`, `timestamp`, `type`"
" , `list`, `index`, `add`, `stackNode`, `stackQuantity`, `nodemeta`"
" , `x`, `y`, `z`"
" , `oldNode`, `oldParam1`, `oldParam2`, `oldMeta`"
" , `newNode`, `newParam1`, `newParam2`, `newMeta`"
" , `guessedActor`"
" FROM `action`"
" WHERE `timestamp` >= ?"
" AND `actor` = ?"
" ORDER BY `timestamp` DESC, `id` DESC"
, -1, &dbs_select_withActor, NULL
);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `actor`", -1, &dbs_knownActor_select, NULL);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `actor` (`name`) VALUES (?)", -1, &dbs_knownActor_insert, NULL);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh, "SELECT `id`, `name` FROM `node`", -1, &dbs_knownNode_select, NULL);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
dbr = sqlite3_prepare_v2(dbh, "INSERT INTO `node` (`name`) VALUES (?)", -1, &dbs_knownNode_insert, NULL);
if (dbr != SQLITE_OK)
throw FileNotGoodException(itos(dbr).c_str());
infostream << "SQL prepared statements setup correctly" << std::endl;
int select;
sqlite3_reset(dbs_knownActor_select);
while (SQLITE_ROW == (select = sqlite3_step(dbs_knownActor_select)))
registerNewActor(
sqlite3_column_int (dbs_knownActor_select, 0),
reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownActor_select, 1))
);
sqlite3_reset(dbs_knownNode_select);
while (SQLITE_ROW == (select = sqlite3_step(dbs_knownNode_select)))
registerNewNode(
sqlite3_column_int (dbs_knownNode_select, 0),
reinterpret_cast<const char *>(sqlite3_column_text (dbs_knownNode_select, 1))
);
return;
}
bool SQL_registerRow (ActionRow row)
{
SQL_databaseCheck();
sqlite3_stmt * dbs_do = (row.id)? dbs_replace: dbs_insert;
/*
std::cout
<< (row.id? "Replacing": "Inserting")
<< " ActionRow" << std::endl;
*/
sqlite3_reset(dbs_do);
int bind [20 + (((bool) row.id)? 1: 0)], ii = 0;
bool nodeMeta = false;
bind[ii++] = sqlite3_bind_int (dbs_do, 1, row.actor);
bind[ii++] = sqlite3_bind_int (dbs_do, 2, row.timestamp);
bind[ii++] = sqlite3_bind_int (dbs_do, 3, row.type);
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
std::string loc = row.location;
std::string locType = loc.substr(0, loc.find(":"));
nodeMeta = (locType == "nodemeta");
bind[ii++] = sqlite3_bind_text (dbs_do, 4, row.list.c_str(), row.list.size(), NULL);
bind[ii++] = sqlite3_bind_int (dbs_do, 5, row.index);
bind[ii++] = sqlite3_bind_int (dbs_do, 6, row.add);
bind[ii++] = sqlite3_bind_int (dbs_do, 7, row.stack.node);
bind[ii++] = sqlite3_bind_int (dbs_do, 8, row.stack.quantity);
bind[ii++] = sqlite3_bind_int (dbs_do, 9, (int) nodeMeta);
if (nodeMeta)
{
std::string x, y, z;
int l, r;
l = loc.find(':') + 1;
r = loc.find(',');
x = loc.substr(l, r - l);
l = r + 1;
r = loc.find(',', l);
y = loc.substr(l, r - l);
z = loc.substr(r +1);
bind[ii++] = sqlite3_bind_int (dbs_do, 10, atoi(x.c_str()));
bind[ii++] = sqlite3_bind_int (dbs_do, 11, atoi(y.c_str()));
bind[ii++] = sqlite3_bind_int (dbs_do, 12, atoi(z.c_str()));
}
}
else
{
bind[ii++] = sqlite3_bind_null (dbs_do, 4);
bind[ii++] = sqlite3_bind_null (dbs_do, 5);
bind[ii++] = sqlite3_bind_null (dbs_do, 6);
bind[ii++] = sqlite3_bind_null (dbs_do, 7);
bind[ii++] = sqlite3_bind_null (dbs_do, 8);
bind[ii++] = sqlite3_bind_null (dbs_do, 9);
}
if (row.type == RollbackAction::TYPE_SET_NODE)
{
bind[ii++] = sqlite3_bind_int (dbs_do, 10, row.x);
bind[ii++] = sqlite3_bind_int (dbs_do, 11, row.y);
bind[ii++] = sqlite3_bind_int (dbs_do, 12, row.z);
bind[ii++] = sqlite3_bind_int (dbs_do, 13, row.oldNode);
bind[ii++] = sqlite3_bind_int (dbs_do, 14, row.oldParam1);
bind[ii++] = sqlite3_bind_int (dbs_do, 15, row.oldParam2);
bind[ii++] = sqlite3_bind_text (dbs_do, 16, row.oldMeta.c_str(), row.oldMeta.size(), NULL);
bind[ii++] = sqlite3_bind_int (dbs_do, 17, row.newNode);
bind[ii++] = sqlite3_bind_int (dbs_do, 18, row.newParam1);
bind[ii++] = sqlite3_bind_int (dbs_do, 19, row.newParam2);
bind[ii++] = sqlite3_bind_text (dbs_do, 20, row.newMeta.c_str(), row.newMeta.size(), NULL);
bind[ii++] = sqlite3_bind_int (dbs_do, 21, row.guessed? 1: 0);
}
else
{
if (!nodeMeta)
{
bind[ii++] = sqlite3_bind_null (dbs_do, 10);
bind[ii++] = sqlite3_bind_null (dbs_do, 11);
bind[ii++] = sqlite3_bind_null (dbs_do, 12);
}
bind[ii++] = sqlite3_bind_null (dbs_do, 13);
bind[ii++] = sqlite3_bind_null (dbs_do, 14);
bind[ii++] = sqlite3_bind_null (dbs_do, 15);
bind[ii++] = sqlite3_bind_null (dbs_do, 16);
bind[ii++] = sqlite3_bind_null (dbs_do, 17);
bind[ii++] = sqlite3_bind_null (dbs_do, 18);
bind[ii++] = sqlite3_bind_null (dbs_do, 19);
bind[ii++] = sqlite3_bind_null (dbs_do, 20);
bind[ii++] = sqlite3_bind_null (dbs_do, 21);
}
if (row.id)
bind[ii++] = sqlite3_bind_int (dbs_do, 22, row.id);
for (ii = 0; ii < 20; ++ii)
if (bind[ii] != SQLITE_OK)
infostream
<< "WARNING: failed to bind param " << ii + 1
<< " when inserting an entry in table setnode" << std::endl;
/*
std::cout << "========DB-WRITTEN==========" << std::endl;
std::cout << "id: " << row.id << std::endl;
std::cout << "actor: " << row.actor << std::endl;
std::cout << "time: " << row.timestamp << std::endl;
std::cout << "type: " << row.type << std::endl;
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
std::cout << "Location: " << row.location << std::endl;
std::cout << "List: " << row.list << std::endl;
std::cout << "Index: " << row.index << std::endl;
std::cout << "Add: " << row.add << std::endl;
std::cout << "Stack: " << row.stack << std::endl;
}
if (row.type == RollbackAction::TYPE_SET_NODE)
{
std::cout << "x: " << row.x << std::endl;
std::cout << "y: " << row.y << std::endl;
std::cout << "z: " << row.z << std::endl;
std::cout << "oldNode: " << row.oldNode << std::endl;
std::cout << "oldParam1: " << row.oldParam1 << std::endl;
std::cout << "oldParam2: " << row.oldParam2 << std::endl;
std::cout << "oldMeta: " << row.oldMeta << std::endl;
std::cout << "newNode: " << row.newNode << std::endl;
std::cout << "newParam1: " << row.newParam1 << std::endl;
std::cout << "newParam2: " << row.newParam2 << std::endl;
std::cout << "newMeta: " << row.newMeta << std::endl;
std::cout << "DESERIALIZE" << row.newMeta.c_str() << std::endl;
std::cout << "guessed: " << row.guessed << std::endl;
}
*/
int written = sqlite3_step(dbs_do);
return written == SQLITE_DONE;
//if (written != SQLITE_DONE)
// std::cout << "WARNING: rollback action not written: " << sqlite3_errmsg(dbh) << std::endl;
//else std::cout << "Action correctly inserted via SQL" << std::endl;
}
std::list<ActionRow> actionRowsFromSelect (sqlite3_stmt* stmt)
{
std::list<ActionRow> rows;
const unsigned char * text;
size_t size;
while (SQLITE_ROW == sqlite3_step(stmt))
{
ActionRow row;
row.actor = sqlite3_column_int (stmt, 0);
row.timestamp = sqlite3_column_int (stmt, 1);
row.type = sqlite3_column_int (stmt, 2);
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
text = sqlite3_column_text (stmt, 3);
size = sqlite3_column_bytes(stmt, 3);
row.list = std::string(reinterpret_cast<const char*>(text), size);
row.index = sqlite3_column_int (stmt, 4);
row.add = sqlite3_column_int (stmt, 5);
row.stack.node = sqlite3_column_int (stmt, 6);
row.stack.quantity = sqlite3_column_int (stmt, 7);
row.nodeMeta = sqlite3_column_int (stmt, 8);
}
if (row.type == RollbackAction::TYPE_SET_NODE || row.nodeMeta)
{
row.x = sqlite3_column_int (stmt, 9);
row.y = sqlite3_column_int (stmt, 10);
row.z = sqlite3_column_int (stmt, 11);
}
if (row.type == RollbackAction::TYPE_SET_NODE)
{
row.oldNode = sqlite3_column_int (stmt, 12);
row.oldParam1 = sqlite3_column_int (stmt, 13);
row.oldParam2 = sqlite3_column_int (stmt, 14);
text = sqlite3_column_text (stmt, 15);
size = sqlite3_column_bytes(stmt, 15);
row.oldMeta = std::string(reinterpret_cast<const char*>(text), size);
row.newNode = sqlite3_column_int (stmt, 16);
row.newParam1 = sqlite3_column_int (stmt, 17);
row.newParam2 = sqlite3_column_int (stmt, 18);
text = sqlite3_column_text (stmt, 19);
size = sqlite3_column_bytes(stmt, 19);
row.newMeta = std::string(reinterpret_cast<const char*>(text), size);
row.guessed = sqlite3_column_int (stmt, 20);
}
row.location = row.nodeMeta? "nodemeta:": getActorName(row.actor);
if (row.nodeMeta)
{
row.location.append(itos(row.x));
row.location.append(",");
row.location.append(itos(row.y));
row.location.append(",");
row.location.append(itos(row.z));
}
/*
std::cout << "=======SELECTED==========" << "\n";
std::cout << "Actor: " << row.actor << "\n";
std::cout << "Timestamp: " << row.timestamp << "\n";
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
std::cout << "list: " << row.list << "\n";
std::cout << "index: " << row.index << "\n";
std::cout << "add: " << row.add << "\n";
std::cout << "stackNode: " << row.stack.node << "\n";
std::cout << "stackQuantity: " << row.stack.quantity << "\n";
if (row.nodeMeta)
{
std::cout << "X: " << row.x << "\n";
std::cout << "Y: " << row.y << "\n";
std::cout << "Z: " << row.z << "\n";
}
std::cout << "Location: " << row.location << "\n";
}
else
{
std::cout << "X: " << row.x << "\n";
std::cout << "Y: " << row.y << "\n";
std::cout << "Z: " << row.z << "\n";
std::cout << "oldNode: " << row.oldNode << "\n";
std::cout << "oldParam1: " << row.oldParam1 << "\n";
std::cout << "oldParam2: " << row.oldParam2 << "\n";
std::cout << "oldMeta: " << row.oldMeta << "\n";
std::cout << "newNode: " << row.newNode << "\n";
std::cout << "newParam1: " << row.newParam1 << "\n";
std::cout << "newParam2: " << row.newParam2 << "\n";
std::cout << "newMeta: " << row.newMeta << "\n";
std::cout << "guessed: " << row.guessed << "\n";
}
*/
rows.push_back(row);
}
return rows;
}
ActionRow actionRowFromRollbackAction (RollbackAction action)
{
ActionRow row;
row.id = 0;
row.actor = getActorId(action.actor);
row.timestamp = action.unix_time;
row.type = action.type;
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
row.location = action.inventory_location;
row.list = action.inventory_list;
row.index = action.inventory_index;
row.add = action.inventory_add;
row.stack = getStackFromString(action.inventory_stack);
}
else
{
row.x = action.p.X;
row.y = action.p.Y;
row.z = action.p.Z;
row.oldNode = getNodeId(action.n_old.name);
row.oldParam1 = action.n_old.param1;
row.oldParam2 = action.n_old.param2;
row.oldMeta = action.n_old.meta;
row.newNode = getNodeId(action.n_new.name);
row.newParam1 = action.n_new.param1;
row.newParam2 = action.n_new.param2;
row.newMeta = action.n_new.meta;
row.guessed = action.actor_is_guess;
}
return row;
}
std::list<RollbackAction> rollbackActionsFromActionRows (std::list<ActionRow> rows)
{
std::list<RollbackAction> actions;
std::list<ActionRow>::const_iterator it;
for (it = rows.begin(); it != rows.end(); ++it)
{
RollbackAction action;
action.actor = (it->actor)? getActorName(it->actor): "";
action.unix_time = it->timestamp;
action.type = static_cast<RollbackAction::Type>(it->type);
switch (action.type)
{
case RollbackAction::TYPE_MODIFY_INVENTORY_STACK:
action.inventory_location = it->location.c_str();
action.inventory_list = it->list;
action.inventory_index = it->index;
action.inventory_add = it->add;
action.inventory_stack = getStringFromStack(it->stack);
break;
case RollbackAction::TYPE_SET_NODE:
action.p = v3s16(it->x, it->y, it->z);
action.n_old.name = getNodeName(it->oldNode);
action.n_old.param1 = it->oldParam1;
action.n_old.param2 = it->oldParam2;
action.n_old.meta = it->oldMeta;
action.n_new.name = getNodeName(it->newNode);
action.n_new.param1 = it->newParam1;
action.n_new.param2 = it->newParam2;
action.n_new.meta = it->newMeta;
break;
default:
throw("W.T.F.");
break;
}
actions.push_back(action);
}
return actions;
}
std::list<ActionRow> SQL_getRowsSince (int firstTime, std::string actor = "")
{
sqlite3_stmt * dbs_stmt = (!actor.length())? dbs_select: dbs_select_withActor;
sqlite3_reset (dbs_stmt);
sqlite3_bind_int (dbs_stmt, 1, firstTime);
if (actor.length())
sqlite3_bind_int (dbs_stmt, 2, getActorId(actor));
return actionRowsFromSelect(dbs_stmt);
}
std::list<ActionRow> SQL_getRowsSince_range (int firstTime, v3s16 p, int range)
{
sqlite3_stmt * stmt = dbs_select_range;
sqlite3_reset(stmt);
sqlite3_bind_int(stmt, 1, firstTime);
sqlite3_bind_int(stmt, 2, (int) p.X);
sqlite3_bind_int(stmt, 3, range);
sqlite3_bind_int(stmt, 4, (int) p.Y);
sqlite3_bind_int(stmt, 5, range);
sqlite3_bind_int(stmt, 6, (int) p.Z);
sqlite3_bind_int(stmt, 7, range);
return actionRowsFromSelect(stmt);
}
std::list<RollbackAction> SQL_getActionsSince_range (int firstTime, v3s16 p, int range)
{
std::list<ActionRow> rows = SQL_getRowsSince_range(firstTime, p, range);
return rollbackActionsFromActionRows(rows);
}
std::list<RollbackAction> SQL_getActionsSince (int firstTime, std::string actor = "")
{
std::list<ActionRow> rows = SQL_getRowsSince(firstTime, actor);
return rollbackActionsFromActionRows(rows);
}
void TXT_migrate (std::string filepath)
{
std::cout << "Migrating from rollback.txt to rollback.sqlite" << std::endl;
SQL_databaseCheck();
std::ifstream fh (filepath.c_str(), std::ios::in | std::ios::ate);
if (!fh.good()) throw("DIE");
int filesize = fh.tellg();
if (filesize > 10)
{
fh.seekg(0);
std::string bit;
int i = 0;
int id = 1;
int t = 0;
do
{
ActionRow row;
row.id = id;
// Get the timestamp
std::getline(fh, bit, ' '); bit = trim(bit); if (!atoi(trim(bit).c_str())) { std::getline(fh, bit); continue; }
row.timestamp = atoi(bit.c_str());
// Get the actor
row.actor = getActorId(trim(deSerializeJsonString(fh)));
// Get the action type
std::getline(fh, bit, '[');
std::getline(fh, bit, ' ');
if (bit == "modify_inventory_stack")
row.type = RollbackAction::TYPE_MODIFY_INVENTORY_STACK;
if (bit == "set_node")
row.type = RollbackAction::TYPE_SET_NODE;
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
row.location = trim(deSerializeJsonString(fh));
std::getline(fh, bit, ' '); row.list = trim(deSerializeJsonString(fh));
std::getline(fh, bit, ' ');
std::getline(fh, bit, ' ');
row.index = atoi(trim(bit).c_str());
std::getline(fh, bit, ' '); row.add = (int) ( trim(bit) == "add" );
row.stack = getStackFromString(trim(deSerializeJsonString(fh)));
std::getline(fh, bit);
}
else
if (row.type == RollbackAction::TYPE_SET_NODE)
{
std::getline(fh, bit, '(');
std::getline(fh, bit, ','); row.x = atoi(trim(bit).c_str());
std::getline(fh, bit, ','); row.y = atoi(trim(bit).c_str());
std::getline(fh, bit, ')'); row.z = atoi(trim(bit).c_str());
std::getline(fh, bit, ' '); row.oldNode = getNodeId(trim(deSerializeJsonString(fh)));
std::getline(fh, bit, ' ');
std::getline(fh, bit, ' '); row.oldParam1 = atoi(trim(bit).c_str());
std::getline(fh, bit, ' '); row.oldParam2 = atoi(trim(bit).c_str());
row.oldMeta = trim(deSerializeJsonString(fh));
std::getline(fh, bit, ' '); row.newNode = getNodeId(trim(deSerializeJsonString(fh)));
std::getline(fh, bit, ' ');
std::getline(fh, bit, ' '); row.newParam1 = atoi(trim(bit).c_str());
std::getline(fh, bit, ' '); row.newParam2 = atoi(trim(bit).c_str());
row.newMeta = trim(deSerializeJsonString(fh));
std::getline(fh, bit, ' ');
std::getline(fh, bit, ' ');
std::getline(fh, bit); row.guessed = (int) ( trim(bit) == "actor_is_guess" );
}
/*
std::cout << "==========READ===========" << std::endl;
std::cout << "time: " << row.timestamp << std::endl;
std::cout << "actor: " << row.actor << std::endl;
std::cout << "type: " << row.type << std::endl;
if (row.type == RollbackAction::TYPE_MODIFY_INVENTORY_STACK)
{
std::cout << "Location: " << row.location << std::endl;
std::cout << "List: " << row.list << std::endl;
std::cout << "Index: " << row.index << std::endl;
std::cout << "Add: " << row.add << std::endl;
std::cout << "Stack: " << row.stack << std::endl;
}
if (row.type == RollbackAction::TYPE_SET_NODE)
{
std::cout << "x: " << row.x << std::endl;
std::cout << "y: " << row.y << std::endl;
std::cout << "z: " << row.z << std::endl;
std::cout << "oldNode: " << row.oldNode << std::endl;
std::cout << "oldParam1: " << row.oldParam1 << std::endl;
std::cout << "oldParam2: " << row.oldParam2 << std::endl;
std::cout << "oldMeta: " << row.oldMeta << std::endl;
std::cout << "newNode: " << row.newNode << std::endl;
std::cout << "newParam1: " << row.newParam1 << std::endl;
std::cout << "newParam2: " << row.newParam2 << std::endl;
std::cout << "newMeta: " << row.newMeta << std::endl;
std::cout << "guessed: " << row.guessed << std::endl;
}
*/
if (i == 0)
{
t = time(0);
sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
}
SQL_registerRow(row); ++i;
if (time(0) - t)
{
sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL);
t = time(0) - t;
std::cout
<< " Done: " << (int)(((float) fh.tellg() / (float) filesize) * 100) << "%"
<< "\tSpeed: " << i / t << " actions inserted per second "
<< "\r";
std::cout.flush();
i = 0;
}
++id;
}
while (!fh.eof() && fh.good());
}
std::cout
<< " Done: 100%" << std::endl
<< " Now you can delete the old rollback.txt file." << std::endl;
}
// Get nearness factor for subject's action for this action
// Return value: 0 = impossible, >0 = factor
static float getSuspectNearness(bool is_guess, v3s16 suspect_p, int suspect_t,
......@@ -56,38 +921,43 @@ static float getSuspectNearness(bool is_guess, v3s16 suspect_p, int suspect_t,
f = 0;
return f;
}
class RollbackManager: public IRollbackManager
{
public:
// IRollbackManager interface
void reportAction(const RollbackAction &action_)
{
// Ignore if not important
if(!action_.isImportant(m_gamedef))
if (!action_.isImportant(m_gamedef))
return;
RollbackAction action = action_;
action.unix_time = time(0);
// Figure out actor
action.actor = m_current_actor;
action.actor_is_guess = m_current_actor_is_guess;
// If actor is not known, find out suspect or cancel
if(action.actor.empty()){
if (action.actor.empty()) // If actor is not known, find out suspect or cancel
{
v3s16 p;
if(!action.getPosition(&p))
if (!action.getPosition(&p))
return;
action.actor = getSuspect(p, 83, 1);
if(action.actor.empty())
if (action.actor.empty())
return;
action.actor_is_guess = true;
}
infostream<<"RollbackManager::reportAction():"
<<" time="<<action.unix_time
<<" actor=\""<<action.actor<<"\""
<<(action.actor_is_guess?" (guess)":"")
<<" action="<<action.toString()
<<std::endl;
infostream
<< "RollbackManager::reportAction():"
<< " time=" << action.unix_time
<< " actor=\"" << action.actor << "\""
<< (action.actor_is_guess? " (guess)": "")
<< " action=" << action.toString()
<< std::endl;
addAction(action);
}
std::string getActor()
......@@ -140,213 +1010,131 @@ class RollbackManager: public IRollbackManager
}
void flush()
{
infostream<<"RollbackManager::flush()"<<std::endl;
std::ofstream of(m_filepath.c_str(), std::ios::app);
if(!of.good()){
errorstream<<"RollbackManager::flush(): Could not open file "
<<"for appending: \""<<m_filepath<<"\""<<std::endl;
return;
}
for(std::list<RollbackAction>::const_iterator
i = m_action_todisk_buffer.begin();
i != m_action_todisk_buffer.end(); i++)
infostream << "RollbackManager::flush()" << std::endl;
sqlite3_exec(dbh, "BEGIN", NULL, NULL, NULL);
std::list<RollbackAction>::const_iterator iter;
for (iter = m_action_todisk_buffer.begin();
iter != m_action_todisk_buffer.end();
iter++)
{
// Do not save stuff that does not have an actor
if(i->actor == "")
if (iter->actor == "")
continue;
of<<i->unix_time;
of<<" ";
of<<serializeJsonString(i->actor);
of<<" ";
of<<i->toString();
if(i->actor_is_guess){
of<<" ";
of<<"actor_is_guess";
}
of<<std::endl;
SQL_registerRow(actionRowFromRollbackAction(*iter));
}
sqlite3_exec(dbh, "COMMIT", NULL, NULL, NULL);
m_action_todisk_buffer.clear();
}
// Other
RollbackManager(const std::string &filepath, IGameDef *gamedef):
m_filepath(filepath),
m_gamedef(gamedef),
m_current_actor_is_guess(false)
{
infostream<<"RollbackManager::RollbackManager("<<filepath<<")"
<<std::endl;
infostream
<< "RollbackManager::RollbackManager(" << filepath << ")"
<< std::endl;
// Operate correctly in case of still being given rollback.txt as filepath
std::string directory = filepath.substr(0, filepath.rfind(DIR_DELIM) + 1);
std::string filenameOld = filepath.substr(1+ filepath.rfind(DIR_DELIM));
std::string filenameNew = (filenameOld == "rollback.txt")? "rollback.sqlite": filenameOld;
std::string filenameTXT = directory + "rollback.txt";
std::string migratingFlag = filepath;
migratingFlag.append(".migrating");
infostream << "Directory: " << directory << std::endl;
infostream << "CheckFor: " << filenameTXT << std::endl;
infostream << "FileOld: " << filenameOld << std::endl;
infostream << "FileNew: " << filenameNew << std::endl;
dbp = directory + filenameNew;
if ((fs::PathExists(filenameTXT) && fs::PathExists(migratingFlag))
|| (fs::PathExists(filenameTXT) && !fs::PathExists(dbp)))
{
std::ofstream of(migratingFlag.c_str());
TXT_migrate(filenameTXT);
fs::DeleteSingleFileOrEmptyDirectory(migratingFlag);
}
SQL_databaseCheck();
}
~RollbackManager()
{
infostream<<"RollbackManager::~RollbackManager()"<<std::endl;
infostream << "RollbackManager::~RollbackManager()" << std::endl;
flush();
}
void addAction(const RollbackAction &action)
{
m_action_todisk_buffer.push_back(action);
m_action_latest_buffer.push_back(action);
// Flush to disk sometimes
if(m_action_todisk_buffer.size() >= 100)
if(m_action_todisk_buffer.size() >= 500)
flush();
}
bool readFile(std::list<RollbackAction> &dst)
{
// Load whole file to memory
std::ifstream f(m_filepath.c_str(), std::ios::in);
if(!f.good()){
errorstream<<"RollbackManager::readFile(): Could not open "
<<"file for reading: \""<<m_filepath<<"\""<<std::endl;
return false;
}
for(;;){
if(f.eof() || !f.good())
break;
std::string line;
std::getline(f, line);
line = trim(line);
if(line == "")
continue;
std::istringstream is(line);
try{
std::string action_time_raw;
std::getline(is, action_time_raw, ' ');
std::string action_actor;
try{
action_actor = deSerializeJsonString(is);
}catch(SerializationError &e){
errorstream<<"RollbackManager: Error deserializing actor: "
<<e.what()<<std::endl;
throw e;
}
RollbackAction action;
action.unix_time = stoi(action_time_raw);
action.actor = action_actor;
int c = is.get();
if(c != ' '){
is.putback(c);
throw SerializationError("readFile(): second ' ' not found");
}
action.fromStream(is);
/*infostream<<"RollbackManager::readFile(): Action from disk: "
<<action.toString()<<std::endl;*/
dst.push_back(action);
}
catch(SerializationError &e){
errorstream<<"RollbackManager: Error on line: "<<line<<std::endl;
errorstream<<"RollbackManager: ^ error: "<<e.what()<<std::endl;
}
}
return true;
}
std::list<RollbackAction> getEntriesSince(int first_time)
{
infostream<<"RollbackManager::getEntriesSince("<<first_time<<")"<<std::endl;
// Collect enough data to this buffer
std::list<RollbackAction> action_buffer;
// Use the latest buffer if it is long enough
if(!m_action_latest_buffer.empty() &&
m_action_latest_buffer.begin()->unix_time <= first_time){
action_buffer = m_action_latest_buffer;
}
else
{
// Save all remaining stuff
flush();
// Load whole file to memory
bool good = readFile(action_buffer);
if(!good){
errorstream<<"RollbackManager::getEntriesSince(): Failed to"
<<" open file; using data in memory."<<std::endl;
action_buffer = m_action_latest_buffer;
}
}
return action_buffer;
infostream
<< "RollbackManager::getEntriesSince(" << first_time << ")"
<< std::endl;
flush();
std::list<RollbackAction> result = SQL_getActionsSince(first_time);
return result;
}
std::string getLastNodeActor(v3s16 p, int range, int seconds,
v3s16 *act_p, int *act_seconds)
std::string getLastNodeActor(v3s16 p, int range, int seconds, v3s16 *act_p, int *act_seconds)
{
infostream<<"RollbackManager::getLastNodeActor("<<PP(p)
<<", "<<seconds<<")"<<std::endl;
// Figure out time
int cur_time = time(0);
int first_time = cur_time - seconds;
std::list<RollbackAction> action_buffer = getEntriesSince(first_time);
std::list<RollbackAction> result;
std::list<RollbackAction> action_buffer = SQL_getActionsSince_range(first_time, p, range);
std::list<RollbackAction>::const_reverse_iterator iter;
for(std::list<RollbackAction>::const_reverse_iterator
i = action_buffer.rbegin();
i != action_buffer.rend(); i++)
for (iter = action_buffer.rbegin();
iter != action_buffer.rend();
iter++)
{
if(i->unix_time < first_time)
break;
// Find position of action or continue
v3s16 action_p;
if(!i->getPosition(&action_p))
continue;
if(range == 0){
if(action_p != p)
continue;
} else {
if(abs(action_p.X - p.X) > range ||
abs(action_p.Y - p.Y) > range ||
abs(action_p.Z - p.Z) > range)
continue;
}
action_p.X = iter->p.X;
action_p.Y = iter->p.Y;
action_p.Z = iter->p.Z;
if(act_p)
if (act_p)
*act_p = action_p;
if(act_seconds)
*act_seconds = cur_time - i->unix_time;
return i->actor;
if (act_seconds)
*act_seconds = cur_time - iter->unix_time;
return iter->actor;
}
return "";
}
std::list<RollbackAction> getRevertActions(const std::string &actor_filter,
int seconds)
std::list<RollbackAction> getRevertActions(const std::string &actor_filter, int seconds)
{
infostream<<"RollbackManager::getRevertActions("<<actor_filter
<<", "<<seconds<<")"<<std::endl;
infostream
<< "RollbackManager::getRevertActions(" << actor_filter
<< ", " << seconds << ")"
<< std::endl;
// Figure out time
int cur_time = time(0);
int first_time = cur_time - seconds;
std::list<RollbackAction> action_buffer = getEntriesSince(first_time);
flush();
std::list<RollbackAction> result;
for(std::list<RollbackAction>::const_reverse_iterator
i = action_buffer.rbegin();
i != action_buffer.rend(); i++)
{
if(i->unix_time < first_time)
break;
if(i->actor != actor_filter)
continue;
const RollbackAction &action = *i;
/*infostream<<"RollbackManager::revertAction(): Should revert"
<<" time="<<action.unix_time
<<" actor=\""<<action.actor<<"\""
<<" action="<<action.toString()
<<std::endl;*/
result.push_back(action);
}
std::list<RollbackAction> result = SQL_getActionsSince(first_time, actor_filter);
return result;
}
private:
std::string m_filepath;
IGameDef *m_gamedef;
......@@ -360,5 +1148,3 @@ IRollbackManager *createRollbackManager(const std::string &filepath, IGameDef *g
{
return new RollbackManager(filepath, gamedef);
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment