// rollback_revert_actions_by(actor, seconds) -> bool, log messages int ModApiBasic::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(); 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); // Get the table insert function and push the log table lua_getglobal(L, "table"); lua_getfield(L, -1, "insert"); int table_insert = lua_gettop(L); lua_newtable(L); int table = lua_gettop(L); for(std::list<std::string>::const_iterator i = log.begin(); i != log.end(); i++) { lua_pushvalue(L, table_insert); lua_pushvalue(L, table); lua_pushstring(L, i->c_str()); if(lua_pcall(L, 2, 0, 0)) script_error(L, "error: %s", lua_tostring(L, -1)); } lua_remove(L, -2); // Remove table lua_remove(L, -2); // Remove insert return 2; }
// 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; }
// rollback_get_last_node_actor(p, range, seconds) -> actor, p, seconds int ModApiBasic::l_rollback_get_last_node_actor(lua_State *L) { v3s16 p = read_v3s16(L, 1); int range = luaL_checknumber(L, 2); int seconds = luaL_checknumber(L, 3); Server *server = getServer(L); IRollbackManager *rollback = server->getRollbackManager(); v3s16 act_p; int act_seconds = 0; std::string actor = rollback->getLastNodeActor(p, range, seconds, &act_p, &act_seconds); lua_pushstring(L, actor.c_str()); push_v3s16(L, act_p); lua_pushnumber(L, act_seconds); return 3; }
// rollback_get_node_actions(pos, range, seconds, limit) -> {{actor, pos, time, oldnode, newnode}, ...} int ModApiRollback::l_rollback_get_node_actions(lua_State *L) { NO_MAP_LOCK_REQUIRED; 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; }
void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); if(!inv_from){ infostream<<"IDropAction::apply(): FAIL: source inventory not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); /* If a list doesn't exist or the source item doesn't exist */ if(!list_from){ infostream<<"IDropAction::apply(): FAIL: source list not found: " <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl; return; } if(list_from->getItem(from_i).empty()) { infostream<<"IDropAction::apply(): FAIL: source item not found: " <<"from_inv=\""<<from_inv.dump()<<"\"" <<", from_list=\""<<from_list<<"\"" <<" from_i="<<from_i<<std::endl; return; } /* Do not handle rollback if inventory is player's */ bool ignore_src_rollback = (from_inv.type == InventoryLocation::PLAYER); /* Collect information of endpoints */ int take_count = list_from->getItem(from_i).count; if(count != 0 && count < take_count) take_count = count; int src_can_take_count = take_count; // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = take_count; src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( from_inv.p, from_list, from_i, src_item, player); } if(src_can_take_count != -1 && src_can_take_count < take_count) take_count = src_can_take_count; int actually_dropped_count = 0; ItemStack src_item = list_from->getItem(from_i); // Drop the item ItemStack item1 = list_from->getItem(from_i); item1.count = take_count; if(PLAYER_TO_SA(player)->item_OnDrop(item1, player, player->getBasePosition() + v3f(0,1,0))) { actually_dropped_count = take_count - item1.count; if(actually_dropped_count == 0){ infostream<<"Actually dropped no items"<<std::endl; return; } // If source isn't infinite if(src_can_take_count != -1){ // Take item from source list ItemStack item2 = list_from->takeItem(from_i, actually_dropped_count); if(item2.count != actually_dropped_count) errorstream<<"Could not take dropped count of items"<<std::endl; mgr->setInventoryModified(from_inv, false); } } infostream<<"IDropAction::apply(): dropped " <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<std::endl; src_item.count = actually_dropped_count; /* Report drop to endpoints */ // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnTake( from_inv.name, from_list, from_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( from_inv.p, from_list, from_i, src_item, player); } /* Record rollback information */ if(!ignore_src_rollback && gamedef->rollback()) { IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if(src_can_take_count != -1){ RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); from_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, false, src_item); rollback->reportAction(action); } } }
void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player, IGameDef *gamedef) { Inventory *inv_from = mgr->getInventory(from_inv); Inventory *inv_to = mgr->getInventory(to_inv); if (!inv_from) { infostream << "IMoveAction::apply(): FAIL: source inventory not found: " << "from_inv=\""<<from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } if (!inv_to) { infostream << "IMoveAction::apply(): FAIL: destination inventory not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", to_inv=\"" << to_inv.dump() << "\"" << std::endl; return; } InventoryList *list_from = inv_from->getList(from_list); InventoryList *list_to = inv_to->getList(to_list); /* If a list doesn't exist or the source item doesn't exist */ if (!list_from) { infostream << "IMoveAction::apply(): FAIL: source list not found: " << "from_inv=\"" << from_inv.dump() << "\"" << ", from_list=\"" << from_list << "\"" << std::endl; return; } if (!list_to) { infostream << "IMoveAction::apply(): FAIL: destination list not found: " << "to_inv=\""<<to_inv.dump() << "\"" << ", to_list=\"" << to_list << "\"" << std::endl; return; } if (move_somewhere) { s16 old_to_i = to_i; u16 old_count = count; caused_by_move_somewhere = true; move_somewhere = false; infostream << "IMoveAction::apply(): moving item somewhere" << " msom=" << move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << std::endl; // Try to add the item to destination list s16 dest_size = list_to->getSize(); // First try all the non-empty slots for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (!list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } // Then try all the empty ones for (s16 dest_i = 0; dest_i < dest_size && count > 0; dest_i++) { if (list_to->getItem(dest_i).empty()) { to_i = dest_i; apply(mgr, player, gamedef); count -= move_count; } } to_i = old_to_i; count = old_count; caused_by_move_somewhere = false; move_somewhere = true; return; } if ((u16)to_i > list_to->getSize()) { infostream << "IMoveAction::apply(): FAIL: destination index out of bounds: " << "to_i=" << to_i << ", size=" << list_to->getSize() << std::endl; return; } /* Do not handle rollback if both inventories are that of the same player */ bool ignore_rollback = ( from_inv.type == InventoryLocation::PLAYER && to_inv.type == InventoryLocation::PLAYER && from_inv.name == to_inv.name); /* Collect information of endpoints */ int try_take_count = count; if(try_take_count == 0) try_take_count = list_from->getItem(from_i).count; int src_can_take_count = 0xffff; int dst_can_put_count = 0xffff; /* Query detached inventories */ // Move occurs in the same detached inventory if(from_inv.type == InventoryLocation::DETACHED && to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowMove( from_inv.name, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->detached_inventory_AllowPut( to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->detached_inventory_AllowTake( from_inv.name, from_list, from_i, src_item, player); } } /* Query node metadata inventories */ // Both endpoints are nodemeta // Move occurs in the same nodemeta inventory if(from_inv.type == InventoryLocation::NODEMETA && to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowMove( from_inv.p, from_list, from_i, to_list, to_i, try_take_count, player); dst_can_put_count = src_can_take_count; } else { // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; dst_can_put_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowPut( to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta if(from_inv.type == InventoryLocation::NODEMETA) { ItemStack src_item = list_from->getItem(from_i); src_item.count = try_take_count; src_can_take_count = PLAYER_TO_SA(player)->nodemeta_inventory_AllowTake( from_inv.p, from_list, from_i, src_item, player); } } int old_count = count; /* Modify count according to collected data */ count = try_take_count; if(src_can_take_count != -1 && count > src_can_take_count) count = src_can_take_count; if(dst_can_put_count != -1 && count > dst_can_put_count) count = dst_can_put_count; /* Limit according to source item count */ if(count > list_from->getItem(from_i).count) count = list_from->getItem(from_i).count; /* If no items will be moved, don't go further */ if(count == 0) { infostream<<"IMoveAction::apply(): move was completely disallowed:" <<" count="<<old_count <<" from inv=\""<<from_inv.dump()<<"\"" <<" list=\""<<from_list<<"\"" <<" i="<<from_i <<" to inv=\""<<to_inv.dump()<<"\"" <<" list=\""<<to_list<<"\"" <<" i="<<to_i <<std::endl; return; } ItemStack src_item = list_from->getItem(from_i); src_item.count = count; ItemStack from_stack_was = list_from->getItem(from_i); ItemStack to_stack_was = list_to->getItem(to_i); /* Perform actual move If something is wrong (source item is empty, destination is the same as source), nothing happens */ move_count = list_from->moveItem(from_i, list_to, to_i, count, !caused_by_move_somewhere); // If source is infinite, reset it's stack if(src_can_take_count == -1){ // If destination stack is of different type and there are leftover // items, attempt to put the leftover items to a different place in the // destination inventory. // The client-side GUI will try to guess if this happens. if(from_stack_was.name != to_stack_was.name){ for(u32 i=0; i<list_to->getSize(); i++){ if(list_to->getItem(i).empty()){ list_to->changeItem(i, to_stack_was); break; } } } list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); } // If destination is infinite, reset it's stack and take count from source if(dst_can_put_count == -1){ list_to->deleteItem(to_i); list_to->addItem(to_i, to_stack_was); list_from->deleteItem(from_i); list_from->addItem(from_i, from_stack_was); list_from->takeItem(from_i, count); } infostream << "IMoveAction::apply(): moved" << " msom=" << move_somewhere << " caused=" << caused_by_move_somewhere << " count=" << count << " from inv=\"" << from_inv.dump() << "\"" << " list=\"" << from_list << "\"" << " i=" << from_i << " to inv=\"" << to_inv.dump() << "\"" << " list=\"" << to_list << "\"" << " i=" << to_i << std::endl; /* Record rollback information */ if(!ignore_rollback && gamedef->rollback()) { IRollbackManager *rollback = gamedef->rollback(); // If source is not infinite, record item take if(src_can_take_count != -1){ RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); from_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, from_list, from_i, false, src_item); rollback->reportAction(action); } // If destination is not infinite, record item put if(dst_can_put_count != -1){ RollbackAction action; std::string loc; { std::ostringstream os(std::ios::binary); to_inv.serialize(os); loc = os.str(); } action.setModifyInventoryStack(loc, to_list, to_i, true, src_item); rollback->reportAction(action); } } /* Report move to endpoints */ /* Detached inventories */ // Both endpoints are same detached if(from_inv.type == InventoryLocation::DETACHED && to_inv.type == InventoryLocation::DETACHED && from_inv.name == to_inv.name) { PLAYER_TO_SA(player)->detached_inventory_OnMove( from_inv.name, from_list, from_i, to_list, to_i, count, player); } else { // Destination is detached if(to_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnPut( to_inv.name, to_list, to_i, src_item, player); } // Source is detached if(from_inv.type == InventoryLocation::DETACHED) { PLAYER_TO_SA(player)->detached_inventory_OnTake( from_inv.name, from_list, from_i, src_item, player); } } /* Node metadata inventories */ // Both endpoints are same nodemeta if(from_inv.type == InventoryLocation::NODEMETA && to_inv.type == InventoryLocation::NODEMETA && from_inv.p == to_inv.p) { PLAYER_TO_SA(player)->nodemeta_inventory_OnMove( from_inv.p, from_list, from_i, to_list, to_i, count, player); } else{ // Destination is nodemeta if(to_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnPut( to_inv.p, to_list, to_i, src_item, player); } // Source is nodemeta else if(from_inv.type == InventoryLocation::NODEMETA) { PLAYER_TO_SA(player)->nodemeta_inventory_OnTake( from_inv.p, from_list, from_i, src_item, player); } } mgr->setInventoryModified(from_inv, false); if(inv_from != inv_to) mgr->setInventoryModified(to_inv, false); }