diff --git a/README.md b/README.md index bd40e4d..02561fd 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,18 @@ To provide a web-based interface to receive/send mails you can use the [mtui](ht To access your mail click on the inventory mail button or use the "/mail" command Mails can be deleted, marked as read or unread, replied to and forwarded to another player +# Compatibility / Migration + +Overview: +* `v1` all the data is in the `/mails.db` file +* `v2` every player has its own (in-) mailbox in the `/mails/.json` file +* `v3` every player has an entry in the `` modstorage (inbox, outbox, contacts) + +Mails in the v1 format are supported until commit `b0a5bc7e47ec1c75339e65ec07d0a0ac2b17288b`. +Everything after that assumes either the v2 or v3 is used. + +For a v1 to v3 migration the version in `b0a5bc7e47ec1c75339e65ec07d0a0ac2b17288b` has to be at leas run once (startup). + # Dependencies * None diff --git a/gui.lua b/gui.lua index 7830136..00521ad 100644 --- a/gui.lua +++ b/gui.lua @@ -109,7 +109,8 @@ end function mail.show_inbox(name) local formspec = { mail.inbox_formspec } - local messages = mail.getPlayerInboxMessages(name) + local entry = mail.get_storage_entry(name) + local messages = entry.inbox message_drafts[name] = nil @@ -509,8 +510,10 @@ function mail.handle_receivefields(player, formname, fields) elseif formname == "mail:inbox" or formname == "mail:sent" then local name = player:get_player_name() -- split inbox and sent msgs for different tests - local messagesInbox = mail.getPlayerInboxMessages(name) - local messagesSent = mail.getPlayerSentMessages(name) + local entry = mail.get_storage_entry(name) + + local messagesInbox = entry.inbox + local messagesSent = entry.outbox if fields.inbox then -- inbox table local evt = minetest.explode_table_event(fields.inbox) @@ -629,8 +632,10 @@ function mail.handle_receivefields(player, formname, fields) elseif formname == "mail:message" then local name = player:get_player_name() - local messagesInbox = mail.getPlayerInboxMessages(name) - local messagesSent = mail.getPlayerSentMessages(name) + local entry = mail.get_storage_entry(name) + + local messagesInbox = entry.inbox + local messagesSent = entry.outbox if fields.back then if boxtab_index == 1 then diff --git a/init.lua b/init.lua index 285688d..c8e1959 100644 --- a/init.lua +++ b/init.lua @@ -7,7 +7,10 @@ mail = { -- mail directory maildir = minetest.get_worldpath().."/mails", - contactsdir = minetest.get_worldpath().."/mails/contacts" + contactsdir = minetest.get_worldpath().."/mails/contacts", + + -- mod storage + storage = minetest.get_mod_storage() } diff --git a/migrate.lua b/migrate.lua index d0f2f72..b6829e8 100644 --- a/migrate.lua +++ b/migrate.lua @@ -1,58 +1,28 @@ + +local STORAGE_VERSION_KEY = "@@version" + function mail.migrate() - local gen_file_v1 = io.open(minetest.get_worldpath().."/mail.db", "r") - if gen_file_v1 then - mail.migrate_v1_to_v2() - end - - local info_file_v3 = mail.read_json_file(mail.maildir .. "/mail.info.json") - if not info_file_v3.dbversion then + local version = mail.storage:get_int(STORAGE_VERSION_KEY) + if version < 3 then mail.migrate_v2_to_v3() + mail.storage:set_int(STORAGE_VERSION_KEY, 3) end end --- migrate from mail.db to player-file-based mailbox -function mail.migrate_v1_to_v2() - -- create directory, just in case - minetest.mkdir(mail.maildir) - minetest.mkdir(mail.contactsdir) - - local file = io.open(minetest.get_worldpath().."/mail.db", "r") - if file then - print("[mail] migrating to new per-player storage") - - local data = file:read("*a") - local oldmails = minetest.deserialize(data) - file:close() - - for name, oldmessages in pairs(oldmails) do - mail.setMessages(name, oldmessages) - end - - -- rename file - print("[mail] migration done, renaming old mail.db") - os.rename(minetest.get_worldpath().."/mail.db", minetest.get_worldpath().."/mail.db.old") - end - -end - -- migrate from v2 to v3 database function mail.migrate_v2_to_v3() minetest.mkdir(mail.maildir) -- if necessary (eg. first login) - minetest.log("info", "[mail] Migration from v2 to v3 database") - minetest.after(0,function() - for playername, _ in minetest.get_auth_handler().iterate() do - mail.migrate_contacts_v2_to_v3(playername) - end - end) - mail.migrate_messages_v2_to_v3() - mail.write_json_file(mail.maildir .. "/mail.info.json", { dbversion = 3.0 }) -end - -function mail.migrate_messages_v2_to_v3() + print("[mail] Migration from v2 to v3 database") local already_processed = {} -- store messages that are already process to avoid duplicates + minetest.after(0,function() - -- check in every inbox to fetch messages for playername, _ in minetest.get_auth_handler().iterate() do + local player_contacts = mail.read_json_file(mail.maildir .. "/contacts/" .. playername .. ".json") + local entry = mail.get_storage_entry(playername) + for _, c in pairs(player_contacts) do + table.insert(entry.contacts, { name = c.name, note = c.note }) + end + local saneplayername = string.gsub(playername, "[.|/]", "") local player_inbox = mail.read_json_file(mail.maildir .. "/" .. saneplayername .. ".json") for _, msg in ipairs(player_inbox) do @@ -79,46 +49,8 @@ function mail.migrate_messages_v2_to_v3() table.insert(already_processed, msg_id) end end + + mail.set_storage_entry(playername, entry) end end) end - -function mail.migrate_contacts(playername) - local gen_file_v1 = io.open(minetest.get_worldpath().."/mail.db", "r") - if gen_file_v1 then - mail.migrate_contacts_v1_to_v2(playername) - end - - -- v2 to v3 directly in general function -end - -function mail.migrate_contacts_v1_to_v2(playername) - local file = io.open(mail.getContactsFile(playername), 'r') - if not file then - -- file doesn't exist! This is a case for Migrate Man! - local messages = mail.getMessages(playername) - local contacts = {} - - if messages and not contacts then - for _, message in pairs(messages) do - mail.ensure_new_format(message) - if contacts[string.lower(message.from)] == nil then - contacts[string.lower(message.from)] = { - name = message.from, - note = "", - } - end - end - end - else - file:close() -- uh, um, nope, let's leave those alone, shall we? - end -end - -function mail.migrate_contacts_v2_to_v3(playername) - local player_contacts = mail.read_json_file(mail.maildir .. "/contacts/" .. playername .. ".json") - - for _, c in pairs(player_contacts) do - mail.addContact(playername, { name = c.name, note = c.note }) - end -end diff --git a/mtt.lua b/mtt.lua index 48d4838..ab0b017 100644 --- a/mtt.lua +++ b/mtt.lua @@ -7,7 +7,7 @@ mtt.register("send mail", function(callback) mail.send({from = "player1", to = "player2", subject = "something", body = "blah"}) -- check the receivers inbox - local list2 = mail.getPlayerInboxMessages("player2") - assert(list2 ~= nil and #list2 > 0) + local entry = mail.get_storage_entry("player2") + assert(entry ~= nil and #entry.inbox > 0) callback() end) diff --git a/onjoin.lua b/onjoin.lua index 4576c86..21b47d5 100644 --- a/onjoin.lua +++ b/onjoin.lua @@ -1,6 +1,6 @@ minetest.register_on_joinplayer(function(player) minetest.after(2, function(name) - local messages = mail.getPlayerMessages(name) + local messages = mail.getMessages(name) local unreadcount = 0 @@ -16,6 +16,4 @@ minetest.register_on_joinplayer(function(player) end end, player:get_player_name()) - - mail.migrate_contacts(player:get_player_name()) end) diff --git a/storage.lua b/storage.lua index f4dcae8..ff8e9d2 100644 --- a/storage.lua +++ b/storage.lua @@ -1,26 +1,20 @@ -function mail.getMailFile(playername) - local saneplayername = string.gsub(playername, "[.|/]", "") - return mail.maildir .. "/" .. saneplayername .. ".json" +function mail.get_storage_entry(playername) + local str = mail.storage:get_string(playername) + if str == "" then + -- new entry + return { + contacts = {}, + inbox = {}, + outbox = {} + } + else + -- deserialize existing entry + return minetest.parse_json(str) + end end -function mail.getMessages(playername) - if (playername) then - return mail.getPlayerMessages(playername) - end - local messages = mail.read_json_file(mail.maildir .. "/mail.messages.json") - if messages then - for _, msg in ipairs(messages) do - if not msg.time then - -- add missing time field if not available (happens with old data) - msg.time = 0 - end - end - - -- sort by received date descending - table.sort(messages, function(a,b) return a.time > b.time end) - end - - return messages +function mail.set_storage_entry(playername, entry) + mail.storage:get_string(playername, minetest.write_json(entry)) end function mail.getMessage(msg_id) @@ -34,7 +28,8 @@ function mail.getMessage(msg_id) end end -function mail.getPlayerMessages(playername) +-- api in use by the `fancyvend` mod +function mail.getMessages(playername) local messages = mail.getMessages() local playerMessages = {} if messages then @@ -68,37 +63,6 @@ function mail.getPlayerMessages(playername) return playerMessages end -function mail.getPlayerInboxMessages(playername) - local messages = mail.getMessages() - local playerInboxMessages = {} - if messages then - for _, msg in ipairs(messages) do - local cc = "" - local bcc = "" - if msg.cc then - cc = msg.cc - end - if msg.bcc then - bcc = msg.bcc - end - local receivers = (msg.to .. "," .. cc .. "," .. bcc):split(",") - for _, receiver in ipairs(receivers) do - receiver = string.gsub(receiver, " ", "") -- avoid blank spaces (ex : " singleplayer" instead of "singleplayer") - if receiver == playername then -- check if player is a receiver - if mail.getMessageStatus(receiver, msg.id) ~= "deleted" then -- do not return if the message was deleted by player - table.insert(playerInboxMessages, msg) - break - end - end - end - end - -- show hud notification - mail.hud_update(playername, playerInboxMessages) - end - - return playerInboxMessages -end - function mail.getPlayerSentMessages(playername) local messages = mail.getMessages() local playerSentMessages = {}