Example #1
0
bool RollbackAction::applyRevert(Map *map, InventoryManager *imgr, IGameDef *gamedef) const
{
	try {
		switch (type) {
		case TYPE_NOTHING:
			return true;
		case TYPE_SET_NODE: {
			INodeDefManager *ndef = gamedef->ndef();
			// Make sure position is loaded from disk
			map->emergeBlock(getContainerPos(p, MAP_BLOCKSIZE), false);
			// Check current node
			MapNode current_node = map->getNodeNoEx(p);
			std::string current_name = ndef->get(current_node).name;
			// If current node not the new node, it's bad
			if (current_name != n_new.name) {
				return false;
			}
			// Create rollback node
			MapNode n(ndef, n_old.name, n_old.param1, n_old.param2);
			// Set rollback node
			try {
				if (!map->addNodeWithEvent(p, n)) {
					infostream << "RollbackAction::applyRevert(): "
						<< "AddNodeWithEvent failed at "
						<< PP(p) << " for " << n_old.name
						<< std::endl;
					return false;
				}
				if (n_old.meta.empty()) {
					map->removeNodeMetadata(p);
				} else {
					NodeMetadata *meta = map->getNodeMetadata(p);
					if (!meta) {
						meta = new NodeMetadata(gamedef);
						if (!map->setNodeMetadata(p, meta)) {
							delete meta;
							infostream << "RollbackAction::applyRevert(): "
								<< "setNodeMetadata failed at "
								<< PP(p) << " for " << n_old.name
								<< std::endl;
							return false;
						}
					}
					std::istringstream is(n_old.meta, std::ios::binary);
					meta->deSerialize(is);
				}
				// Inform other things that the meta data has changed
				v3s16 blockpos = getContainerPos(p, MAP_BLOCKSIZE);
				MapEditEvent event;
				event.type = MEET_BLOCK_NODE_METADATA_CHANGED;
				event.p = blockpos;
				map->dispatchEvent(&event);
				// Set the block to be saved
				MapBlock *block = map->getBlockNoCreateNoEx(blockpos);
				if (block) {
					block->raiseModified(MOD_STATE_WRITE_NEEDED,
						MOD_REASON_REPORT_META_CHANGE);
				}
			} catch (InvalidPositionException &e) {
				infostream << "RollbackAction::applyRevert(): "
					<< "InvalidPositionException: " << e.what()
					<< std::endl;
				return false;
			}
			// Success
			return true; }
		case TYPE_MODIFY_INVENTORY_STACK: {
			InventoryLocation loc;
			loc.deSerialize(inventory_location);
			std::string real_name = gamedef->idef()->getAlias(inventory_stack.name);
			Inventory *inv = imgr->getInventory(loc);
			if (!inv) {
				infostream << "RollbackAction::applyRevert(): Could not get "
					"inventory at " << inventory_location << std::endl;
				return false;
			}
			InventoryList *list = inv->getList(inventory_list);
			if (!list) {
				infostream << "RollbackAction::applyRevert(): Could not get "
					"inventory list \"" << inventory_list << "\" in "
					<< inventory_location << std::endl;
				return false;
			}
			if (list->getSize() <= inventory_index) {
				infostream << "RollbackAction::applyRevert(): List index "
					<< inventory_index << " too large in "
					<< "inventory list \"" << inventory_list << "\" in "
					<< inventory_location << std::endl;
			}
			// If item was added, take away item, otherwise add removed item
			if (inventory_add) {
				// Silently ignore different current item
				if (list->getItem(inventory_index).name != real_name)
					return false;
				list->takeItem(inventory_index, inventory_stack.count);
			} else {
				list->addItem(inventory_index, inventory_stack);
			}
			// Inventory was modified; send to clients
			imgr->setInventoryModified(loc);
			return true; }
		default:
			errorstream << "RollbackAction::applyRevert(): type not handled"
				<< std::endl;
			return false;
		}
	} catch(SerializationError &e) {
		errorstream << "RollbackAction::applyRevert(): n_old.name=" << n_old.name
				<< ", SerializationError: " << e.what() << std::endl;
	}
	return false;
}