Alright, I'll explain how does it work:
It will save the tile you choose to the database and load it whenever you call the load function. It supports attributes, but it is optional, if you don't want to modify your source you don't need to and it will still work but it wont save any attributes to the db (actionids, names, text etc...)
Lets go to the code:
Table schema:
Lib:
Add this to tile.lua in "\data\lib\core". You can stop here and jump to the talkaction if you don't wanna change your source to save attributes.
C++ :
luascript.h:
Under:
Add:
luascript.cpp:
Under:
Add:
Above:
Add:
Now it's up to you, whatever you're going to build it just be careful on how you save the tiles cause it can and will duplicate items if done wrong. (Saving tiles and then having a rollback wont rollback the saved tiles.)
Example Talkaction:
You can also add loadSavedTiles() to startup.lua in "globalevents\scripts" to load all tiles when opening the server.
You can use it either without any parameter and it will reload all the tiles saved.
If you use only 1 parameter it will reload only that position.
If you use 2 parameters it will reload the tiles inside that area. The first position is the NW corner pos and the second is the SE corner pos. It supports multi level you just have to remember that the NW must be the lower corner and the SE the higher one.
All the others are simple and self explanatory.
It will save the tile you choose to the database and load it whenever you call the load function. It supports attributes, but it is optional, if you don't want to modify your source you don't need to and it will still work but it wont save any attributes to the db (actionids, names, text etc...)
Lets go to the code:
Table schema:
Code:
CREATE TABLE `tile_save` (
`itemid` int(11) NOT NULL,
`count` int(11) NOT NULL,
`posx` int(11) NOT NULL,
`posy` int(11) NOT NULL,
`posz` int(11) NOT NULL,
`stackpos` int(11) NOT NULL,
`attributes` blob NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Lib:
Code:
if not Item.serializeAttributes or not Item.unserializeAttributes then
function Item.serializeAttributes(self)
return ""
end
function Item.unserializeAttributes(self)
return
end
end
function loadSavedTiles(pos, secondpos)
local storeQ
if not pos then
storeQ = "SELECT * FROM `tile_save` ORDER BY `stackpos` ASC;"
elseif not secondpos then
storeQ = "SELECT * FROM `tile_save` WHERE `posx` = " .. pos.x .. " AND `posy` = " .. pos.y .. " AND `posz` = " .. pos.z .. " ORDER BY `stackpos`ASC;"
else
storeQ = "SELECT * FROM `tile_save` WHERE `posx` >= " .. pos.x .. " AND `posy` >= " .. pos.y .. " AND `posx` <= " .. secondpos.x .. " AND `posy` <= " .. secondpos.y .. " AND `posz` >= " .. pos.z .. " AND `posz` <= " .. secondpos.z .. " ORDER BY `stackpos` ASC;"
end
db.asyncStoreQuery(storeQ,
function(query)
if query then
local ret = {}
repeat
local itemid, count, posx, posy, posz, stackpos = result.getDataInt(query, "itemid"), result.getDataInt(query, "count"), result.getDataInt(query, "posx"), result.getDataInt(query, "posy"), result.getDataInt(query, "posz"), result.getDataInt(query, "stackpos")
local attributes = result.getStream(query, "attributes") or ""
if itemid and posx and posy and posz then
local tile = Tile(posx, posy, posz)
if tile then
if stackpos == 0 then
tile:removeFromMap()
end
local item = Game.createItem(itemid, count, {x=posx, y=posy, z=posz, stackpos=stackpos})
if item then
item:unserializeAttributes(attributes)
end
end
end
until not result.next(query)
end
end)
end
function Tile:removeFromMap()
local items = self:getItems()
table.insert(items, self:getGround())
for i = 1, #items do
items[i]:remove()
end
end
function Tile:save()
if self:getGround() then
local pos = self:getPosition()
db.query("DELETE FROM `tile_save` WHERE `posx` = " .. pos.x .. " AND `posy` = " .. pos.y .. " AND `posz` = " .. pos.z .. ";")
local buffer = {"INSERT INTO `tile_save` (`itemid`, `count`, `posx`, `posy`, `posz`, `stackpos`, `attributes`) VALUES "}
local items = {self:getGround()}
local itab = self:getItems()
for i = #itab, 1, -1 do
table.insert(items, itab[i])
end
for i = 1, #items do
local item = items[i]
local attributes = item:serializeAttributes()
if i ~= 1 then
table.insert(buffer, ",")
end
table.insert(buffer, string.format("(%d, %d, %d, %d, %d, %d, %s)", item:getId(), item:getCount(), pos.x, pos.y, pos.z, i-1, db.escapeBlob(attributes, #attributes)))
end
table.insert(buffer, ";")
db.query(table.concat(buffer))
end
end
function Tile:removeSave()
local pos = self:getPosition()
db.query("DELETE FROM `tile_save` WHERE `posx` = " .. pos.x .. " AND `posy` = " .. pos.y .. " AND `posz` = " .. pos.z .. ";")
end
function Tile:load()
loadSavedTiles(self:getPosition())
return true
end
Add this to tile.lua in "\data\lib\core". You can stop here and jump to the talkaction if you don't wanna change your source to save attributes.
C++ :
luascript.h:
Under:
Code:
static int luaItemRemoveAttribute(lua_State* L);
Add:
Code:
static int luaItemSerializeAttributes(lua_State* L);
static int luaItemUnserializeAttributes(lua_State* L);
luascript.cpp:
Under:
Code:
registerMethod("Item", "removeAttribute", LuaScriptInterface::luaItemRemoveAttribute);
Add:
Code:
registerMethod("Item", "serializeAttributes", LuaScriptInterface::luaItemSerializeAttributes);
registerMethod("Item", "unserializeAttributes", LuaScriptInterface::luaItemUnserializeAttributes);
Above:
Code:
int LuaScriptInterface::luaItemMoveTo(lua_State* L)
Add:
Code:
int LuaScriptInterface::luaItemSerializeAttributes(lua_State* L)
{
// item:serializeAttributes()
Item* item = getUserdata<Item>(L, 1);
if (!item) {
lua_pushnil(L);
return 1;
}
PropWriteStream propWriteStream;
item->serializeAttr(propWriteStream);
size_t attributesSize;
const char* attributes = propWriteStream.getStream(attributesSize);
lua_pushlstring(L, attributes, attributesSize);
return 1;
}
int LuaScriptInterface::luaItemUnserializeAttributes(lua_State* L)
{
// item:unserializeAttributes(propStream)
Item* item = getUserdata<Item>(L, 1);
if (!item) {
lua_pushnil(L);
return 1;
}
size_t attrSize;
const char* attr = lua_tolstring(L, 2, &attrSize);
PropStream propStream;
propStream.init(attr, attrSize);
item->unserializeAttr(propStream);
return 1;
}
Now it's up to you, whatever you're going to build it just be careful on how you save the tiles cause it can and will duplicate items if done wrong. (Saving tiles and then having a rollback wont rollback the saved tiles.)
Example Talkaction:
Code:
function onSay(player, words, param)
local tile = Tile(player:getPosition())
if param == "save" then
tile:save()
elseif param == "load" then
loadSavedTiles()
elseif param == "unsave" then
tile:removeSave()
else
player:sendTextMessage(MESSAGE_STATUS_SMALL, "You can either save the tile or load all tiles")
end
return false
end
You can also add loadSavedTiles() to startup.lua in "globalevents\scripts" to load all tiles when opening the server.
- !command save: Save the current tile from the player position
- !command load: Loads ALL tiles saved in the database.
- !command unsave: Removes the current tile information from the database.
You can use it either without any parameter and it will reload all the tiles saved.
If you use only 1 parameter it will reload only that position.
If you use 2 parameters it will reload the tiles inside that area. The first position is the NW corner pos and the second is the SE corner pos. It supports multi level you just have to remember that the NW must be the lower corner and the SE the higher one.
All the others are simple and self explanatory.
Last edited: