re-enable order logging using custom lua serialization code

This commit is contained in:
FaceDeer 2019-07-28 02:44:26 -06:00
parent 101dc3b796
commit e0d373dd5d
3 changed files with 238 additions and 37 deletions

View File

@ -255,17 +255,16 @@ local get_info_formspec = function(market, account)
"size[10,10]",
"tabheader[0,0;tabs;"..market.def.description..",Your Inventory,Market Orders;1;false;true]",
"textarea[0.5,0.5;9.5,1.5;;Description:;"..market.def.long_description.."]",
-- TODO: logging temporarily disabled, it was causing minetest.serialize to generate invalid output for some reason
--"textarea[0.5,2.5;9.5,6;;Your Recent Purchases and Sales:;",
"textarea[0.5,2.5;9.5,6;;Your Recent Purchases and Sales:;",
}
-- if next(account.log) then
-- for _, log_entry in ipairs(account.log) do
-- formspec[#formspec+1] = log_to_string(market, log_entry) .. "\n"
-- end
-- else
-- formspec[#formspec+1] = "No logged activites in this market yet"
-- end
-- formspec[#formspec+1] = "]"
if next(account.log) then
for _, log_entry in ipairs(account.log) do
formspec[#formspec+1] = log_to_string(market, log_entry) .. "\n"
end
else
formspec[#formspec+1] = "No logged activites in this market yet"
end
formspec[#formspec+1] = "]"
return table.concat(formspec)
end

View File

@ -77,21 +77,23 @@ local get_account = function(market, player_name)
return account
end
-- Caution: the data structures produced by sale logging caused me to discover
-- issue https://github.com/minetest/minetest/issues/8719 with minetest.serialize()
-- I'm working around it by using the code in persistence.lua instead
local log_sale = function(item, quantity, price, purchaser, seller)
-- TODO: disabled temporarily, the log code should work in theory but in practice minetest.serialize was generating invalid output for some reason.
-- local log_entry = {item=item, quantity=quantity, price=price, purchaser=purchaser, seller=seller, timestamp = minetest.get_gametime()}
-- local purchaser_log = purchaser.log
-- local seller_log = seller.log
-- table.insert(purchaser_log, log_entry)
-- if #purchaser_log > log_length_limit then
-- table.remove(purchaser_log, 1)
-- end
-- if (purchaser ~= seller) then
-- table.insert(seller_log, log_entry)
-- if #seller_log > log_length_limit then
-- table.remove(seller_log, 1)
-- end
-- end
local log_entry = {item=item, quantity=quantity, price=price, purchaser=purchaser, seller=seller, timestamp = minetest.get_gametime()}
local purchaser_log = purchaser.log
local seller_log = seller.log
table.insert(purchaser_log, log_entry)
if #purchaser_log > log_length_limit then
table.remove(purchaser_log, 1)
end
if (purchaser ~= seller) then
table.insert(seller_log, log_entry)
if #seller_log > log_length_limit then
table.remove(seller_log, 1)
end
end
end
local remove_orders_by_account = function(orders, account)
@ -379,29 +381,23 @@ local buy = function(self, player_name, item, quantity, price)
return add_buy(self, get_account(self, player_name), item, price, quantity)
end
-- Using this instead of minetest.serialize because of https://github.com/minetest/minetest/issues/8719
local MP = minetest.get_modpath(minetest.get_current_modname())
local persistence_store, persistence_load = dofile(MP.."/persistence.lua")
local load_market_data = function(marketname)
local path = minetest.get_worldpath()
local filename = path .. "\\market_"..marketname..".lua"
local file = loadfile(filename) -- returns nil if the file doesn't exist
if file then
return file()
else
return nil
end
return persistence_load(filename)
end
local save_market_data = function(market)
local path = minetest.get_worldpath()
local filename = path .. "\\market_"..market.name..".lua"
local file, err = io.open(filename, "w")
if err ~= nil then
minetest.log("error", "[commoditymarket] Could not save market to \"" .. filename .. "\"")
return false
end
local data = {}
data.player_accounts = market.player_accounts
data.orders_for_items = market.orders_for_items
file:write(minetest.serialize(data))
persistence_store(filename, data)
return true
end
@ -483,8 +479,14 @@ commoditymarket.register_market = function(market_name, market_def)
return 0
end,
allow_put = function(inv, listname, index, stack, player)
-- Currency items are always allowed
local item = stack:get_name()
-- reject unknown items
if minetest.registered_items[item] == nil then
return 0
end
-- Currency items are always allowed
if new_market.def.currency[item] then
return stack:get_count()
end

200
persistence.lua Normal file
View File

@ -0,0 +1,200 @@
-- Internal persistence library
--[[ Provides ]]
-- persistence.store(path, ...): Stores arbitrary items to the file at the given path
-- persistence.load(path): Loads files that were previously stored with store and returns them
--[[ Limitations ]]
-- Does not export userdata, threads or most function values
-- Function export is not portable
--[[ License: MIT (see bottom) ]]
-- Private methods
local write, writeIndent, writers, refCount;
-- write thing (dispatcher)
write = function (file, item, level, objRefNames)
writers[type(item)](file, item, level, objRefNames);
end;
-- write indent
writeIndent = function (file, level)
for i = 1, level do
file:write("\t");
end;
end;
-- recursively count references
refCount = function (objRefCount, item)
-- only count reference types (tables)
if type(item) == "table" then
-- Increase ref count
if objRefCount[item] then
objRefCount[item] = objRefCount[item] + 1;
else
objRefCount[item] = 1;
-- If first encounter, traverse
for k, v in pairs(item) do
refCount(objRefCount, k);
refCount(objRefCount, v);
end;
end;
end;
end;
-- Format items for the purpose of restoring
writers = {
["nil"] = function (file, item)
file:write("nil");
end;
["number"] = function (file, item)
file:write(tostring(item));
end;
["string"] = function (file, item)
file:write(string.format("%q", item));
end;
["boolean"] = function (file, item)
if item then
file:write("true");
else
file:write("false");
end
end;
["table"] = function (file, item, level, objRefNames)
local refIdx = objRefNames[item];
if refIdx then
-- Table with multiple references
file:write("multiRefObjects["..refIdx.."]");
else
-- Single use table
file:write("{\n");
for k, v in pairs(item) do
writeIndent(file, level+1);
file:write("[");
write(file, k, level+1, objRefNames);
file:write("] = ");
write(file, v, level+1, objRefNames);
file:write(";\n");
end
writeIndent(file, level);
file:write("}");
end;
end;
["function"] = function (file, item)
-- Does only work for "normal" functions, not those
-- with upvalues or c functions
local dInfo = debug.getinfo(item, "uS");
if dInfo.nups > 0 then
file:write("nil --[[functions with upvalue not supported]]");
elseif dInfo.what ~= "Lua" then
file:write("nil --[[non-lua function not supported]]");
else
local r, s = pcall(string.dump,item);
if r then
file:write(string.format("loadstring(%q)", s));
else
file:write("nil --[[function could not be dumped]]");
end
end
end;
["thread"] = function (file, item)
file:write("nil --[[thread]]\n");
end;
["userdata"] = function (file, item)
file:write("nil --[[userdata]]\n");
end;
}
return function (path, ...)
local file, e;
if type(path) == "string" then
-- Path, open a file
file, e = io.open(path, "w");
if not file then
return error(e);
end
else
-- Just treat it as file
file = path;
end
local n = select("#", ...);
-- Count references
local objRefCount = {}; -- Stores reference that will be exported
for i = 1, n do
refCount(objRefCount, (select(i,...)));
end;
-- Export Objects with more than one ref and assign name
-- First, create empty tables for each
local objRefNames = {};
local objRefIdx = 0;
file:write("-- Persistent Data\n");
file:write("local multiRefObjects = {\n");
for obj, count in pairs(objRefCount) do
if count > 1 then
objRefIdx = objRefIdx + 1;
objRefNames[obj] = objRefIdx;
file:write("{};"); -- table objRefIdx
end;
end;
file:write("\n} -- multiRefObjects\n");
-- Then fill them (this requires all empty multiRefObjects to exist)
for obj, idx in pairs(objRefNames) do
for k, v in pairs(obj) do
file:write("multiRefObjects["..idx.."][");
write(file, k, 0, objRefNames);
file:write("] = ");
write(file, v, 0, objRefNames);
file:write(";\n");
end;
end;
-- Create the remaining objects
for i = 1, n do
file:write("local ".."obj"..i.." = ");
write(file, (select(i,...)), 0, objRefNames);
file:write("\n");
end
-- Return them
if n > 0 then
file:write("return obj1");
for i = 2, n do
file:write(" ,obj"..i);
end;
file:write("\n");
else
file:write("return\n");
end;
file:close();
end, function (path)
local f, e = loadfile(path);
if f then
return f();
else
return nil, e;
end;
end
--[[
Copyright (c) 2010 Gerhard Roethlin
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
]]