From 8aef2c19bab48d552c1701f22a273b6b521e51fa Mon Sep 17 00:00:00 2001 From: Alexander Weber Date: Mon, 5 Sep 2016 21:02:53 +0200 Subject: The u_skins is a mod, not a modpack. Moved u_skins to the root and all updater stuff to the (new) updater directory --- MT_skins_updater.cs | 200 ------------------------------ MT_skins_updater.exe | Bin 10752 -> 0 bytes Newtonsoft.Json.dll | Bin 491008 -> 0 bytes README | 7 +- depends.txt | 4 + init.lua | 183 +++++++++++++++++++++++++++ locale/de.txt | 9 ++ locale/template.txt | 9 ++ meta/character_1.txt | 3 + meta/character_2.txt | 3 + meta/placeholder.txt | 1 + modpack.txt | 0 players.lua | 40 ++++++ skinlist.lua | 26 ++++ textures/character_1.png | Bin 0 -> 2055 bytes textures/character_1_preview.png | Bin 0 -> 783 bytes textures/character_2.png | Bin 0 -> 3223 bytes textures/character_2_preview.png | Bin 0 -> 1051 bytes textures/inventory_plus_skins.png | Bin 0 -> 673 bytes textures/u_skins_button.png | Bin 0 -> 338 bytes textures/ui_misc_form.png | Bin 0 -> 9354 bytes u_skins/depends.txt | 4 - u_skins/init.lua | 183 --------------------------- u_skins/locale/de.txt | 9 -- u_skins/locale/template.txt | 9 -- u_skins/meta/character_1.txt | 3 - u_skins/meta/character_2.txt | 3 - u_skins/meta/placeholder.txt | 1 - u_skins/players.lua | 40 ------ u_skins/skinlist.lua | 26 ---- u_skins/textures/character_1.png | Bin 2055 -> 0 bytes u_skins/textures/character_1_preview.png | Bin 783 -> 0 bytes u_skins/textures/character_2.png | Bin 3223 -> 0 bytes u_skins/textures/character_2_preview.png | Bin 1051 -> 0 bytes u_skins/textures/inventory_plus_skins.png | Bin 673 -> 0 bytes u_skins/textures/u_skins_button.png | Bin 338 -> 0 bytes u_skins/textures/ui_misc_form.png | Bin 9354 -> 0 bytes update_from_db.py | 180 --------------------------- update_skins_db.sh | 78 ------------ updater/MT_skins_updater.cs | 200 ++++++++++++++++++++++++++++++ updater/MT_skins_updater.exe | Bin 0 -> 10752 bytes updater/Newtonsoft.Json.dll | Bin 0 -> 491008 bytes updater/update_from_db.py | 180 +++++++++++++++++++++++++++ updater/update_skins_db.sh | 79 ++++++++++++ 44 files changed, 741 insertions(+), 739 deletions(-) delete mode 100644 MT_skins_updater.cs delete mode 100644 MT_skins_updater.exe delete mode 100644 Newtonsoft.Json.dll create mode 100644 depends.txt create mode 100644 init.lua create mode 100644 locale/de.txt create mode 100644 locale/template.txt create mode 100644 meta/character_1.txt create mode 100644 meta/character_2.txt create mode 100644 meta/placeholder.txt delete mode 100644 modpack.txt create mode 100644 players.lua create mode 100644 skinlist.lua create mode 100644 textures/character_1.png create mode 100644 textures/character_1_preview.png create mode 100644 textures/character_2.png create mode 100644 textures/character_2_preview.png create mode 100644 textures/inventory_plus_skins.png create mode 100644 textures/u_skins_button.png create mode 100644 textures/ui_misc_form.png delete mode 100644 u_skins/depends.txt delete mode 100644 u_skins/init.lua delete mode 100644 u_skins/locale/de.txt delete mode 100644 u_skins/locale/template.txt delete mode 100644 u_skins/meta/character_1.txt delete mode 100644 u_skins/meta/character_2.txt delete mode 100644 u_skins/meta/placeholder.txt delete mode 100644 u_skins/players.lua delete mode 100644 u_skins/skinlist.lua delete mode 100644 u_skins/textures/character_1.png delete mode 100644 u_skins/textures/character_1_preview.png delete mode 100644 u_skins/textures/character_2.png delete mode 100644 u_skins/textures/character_2_preview.png delete mode 100644 u_skins/textures/inventory_plus_skins.png delete mode 100644 u_skins/textures/u_skins_button.png delete mode 100644 u_skins/textures/ui_misc_form.png delete mode 100755 update_from_db.py delete mode 100644 update_skins_db.sh create mode 100644 updater/MT_skins_updater.cs create mode 100644 updater/MT_skins_updater.exe create mode 100644 updater/Newtonsoft.Json.dll create mode 100755 updater/update_from_db.py create mode 100755 updater/update_skins_db.sh diff --git a/MT_skins_updater.cs b/MT_skins_updater.cs deleted file mode 100644 index 6d27da0..0000000 --- a/MT_skins_updater.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -//Json.NET library (http://json.codeplex.com/) -using Newtonsoft.Json; -using System.Collections.Generic; -using System.Net; -using System.IO; - -// MT skins updater for the u_skins mod -// Creator: Krock -// License: zlib (http://www.zlib.net/zlib_license.html) -namespace MT_skins_updater { - class Program { - static void Main(string[] args) { - Console.WriteLine("Welcome to the MT skins updater!"); - Console.WriteLine("# Created by: Krock (2014-07-10)"); - Engine e = new Engine(); - Console.WriteLine(@"Path to the u_skins mod: (ex. 'E:\Minetest\mods\u_skinsdb\u_skins\')"); - string path = Console.ReadLine(); - Console.WriteLine("Start updating at page: ('0' to update everything)"); - int page = getInt(Console.ReadLine()); - e.Start(path, page); - Console.WriteLine("Press any key to exit."); - Console.ReadKey(false); - } - public static int getInt(string i) { - int ret = 0; - int.TryParse(i, out ret); - return (ret > 0)? ret : 0; - } - } - class Engine { - string root = "http://minetest.fensta.bplaced.net"; - bool alternate = true; //should it use the special version of medadata saving? - - public void Start(string path, int page) { - if (path.Length < 5) { - Console.WriteLine("Too short path. STOP."); - return; - } - if (path[path.Length - 1] != '\\') { - path += '\\'; - } - if(!Directory.Exists(path + "meta")){ - Console.WriteLine("Folder 'meta' not found. STOP."); - return; - } - if(!Directory.Exists(path + "textures")){ - Console.WriteLine("Folder 'textures' not found. STOP."); - return; - } - WebClient cli = new WebClient(); - //add useragent to identify - cli.Headers.Add("User-Agent", "MT_skin_grabber 1.1"); - - bool firstSkin = true; - List skin_local = new List(); - int pages = page, - updated = 0; - - for (; page <= pages; page++) { - string contents = ""; - try { - contents = cli.DownloadString(root + "/api/get.json.php?getlist&page=" + page); - } catch(WebException e) { - Console.WriteLine("Whoops! Error at page ID: " + page + ". WebClient sais: " + e.Message); - Console.WriteLine("Press any key to skip this page."); - Console.ReadKey(false); - continue; - } - Data o = JsonConvert.DeserializeObject(contents); - if (o.pages != pages) { - pages = o.pages; - } - - Console.WriteLine("# Page " + page + " (" + o.per_page + " skins)"); - for (int i = 0; i < o.skins.Length; i++) { - int id = o.skins[i].id; - if(o.skins[i].type != "image/png"){ - Console.WriteLine("Image type '" + o.skins[i].type + "' not supported at skin ID: " + id); - Console.WriteLine("Press any key to continue."); - Console.ReadKey(false); - continue; - } - //eliminate special chars! - o.skins[i].name = WebUtility.HtmlDecode(o.skins[i].name); - o.skins[i].author = WebUtility.HtmlDecode(o.skins[i].author); - - //to delete old, removed skins - if (firstSkin) { - firstSkin = false; - - string[] files = Directory.GetFiles(path + "textures\\"); - for (int f = 0; f < files.Length; f++) { - string[] filePath = stringSplitLast(files[f], '\\'), - fileName = stringSplitLast(filePath[1], '.'), - fileVer = stringSplitLast(fileName[0], '_'); - if (fileVer[1] == "" || fileVer[0] != "character") continue; - - int skinNr = Program.getInt(fileVer[1]); - if (skinNr <= id) continue; - skin_local.Add(fileName[0]); - } - } else skin_local.Remove("character_" + id); - - //get file size, only override changed - FileInfo localImg = new FileInfo(path + "textures\\character_" + id + ".png"); - byte[] imageData = Convert.FromBase64String(o.skins[i].img); - bool isDif = true; - if (localImg.Exists) isDif = (Math.Abs(imageData.Length - localImg.Length) >= 3); - - if (isDif) { - File.WriteAllBytes(localImg.FullName, imageData); - imageData = null; - //previews - try { - cli.DownloadFile(root + "/skins/1/" + id + ".png", path + "textures\\character_" + id + "_preview.png"); - } catch (WebException e) { - Console.WriteLine("Whoops! Error at skin ID: " + id + ". WebClient sais: " + e.Message); - Console.WriteLine("Press any key to continue."); - Console.ReadKey(false); - } - } else { - Console.WriteLine("[SKIP] character_" + id); - continue; - } - - string meta = ""; - if (!alternate) { - meta = "name = \"" + o.skins[i].name + "\",\n"; - meta += "author = \"" + o.skins[i].author + "\",\n"; - meta += "comment = \"" + o.skins[i].license + '"'; - } else { - meta = o.skins[i].name + '\n' + o.skins[i].author + '\n' + o.skins[i].license; - } - File.WriteAllText(path + "meta\\character_" + id + ".txt", meta); - updated++; - Console.WriteLine("[" + id + "] " + shorten(o.skins[i].name, 20) + "\t by: " + o.skins[i].author + "\t (" + o.skins[i].license + ")"); - } - } - foreach (string fileName in skin_local) { - if(File.Exists(path + "textures\\" + fileName + ".png")) { - File.Delete(path + "textures\\" + fileName + ".png"); - } - if(File.Exists(path + "textures\\" + fileName + "_preview.png")) { - File.Delete(path + "textures\\" + fileName + "_preview.png"); - } - if(File.Exists(path + "meta\\" + fileName + ".txt")) { - File.Delete(path + "meta\\" + fileName + ".txt"); - } - Console.WriteLine("[DEL] " + fileName + " (deleted skin)"); - } - Console.WriteLine("Done. Updated " + updated + " skins!"); - } - string shorten(string inp, int len) { - char[] shr = new char[len]; - for (int i = 0; i < len; i++) { - if (i < inp.Length) { - shr[i] = inp[i]; - } else shr[i] = ' '; - } - return new string(shr); - } - - string[] stringSplitLast(string path, char limiter) { - int found = 0; - int totalLen = path.Length - 1; - for (int i = totalLen; i >= 0; i--) { - if (path[i] == limiter) { - found = i; - break; - } - } - if (found == 0) { - return new string[] { "", "" }; - } - - int len = totalLen - found; - char[] str_1 = new char[found], - str_2 = new char[len]; - - for (int i = 0; i < path.Length; i++) { - if (i == found) continue; - if (i < found) { - str_1[i] = path[i]; - } else { - str_2[i - found - 1] = path[i]; - } - } - return new string[] { new string(str_1), new string(str_2) }; - } - } - class Data { - public Skins_data[] skins; - public int page, pages, per_page; - } - class Skins_data { - public string name, author, uploaded, type, license, img; - public int id, license_id; - } -} diff --git a/MT_skins_updater.exe b/MT_skins_updater.exe deleted file mode 100644 index 5b4ee3e..0000000 Binary files a/MT_skins_updater.exe and /dev/null differ diff --git a/Newtonsoft.Json.dll b/Newtonsoft.Json.dll deleted file mode 100644 index 054c933..0000000 Binary files a/Newtonsoft.Json.dll and /dev/null differ diff --git a/README b/README index ff45c16..dec6789 100644 --- a/README +++ b/README @@ -6,9 +6,10 @@ It downloads the skins from the Minetest skin database. (http://minetest.fensta. Requires latest unified_inventory from: https://github.com/minetest-technic/unified_inventory -To download the latest skins you need to run: - "./update_from_db.py" OR - the win32.NET client +To download the latest there are 3 tools available in "updater" folder: + "./update_skins_db.sh" bash and jq required + "./update_from_db.py" python3 required + "MT_skins_updater.exe .." windows or mono required. ".." is the path to u_skins folder Licenses: -------- diff --git a/depends.txt b/depends.txt new file mode 100644 index 0000000..d6abd12 --- /dev/null +++ b/depends.txt @@ -0,0 +1,4 @@ +unified_inventory +default +simple_skins? +intllib? diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..2d3246c --- /dev/null +++ b/init.lua @@ -0,0 +1,183 @@ +-- Unified Skins for Minetest - based modified Bags from unfied_inventory and skins from inventory_plus + +-- Copyright (c) 2012 cornernote, Dean Montgomery +-- License: GPLv3 +-- Boilerplate to support localized strings if intllib mod is installed. +local S +if intllib then + S = intllib.Getter() +else + S = function(s) return s end +end + +u_skins = {} +u_skins.modpath = minetest.get_modpath("u_skins") +u_skins.file = minetest.get_worldpath().."/u_skins.mt" +u_skins.default = "character_1" +u_skins.pages = {} +u_skins.u_skins = {} +u_skins.file_save = false +u_skins.simple_skins = false + +-- ( Deprecated +u_skins.type = { SPRITE=0, MODEL=1, ERROR=99 } +u_skins.get_type = function(texture) + if not u_skins.is_skin(texture) then + return u_skins.type.ERROR + end + return u_skins.type.MODEL +end +-- ) + +u_skins.is_skin = function(texture) + if not texture then + return false + end + if not u_skins.meta[texture] then + return false + end + return true +end + +dofile(u_skins.modpath.."/skinlist.lua") +dofile(u_skins.modpath.."/players.lua") + +if rawget(_G, "skins") then + u_skins.simple_skins = true +end + +u_skins.update_player_skin = function(player) + local name = player:get_player_name() + if u_skins.simple_skins and u_skins.u_skins[name] == u_skins.default then + return + end + + if not u_skins.is_skin(u_skins.u_skins[name]) then + u_skins.u_skins[name] = u_skins.default + end + player:set_properties({ + textures = {u_skins.u_skins[name]..".png"}, + }) +end + +-- Display Current Skin +unified_inventory.register_page("u_skins", { + get_formspec = function(player) + local name = player:get_player_name() + if not u_skins.is_skin(u_skins.u_skins[name]) then + u_skins.u_skins[name] = u_skins.default + end + + local formspec = ("background[0.06,0.99;7.92,7.52;ui_misc_form.png]" + .."image[0,.75;1,2;"..u_skins.u_skins[name].."_preview.png]" + .."label[6,.5;"..S("Raw texture")..":]" + .."image[6,1;2,1;"..u_skins.u_skins[name]..".png]") + + local meta = u_skins.meta[u_skins.u_skins[name]] + if meta then + if meta.name ~= "" then + formspec = formspec.."label[2,.5;"..S("Name")..": "..minetest.formspec_escape(meta.name).."]" + end + if meta.author ~= "" then + formspec = formspec.."label[2,1;"..S("Author")..": "..minetest.formspec_escape(meta.author).."]" + end + if meta.license ~= "" then + formspec = formspec.."label[2,1.5;"..S("License")..": "..minetest.formspec_escape(meta.license).."]" + end + if meta.description ~= "" then --what's that?? + formspec = formspec.."label[2,2;"..S("Description")..": "..minetest.formspec_escape(meta.description).."]" + end + end + local page = 0 + if u_skins.pages[name] then + page = u_skins.pages[name] + end + formspec = formspec .. "button[.75,3;6.5,.5;u_skins_page$"..page..";"..S("Change").."]" + return {formspec=formspec} + end, +}) + +unified_inventory.register_button("u_skins", { + type = "image", + image = "u_skins_button.png", +}) + +-- Create all of the skin-picker pages. + +u_skins.generate_pages = function(texture) + local page = 0 + local pages = {} + for i, skin in ipairs(u_skins.list) do + local p_index = (i - 1) % 16 + if p_index == 0 then + page = page + 1 + pages[page] = {} + end + pages[page][p_index + 1] = {i, skin} + end + local total_pages = page + page = 1 + for page, arr in ipairs(pages) do + local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]" + local y = -0.1 + for i, skin in ipairs(arr) do + local x = (i - 1) % 8 + if i > 1 and x == 0 then + y = 1.8 + end + formspec = (formspec.."image_button["..x..","..y..";1,2;".. + skin[2].."_preview.png;u_skins_set$"..skin[1]..";]".. + "tooltip[u_skins_set$"..skin[1]..";"..u_skins.meta[skin[2]].name.."]") + end + local page_prev = page - 2 + local page_next = page + if page_prev < 0 then + page_prev = total_pages - 1 + end + if page_next >= total_pages then + page_next = 0 + end + formspec = (formspec + .."button[0,3.8;1,.5;u_skins_page$"..page_prev..";<<]" + .."button[.75,3.8;6.5,.5;u_skins_null;"..S("Page").." "..page.."/"..total_pages.."]" + .."button[7,3.8;1,.5;u_skins_page$"..page_next..";>>]") + + unified_inventory.register_page("u_skins_page$"..(page - 1), { + get_formspec = function(player) + return {formspec=formspec} + end + }) + end +end + +-- click button handlers +minetest.register_on_player_receive_fields(function(player, formname, fields) + if fields.u_skins then + unified_inventory.set_inventory_formspec(player, "craft") + return + end + for field, _ in pairs(fields) do + local current = string.split(field, "$", 2) + if current[1] == "u_skins_set" then + u_skins.u_skins[player:get_player_name()] = u_skins.list[tonumber(current[2])] + u_skins.update_player_skin(player) + u_skins.file_save = true + unified_inventory.set_inventory_formspec(player, "u_skins") + elseif current[1] == "u_skins_page" then + u_skins.pages[player:get_player_name()] = current[2] + unified_inventory.set_inventory_formspec(player, "u_skins_page$"..current[2]) + end + end +end) + +-- Change skin on join - reset if invalid +minetest.register_on_joinplayer(function(player) + local player_name = player:get_player_name() + if not u_skins.is_skin(u_skins.u_skins[player_name]) then + u_skins.u_skins[player_name] = u_skins.default + end + u_skins.update_player_skin(player) +end) + +u_skins.generate_pages() +u_skins.load_players() diff --git a/locale/de.txt b/locale/de.txt new file mode 100644 index 0000000..6ad87f3 --- /dev/null +++ b/locale/de.txt @@ -0,0 +1,9 @@ +# Translation by Xanthin + +Raw texture = Rohtextur +Name = Name +Author = Autor +Change = Wechseln +Page = Seite +License = Lizenz +Description = Beschreibung diff --git a/locale/template.txt b/locale/template.txt new file mode 100644 index 0000000..6412b7f --- /dev/null +++ b/locale/template.txt @@ -0,0 +1,9 @@ +# Template + +Raw texture = +Name = +Author = +Change = +Page = +License = +Description = diff --git a/meta/character_1.txt b/meta/character_1.txt new file mode 100644 index 0000000..7211029 --- /dev/null +++ b/meta/character_1.txt @@ -0,0 +1,3 @@ +Sam 0 +Jordach +CC BY-SA 3.0 \ No newline at end of file diff --git a/meta/character_2.txt b/meta/character_2.txt new file mode 100644 index 0000000..0187cab --- /dev/null +++ b/meta/character_2.txt @@ -0,0 +1,3 @@ +Sam I +Jordach +CC BY-SA 3.0 \ No newline at end of file diff --git a/meta/placeholder.txt b/meta/placeholder.txt new file mode 100644 index 0000000..a9e6fce --- /dev/null +++ b/meta/placeholder.txt @@ -0,0 +1 @@ +Please run the update_from_db.py script to update the skins. diff --git a/modpack.txt b/modpack.txt deleted file mode 100644 index e69de29..0000000 diff --git a/players.lua b/players.lua new file mode 100644 index 0000000..3053530 --- /dev/null +++ b/players.lua @@ -0,0 +1,40 @@ +u_skins.load_players = function() + local file = io.open(u_skins.file, "r") + if file then + for line in file:lines() do + local data = string.split(line, " ", 2) + u_skins.u_skins[data[1]] = data[2] + end + io.close(file) + end +end +u_skins.load_players() + +local ttime = 0 +minetest.register_globalstep(function(t) + ttime = ttime + t + if ttime < 360 then --every 6min' + return + end + ttime = 0 + u_skins.save() +end) + +minetest.register_on_shutdown(function() u_skins.save() end) + +u_skins.save = function() + if not u_skins.file_save then + return + end + u_skins.file_save = false + local output = io.open(u_skins.file, "w") + for name, skin in pairs(u_skins.u_skins) do + if name and skin then + if skin ~= u_skins.default then + output:write(name.." "..skin.."\n") + end + end + end + io.close(output) +end + diff --git a/skinlist.lua b/skinlist.lua new file mode 100644 index 0000000..718646c --- /dev/null +++ b/skinlist.lua @@ -0,0 +1,26 @@ +u_skins.list = {} +u_skins.meta = {} + +local id = 1 +local internal_id = 1 +local fetched_skip = 0 +while fetched_skip < 40 do + local name = "character_"..id + local file = io.open(u_skins.modpath.."/meta/"..name..".txt", "r") + if file then + local data = string.split(file:read("*all"), "\n", 3) + file:close() + + u_skins.list[internal_id] = name + u_skins.meta[name] = {} + u_skins.meta[name].name = data[1] + u_skins.meta[name].author = data[2] + u_skins.meta[name].license = data[3] + u_skins.meta[name].description = "" --what's that?? + + fetched_skip = 0 + internal_id = internal_id + 1 + end + fetched_skip = fetched_skip + 1 + id = id + 1 +end \ No newline at end of file diff --git a/textures/character_1.png b/textures/character_1.png new file mode 100644 index 0000000..8d0dd99 Binary files /dev/null and b/textures/character_1.png differ diff --git a/textures/character_1_preview.png b/textures/character_1_preview.png new file mode 100644 index 0000000..62a9ff2 Binary files /dev/null and b/textures/character_1_preview.png differ diff --git a/textures/character_2.png b/textures/character_2.png new file mode 100644 index 0000000..d794b87 Binary files /dev/null and b/textures/character_2.png differ diff --git a/textures/character_2_preview.png b/textures/character_2_preview.png new file mode 100644 index 0000000..0736def Binary files /dev/null and b/textures/character_2_preview.png differ diff --git a/textures/inventory_plus_skins.png b/textures/inventory_plus_skins.png new file mode 100644 index 0000000..781b42f Binary files /dev/null and b/textures/inventory_plus_skins.png differ diff --git a/textures/u_skins_button.png b/textures/u_skins_button.png new file mode 100644 index 0000000..cb49531 Binary files /dev/null and b/textures/u_skins_button.png differ diff --git a/textures/ui_misc_form.png b/textures/ui_misc_form.png new file mode 100644 index 0000000..d34d326 Binary files /dev/null and b/textures/ui_misc_form.png differ diff --git a/u_skins/depends.txt b/u_skins/depends.txt deleted file mode 100644 index d6abd12..0000000 --- a/u_skins/depends.txt +++ /dev/null @@ -1,4 +0,0 @@ -unified_inventory -default -simple_skins? -intllib? diff --git a/u_skins/init.lua b/u_skins/init.lua deleted file mode 100644 index 2d3246c..0000000 --- a/u_skins/init.lua +++ /dev/null @@ -1,183 +0,0 @@ --- Unified Skins for Minetest - based modified Bags from unfied_inventory and skins from inventory_plus - --- Copyright (c) 2012 cornernote, Dean Montgomery --- License: GPLv3 --- Boilerplate to support localized strings if intllib mod is installed. -local S -if intllib then - S = intllib.Getter() -else - S = function(s) return s end -end - -u_skins = {} -u_skins.modpath = minetest.get_modpath("u_skins") -u_skins.file = minetest.get_worldpath().."/u_skins.mt" -u_skins.default = "character_1" -u_skins.pages = {} -u_skins.u_skins = {} -u_skins.file_save = false -u_skins.simple_skins = false - --- ( Deprecated -u_skins.type = { SPRITE=0, MODEL=1, ERROR=99 } -u_skins.get_type = function(texture) - if not u_skins.is_skin(texture) then - return u_skins.type.ERROR - end - return u_skins.type.MODEL -end --- ) - -u_skins.is_skin = function(texture) - if not texture then - return false - end - if not u_skins.meta[texture] then - return false - end - return true -end - -dofile(u_skins.modpath.."/skinlist.lua") -dofile(u_skins.modpath.."/players.lua") - -if rawget(_G, "skins") then - u_skins.simple_skins = true -end - -u_skins.update_player_skin = function(player) - local name = player:get_player_name() - if u_skins.simple_skins and u_skins.u_skins[name] == u_skins.default then - return - end - - if not u_skins.is_skin(u_skins.u_skins[name]) then - u_skins.u_skins[name] = u_skins.default - end - player:set_properties({ - textures = {u_skins.u_skins[name]..".png"}, - }) -end - --- Display Current Skin -unified_inventory.register_page("u_skins", { - get_formspec = function(player) - local name = player:get_player_name() - if not u_skins.is_skin(u_skins.u_skins[name]) then - u_skins.u_skins[name] = u_skins.default - end - - local formspec = ("background[0.06,0.99;7.92,7.52;ui_misc_form.png]" - .."image[0,.75;1,2;"..u_skins.u_skins[name].."_preview.png]" - .."label[6,.5;"..S("Raw texture")..":]" - .."image[6,1;2,1;"..u_skins.u_skins[name]..".png]") - - local meta = u_skins.meta[u_skins.u_skins[name]] - if meta then - if meta.name ~= "" then - formspec = formspec.."label[2,.5;"..S("Name")..": "..minetest.formspec_escape(meta.name).."]" - end - if meta.author ~= "" then - formspec = formspec.."label[2,1;"..S("Author")..": "..minetest.formspec_escape(meta.author).."]" - end - if meta.license ~= "" then - formspec = formspec.."label[2,1.5;"..S("License")..": "..minetest.formspec_escape(meta.license).."]" - end - if meta.description ~= "" then --what's that?? - formspec = formspec.."label[2,2;"..S("Description")..": "..minetest.formspec_escape(meta.description).."]" - end - end - local page = 0 - if u_skins.pages[name] then - page = u_skins.pages[name] - end - formspec = formspec .. "button[.75,3;6.5,.5;u_skins_page$"..page..";"..S("Change").."]" - return {formspec=formspec} - end, -}) - -unified_inventory.register_button("u_skins", { - type = "image", - image = "u_skins_button.png", -}) - --- Create all of the skin-picker pages. - -u_skins.generate_pages = function(texture) - local page = 0 - local pages = {} - for i, skin in ipairs(u_skins.list) do - local p_index = (i - 1) % 16 - if p_index == 0 then - page = page + 1 - pages[page] = {} - end - pages[page][p_index + 1] = {i, skin} - end - local total_pages = page - page = 1 - for page, arr in ipairs(pages) do - local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]" - local y = -0.1 - for i, skin in ipairs(arr) do - local x = (i - 1) % 8 - if i > 1 and x == 0 then - y = 1.8 - end - formspec = (formspec.."image_button["..x..","..y..";1,2;".. - skin[2].."_preview.png;u_skins_set$"..skin[1]..";]".. - "tooltip[u_skins_set$"..skin[1]..";"..u_skins.meta[skin[2]].name.."]") - end - local page_prev = page - 2 - local page_next = page - if page_prev < 0 then - page_prev = total_pages - 1 - end - if page_next >= total_pages then - page_next = 0 - end - formspec = (formspec - .."button[0,3.8;1,.5;u_skins_page$"..page_prev..";<<]" - .."button[.75,3.8;6.5,.5;u_skins_null;"..S("Page").." "..page.."/"..total_pages.."]" - .."button[7,3.8;1,.5;u_skins_page$"..page_next..";>>]") - - unified_inventory.register_page("u_skins_page$"..(page - 1), { - get_formspec = function(player) - return {formspec=formspec} - end - }) - end -end - --- click button handlers -minetest.register_on_player_receive_fields(function(player, formname, fields) - if fields.u_skins then - unified_inventory.set_inventory_formspec(player, "craft") - return - end - for field, _ in pairs(fields) do - local current = string.split(field, "$", 2) - if current[1] == "u_skins_set" then - u_skins.u_skins[player:get_player_name()] = u_skins.list[tonumber(current[2])] - u_skins.update_player_skin(player) - u_skins.file_save = true - unified_inventory.set_inventory_formspec(player, "u_skins") - elseif current[1] == "u_skins_page" then - u_skins.pages[player:get_player_name()] = current[2] - unified_inventory.set_inventory_formspec(player, "u_skins_page$"..current[2]) - end - end -end) - --- Change skin on join - reset if invalid -minetest.register_on_joinplayer(function(player) - local player_name = player:get_player_name() - if not u_skins.is_skin(u_skins.u_skins[player_name]) then - u_skins.u_skins[player_name] = u_skins.default - end - u_skins.update_player_skin(player) -end) - -u_skins.generate_pages() -u_skins.load_players() diff --git a/u_skins/locale/de.txt b/u_skins/locale/de.txt deleted file mode 100644 index 6ad87f3..0000000 --- a/u_skins/locale/de.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Translation by Xanthin - -Raw texture = Rohtextur -Name = Name -Author = Autor -Change = Wechseln -Page = Seite -License = Lizenz -Description = Beschreibung diff --git a/u_skins/locale/template.txt b/u_skins/locale/template.txt deleted file mode 100644 index 6412b7f..0000000 --- a/u_skins/locale/template.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Template - -Raw texture = -Name = -Author = -Change = -Page = -License = -Description = diff --git a/u_skins/meta/character_1.txt b/u_skins/meta/character_1.txt deleted file mode 100644 index 7211029..0000000 --- a/u_skins/meta/character_1.txt +++ /dev/null @@ -1,3 +0,0 @@ -Sam 0 -Jordach -CC BY-SA 3.0 \ No newline at end of file diff --git a/u_skins/meta/character_2.txt b/u_skins/meta/character_2.txt deleted file mode 100644 index 0187cab..0000000 --- a/u_skins/meta/character_2.txt +++ /dev/null @@ -1,3 +0,0 @@ -Sam I -Jordach -CC BY-SA 3.0 \ No newline at end of file diff --git a/u_skins/meta/placeholder.txt b/u_skins/meta/placeholder.txt deleted file mode 100644 index a9e6fce..0000000 --- a/u_skins/meta/placeholder.txt +++ /dev/null @@ -1 +0,0 @@ -Please run the update_from_db.py script to update the skins. diff --git a/u_skins/players.lua b/u_skins/players.lua deleted file mode 100644 index 3053530..0000000 --- a/u_skins/players.lua +++ /dev/null @@ -1,40 +0,0 @@ -u_skins.load_players = function() - local file = io.open(u_skins.file, "r") - if file then - for line in file:lines() do - local data = string.split(line, " ", 2) - u_skins.u_skins[data[1]] = data[2] - end - io.close(file) - end -end -u_skins.load_players() - -local ttime = 0 -minetest.register_globalstep(function(t) - ttime = ttime + t - if ttime < 360 then --every 6min' - return - end - ttime = 0 - u_skins.save() -end) - -minetest.register_on_shutdown(function() u_skins.save() end) - -u_skins.save = function() - if not u_skins.file_save then - return - end - u_skins.file_save = false - local output = io.open(u_skins.file, "w") - for name, skin in pairs(u_skins.u_skins) do - if name and skin then - if skin ~= u_skins.default then - output:write(name.." "..skin.."\n") - end - end - end - io.close(output) -end - diff --git a/u_skins/skinlist.lua b/u_skins/skinlist.lua deleted file mode 100644 index 718646c..0000000 --- a/u_skins/skinlist.lua +++ /dev/null @@ -1,26 +0,0 @@ -u_skins.list = {} -u_skins.meta = {} - -local id = 1 -local internal_id = 1 -local fetched_skip = 0 -while fetched_skip < 40 do - local name = "character_"..id - local file = io.open(u_skins.modpath.."/meta/"..name..".txt", "r") - if file then - local data = string.split(file:read("*all"), "\n", 3) - file:close() - - u_skins.list[internal_id] = name - u_skins.meta[name] = {} - u_skins.meta[name].name = data[1] - u_skins.meta[name].author = data[2] - u_skins.meta[name].license = data[3] - u_skins.meta[name].description = "" --what's that?? - - fetched_skip = 0 - internal_id = internal_id + 1 - end - fetched_skip = fetched_skip + 1 - id = id + 1 -end \ No newline at end of file diff --git a/u_skins/textures/character_1.png b/u_skins/textures/character_1.png deleted file mode 100644 index 8d0dd99..0000000 Binary files a/u_skins/textures/character_1.png and /dev/null differ diff --git a/u_skins/textures/character_1_preview.png b/u_skins/textures/character_1_preview.png deleted file mode 100644 index 62a9ff2..0000000 Binary files a/u_skins/textures/character_1_preview.png and /dev/null differ diff --git a/u_skins/textures/character_2.png b/u_skins/textures/character_2.png deleted file mode 100644 index d794b87..0000000 Binary files a/u_skins/textures/character_2.png and /dev/null differ diff --git a/u_skins/textures/character_2_preview.png b/u_skins/textures/character_2_preview.png deleted file mode 100644 index 0736def..0000000 Binary files a/u_skins/textures/character_2_preview.png and /dev/null differ diff --git a/u_skins/textures/inventory_plus_skins.png b/u_skins/textures/inventory_plus_skins.png deleted file mode 100644 index 781b42f..0000000 Binary files a/u_skins/textures/inventory_plus_skins.png and /dev/null differ diff --git a/u_skins/textures/u_skins_button.png b/u_skins/textures/u_skins_button.png deleted file mode 100644 index cb49531..0000000 Binary files a/u_skins/textures/u_skins_button.png and /dev/null differ diff --git a/u_skins/textures/ui_misc_form.png b/u_skins/textures/ui_misc_form.png deleted file mode 100644 index d34d326..0000000 Binary files a/u_skins/textures/ui_misc_form.png and /dev/null differ diff --git a/update_from_db.py b/update_from_db.py deleted file mode 100755 index 159acf7..0000000 --- a/update_from_db.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python3 -from http.client import HTTPConnection,HTTPException,BadStatusLine,_CS_IDLE -import json -import base64 -from contextlib import closing -import sys,os,shutil,time - -def die(message,code=23): - print(message,file=sys.stderr) - raise SystemExit(code) - -server = "minetest.fensta.bplaced.net" -skinsdir = "u_skins/textures/" -metadir = "u_skins/meta/" -curskin = 0 -curpage = 1 -pages = None - -def replace(location,base,encoding=None,path=None): - if path is None: - path = os.path.join(location,base) - mode = "wt" if encoding else "wb" - # an unpredictable temp name only needed for a+rwxt directories - tmp = os.path.join(location,'.'+base+'-tmp') - def deco(handle): - with open(tmp,mode,encoding=encoding) as out: - handle(out) - os.rename(tmp,path) - return deco - -def maybeReplace(location,base,encoding=None): - def deco(handle): - path = os.path.join(location,base) - if os.path.exists(path): return - return replace(location,base,encoding=encoding,path=path)(handle) - return deco - -class Penguin: - "idk" - def __init__(self, url, recv, diemessage): - self.url = url - self.recv = recv - self.diemessage = diemessage - -class Pipeline(list): - "Gawd why am I being so elaborate?" - def __init__(self, threshold=10): - "threshold is how many requests in parallel to pipeline" - self.threshold = threshold - self.sent = True - def __enter__(self): - self.reopen() - return self - def __exit__(self,typ,exn,trace): - self.send() - self.drain() - def reopen(self): - self.c = HTTPConnection(server) - self.send() - def append(self,url,recv,diemessage): - self.sent = False - super().append(Penguin(url,recv,diemessage)) - if len(self) > self.threshold: - self.send() - self.drain() - def trydrain(self): - for penguin in self: - print('drain',penguin.url) - try: - penguin.response.begin() - penguin.recv(penguin.response) - except BadStatusLine as e: - print('derped requesting',penguin.url) - return False - except HTTPException as e: - die(penguin.diemessage+' '+repr(e)+' (url='+penguin.url+')') - self.clear() - return True - def drain(self): - print('draining pipeline...',len(self)) - assert self.sent, "Can't drain without sending the requests!" - self.sent = False - while self.trydrain() is not True: - self.c.close() - print('drain failed, trying again') - time.sleep(1) - self.reopen() - def trysend(self): - for penguin in pipeline: - print('fill',penguin.url) - try: - self.c.request("GET", penguin.url) - self.c._HTTPConnection__state = _CS_IDLE - penguin.response = self.c.response_class(self.c.sock, - method="GET") - # begin LATER so we can send multiple requests w/out response headers - except BadStatusLine: - return False - except HTTPException as e: - die(diemessage+' because of a '+repr(e)) - return True - def send(self): - if self.sent: return - print('filling pipeline...',len(self)) - while self.trysend() is not True: - self.c.close() - print('derped resending') - time.sleep(1) - self.reopen() - self.sent = True - -with Pipeline() as pipeline: - # two connections is okay, right? one for json, one for preview images - c = HTTPConnection(server) - def addpage(page): - global curskin, pages - print("Page: " + str(page)) - r = 0 - try: - c.request("GET", "/api/get.json.php?getlist&page=" + str(page) + "&outformat=base64") - r = c.getresponse() - except Exception: - if r != 0: - if r.status != 200: - die("Error", r.status) - return - - data = r.read().decode() - l = json.loads(data) - if not l["success"]: - die("Success != True") - r = 0 - pages = int(l["pages"]) - foundOne = False - for s in l["skins"]: - # make sure to increment this, even if the preview exists! - curskin = curskin + 1 - previewbase = "character_" + str(curskin) + "_preview.png" - preview = os.path.join(skinsdir, previewbase) - if os.path.exists(preview): - print('skin',curskin,'already retrieved') - continue - print('updating skin',curskin,'id',s["id"]) - foundOne = True - @maybeReplace(skinsdir, "character_" + str(curskin) + ".png") - def go(f): - f.write(base64.b64decode(bytes(s["img"], 'utf-8'))) - f.close() - - @maybeReplace(metadir, "character_" + str(curskin) + ".txt", - encoding='utf-8') - def go(f): - f.write(str(s["name"]) + '\n') - f.write(str(s["author"]) + '\n') - f.write(str(s["license"])) - url = "/skins/1/" + str(s["id"]) + ".png" - def closure(skinsdir,previewbase,preview,s): - "explanation: python sucks" - def tryget(r): - print('replacing',s["id"]) - if r.status != 200: - print("Error", r.status) - return - @replace(skinsdir,previewbase,path=preview) - def go(f): - shutil.copyfileobj(r,f) - return tryget - - pipeline.append(url,closure(skinsdir,previewbase,preview,s), - "Couldn't get {} because of a".format( - s["id"])) - if not foundOne: - print("No skins updated on this page. Seems we're done?") - #raise SystemExit - addpage(curpage) - while pages > curpage: - curpage = curpage + 1 - addpage(curpage) - print("Skins have been updated!") - diff --git a/update_skins_db.sh b/update_skins_db.sh deleted file mode 100644 index 8bf9e1a..0000000 --- a/update_skins_db.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/bash -#### -# Licenced under Attribution-NonCommercial-ShareAlike 4.0 International -# http://creativecommons.org/licenses/by-nc-sa/4.0/ -#### ATTENTION #### -## This script requires that jq and coreutils are installed on your system ## -## In Debian-based distros, open a terminal and run -## sudo apt-get install jq coreutils -################### - -# == Set variables === -# ==================== -NUMPAGES="1" # Number of pages. Default is 1 page -PERPAGE="2000" # Number of items per page. Default is 2000. -JSONURL="http://minetest.fensta.bplaced.net/api/get.json.php?getlist&page=$NUMPAGES&outformat=base64&per_page=$PERPAGE" # The URL to the database -PREVIEWURL="http://minetest.fensta.bplaced.net/skins/1/" # The url to the location of the previews. -temp=$PWD/tmp # Where the temp folder will be. Default is $PWD/tmp, which means that the tmp folder will be put in the current folder -METADEST=$PWD/u_skins/meta # This is the folder where the meta data will be saved -TEXTUREDEST=$PWD/u_skins/textures # This is the folder where the skins and the previews will be saved - - -# === Make a bunch of folders and download the db === -# =================================================== -if [ -d "$temp" ]; then - rm -r $temp # If the temp dir exists we will remove it and its contents. -fi -mkdir $temp # Make a new temp dir. Redundant? No. We will get rid of it later. - -if [ ! -d "$METADEST" ]; then # Check to see if the meta dir exists, and if not, create it - mkdir $METADEST -fi - -if [ ! -d "$TEXTUREDEST" ]; then # Check to see if the textures dir exists, and if not, create it - mkdir $TEXTUREDEST -fi - -wget $JSONURL -O $temp/rawdb.txt # Download the entire database - - -# === Do the JSON thing === -# ========================= -i="0" # This will be the counter. -while [ "$ID" != "null" ] # Repeat for as long as there is data to process - do - ID=$(cat $temp/rawdb.txt | jq ".skins[$i].id") - - # The next lines are kinda complex. sed is being used to strip the quotes from the variables. I had help... - meta_name=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].name") | sed -e 's/^"//' -e 's/"$//') - meta_author=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].author") | sed -e 's/^"//' -e 's/"$//') - meta_license=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].license") | sed -e 's/^"//' -e 's/"$//') - - if [[ "$ID" != "null" ]]; then # Check to see if ID has a value - echo "#"$ID "name:" $meta_name "author:" $meta_author "license:" $meta_license # Verbosity to show that the script is working. - - echo $meta_name > $METADEST/character_$ID.txt # Save the meta data to files, this line overwrites the data inside the file - echo $meta_author >> $METADEST/character_$ID.txt # Save the meta data to files, this line is added to the file - echo $meta_license >> $METADEST/character_$ID.txt # Save the meta data to files, and this line is added to the file as well. - - - # === Extract and save the image from the JSON file === - # ====================================================== - skin=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].img") | sed -e 's/^"//' -e 's/"$//') # Strip the quotes from the base64 encoded string - echo $skin | base64 --decode > $TEXTUREDEST"/character_"$ID".png" # Decode the string, and save it as a .png file - - - # === Download a preview image whilst we're at it === - # ==================================================== - wget -nv $PREVIEWURL/$ID".png" -O $TEXTUREDEST"/character_"$ID"_preview.png" # Downloads a preview of the skin that we just saved. - - fi - i=$[$i+1] # Increase the counter by one. - done - -# === Now we'll clean up the mess === -# =================================== -rm -r $temp # Remove the temp dir and its contents. - -exit # Not strictly needed, but i like to use it to wrap things up. diff --git a/updater/MT_skins_updater.cs b/updater/MT_skins_updater.cs new file mode 100644 index 0000000..6d27da0 --- /dev/null +++ b/updater/MT_skins_updater.cs @@ -0,0 +1,200 @@ +using System; +//Json.NET library (http://json.codeplex.com/) +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Net; +using System.IO; + +// MT skins updater for the u_skins mod +// Creator: Krock +// License: zlib (http://www.zlib.net/zlib_license.html) +namespace MT_skins_updater { + class Program { + static void Main(string[] args) { + Console.WriteLine("Welcome to the MT skins updater!"); + Console.WriteLine("# Created by: Krock (2014-07-10)"); + Engine e = new Engine(); + Console.WriteLine(@"Path to the u_skins mod: (ex. 'E:\Minetest\mods\u_skinsdb\u_skins\')"); + string path = Console.ReadLine(); + Console.WriteLine("Start updating at page: ('0' to update everything)"); + int page = getInt(Console.ReadLine()); + e.Start(path, page); + Console.WriteLine("Press any key to exit."); + Console.ReadKey(false); + } + public static int getInt(string i) { + int ret = 0; + int.TryParse(i, out ret); + return (ret > 0)? ret : 0; + } + } + class Engine { + string root = "http://minetest.fensta.bplaced.net"; + bool alternate = true; //should it use the special version of medadata saving? + + public void Start(string path, int page) { + if (path.Length < 5) { + Console.WriteLine("Too short path. STOP."); + return; + } + if (path[path.Length - 1] != '\\') { + path += '\\'; + } + if(!Directory.Exists(path + "meta")){ + Console.WriteLine("Folder 'meta' not found. STOP."); + return; + } + if(!Directory.Exists(path + "textures")){ + Console.WriteLine("Folder 'textures' not found. STOP."); + return; + } + WebClient cli = new WebClient(); + //add useragent to identify + cli.Headers.Add("User-Agent", "MT_skin_grabber 1.1"); + + bool firstSkin = true; + List skin_local = new List(); + int pages = page, + updated = 0; + + for (; page <= pages; page++) { + string contents = ""; + try { + contents = cli.DownloadString(root + "/api/get.json.php?getlist&page=" + page); + } catch(WebException e) { + Console.WriteLine("Whoops! Error at page ID: " + page + ". WebClient sais: " + e.Message); + Console.WriteLine("Press any key to skip this page."); + Console.ReadKey(false); + continue; + } + Data o = JsonConvert.DeserializeObject(contents); + if (o.pages != pages) { + pages = o.pages; + } + + Console.WriteLine("# Page " + page + " (" + o.per_page + " skins)"); + for (int i = 0; i < o.skins.Length; i++) { + int id = o.skins[i].id; + if(o.skins[i].type != "image/png"){ + Console.WriteLine("Image type '" + o.skins[i].type + "' not supported at skin ID: " + id); + Console.WriteLine("Press any key to continue."); + Console.ReadKey(false); + continue; + } + //eliminate special chars! + o.skins[i].name = WebUtility.HtmlDecode(o.skins[i].name); + o.skins[i].author = WebUtility.HtmlDecode(o.skins[i].author); + + //to delete old, removed skins + if (firstSkin) { + firstSkin = false; + + string[] files = Directory.GetFiles(path + "textures\\"); + for (int f = 0; f < files.Length; f++) { + string[] filePath = stringSplitLast(files[f], '\\'), + fileName = stringSplitLast(filePath[1], '.'), + fileVer = stringSplitLast(fileName[0], '_'); + if (fileVer[1] == "" || fileVer[0] != "character") continue; + + int skinNr = Program.getInt(fileVer[1]); + if (skinNr <= id) continue; + skin_local.Add(fileName[0]); + } + } else skin_local.Remove("character_" + id); + + //get file size, only override changed + FileInfo localImg = new FileInfo(path + "textures\\character_" + id + ".png"); + byte[] imageData = Convert.FromBase64String(o.skins[i].img); + bool isDif = true; + if (localImg.Exists) isDif = (Math.Abs(imageData.Length - localImg.Length) >= 3); + + if (isDif) { + File.WriteAllBytes(localImg.FullName, imageData); + imageData = null; + //previews + try { + cli.DownloadFile(root + "/skins/1/" + id + ".png", path + "textures\\character_" + id + "_preview.png"); + } catch (WebException e) { + Console.WriteLine("Whoops! Error at skin ID: " + id + ". WebClient sais: " + e.Message); + Console.WriteLine("Press any key to continue."); + Console.ReadKey(false); + } + } else { + Console.WriteLine("[SKIP] character_" + id); + continue; + } + + string meta = ""; + if (!alternate) { + meta = "name = \"" + o.skins[i].name + "\",\n"; + meta += "author = \"" + o.skins[i].author + "\",\n"; + meta += "comment = \"" + o.skins[i].license + '"'; + } else { + meta = o.skins[i].name + '\n' + o.skins[i].author + '\n' + o.skins[i].license; + } + File.WriteAllText(path + "meta\\character_" + id + ".txt", meta); + updated++; + Console.WriteLine("[" + id + "] " + shorten(o.skins[i].name, 20) + "\t by: " + o.skins[i].author + "\t (" + o.skins[i].license + ")"); + } + } + foreach (string fileName in skin_local) { + if(File.Exists(path + "textures\\" + fileName + ".png")) { + File.Delete(path + "textures\\" + fileName + ".png"); + } + if(File.Exists(path + "textures\\" + fileName + "_preview.png")) { + File.Delete(path + "textures\\" + fileName + "_preview.png"); + } + if(File.Exists(path + "meta\\" + fileName + ".txt")) { + File.Delete(path + "meta\\" + fileName + ".txt"); + } + Console.WriteLine("[DEL] " + fileName + " (deleted skin)"); + } + Console.WriteLine("Done. Updated " + updated + " skins!"); + } + string shorten(string inp, int len) { + char[] shr = new char[len]; + for (int i = 0; i < len; i++) { + if (i < inp.Length) { + shr[i] = inp[i]; + } else shr[i] = ' '; + } + return new string(shr); + } + + string[] stringSplitLast(string path, char limiter) { + int found = 0; + int totalLen = path.Length - 1; + for (int i = totalLen; i >= 0; i--) { + if (path[i] == limiter) { + found = i; + break; + } + } + if (found == 0) { + return new string[] { "", "" }; + } + + int len = totalLen - found; + char[] str_1 = new char[found], + str_2 = new char[len]; + + for (int i = 0; i < path.Length; i++) { + if (i == found) continue; + if (i < found) { + str_1[i] = path[i]; + } else { + str_2[i - found - 1] = path[i]; + } + } + return new string[] { new string(str_1), new string(str_2) }; + } + } + class Data { + public Skins_data[] skins; + public int page, pages, per_page; + } + class Skins_data { + public string name, author, uploaded, type, license, img; + public int id, license_id; + } +} diff --git a/updater/MT_skins_updater.exe b/updater/MT_skins_updater.exe new file mode 100644 index 0000000..5b4ee3e Binary files /dev/null and b/updater/MT_skins_updater.exe differ diff --git a/updater/Newtonsoft.Json.dll b/updater/Newtonsoft.Json.dll new file mode 100644 index 0000000..054c933 Binary files /dev/null and b/updater/Newtonsoft.Json.dll differ diff --git a/updater/update_from_db.py b/updater/update_from_db.py new file mode 100755 index 0000000..685d1e0 --- /dev/null +++ b/updater/update_from_db.py @@ -0,0 +1,180 @@ +#!/usr/bin/python3 +from http.client import HTTPConnection,HTTPException,BadStatusLine,_CS_IDLE +import json +import base64 +from contextlib import closing +import sys,os,shutil,time + +def die(message,code=23): + print(message,file=sys.stderr) + raise SystemExit(code) + +server = "minetest.fensta.bplaced.net" +skinsdir = "../textures/" +metadir = "../meta/" +curskin = 0 +curpage = 1 +pages = None + +def replace(location,base,encoding=None,path=None): + if path is None: + path = os.path.join(location,base) + mode = "wt" if encoding else "wb" + # an unpredictable temp name only needed for a+rwxt directories + tmp = os.path.join(location,'.'+base+'-tmp') + def deco(handle): + with open(tmp,mode,encoding=encoding) as out: + handle(out) + os.rename(tmp,path) + return deco + +def maybeReplace(location,base,encoding=None): + def deco(handle): + path = os.path.join(location,base) + if os.path.exists(path): return + return replace(location,base,encoding=encoding,path=path)(handle) + return deco + +class Penguin: + "idk" + def __init__(self, url, recv, diemessage): + self.url = url + self.recv = recv + self.diemessage = diemessage + +class Pipeline(list): + "Gawd why am I being so elaborate?" + def __init__(self, threshold=10): + "threshold is how many requests in parallel to pipeline" + self.threshold = threshold + self.sent = True + def __enter__(self): + self.reopen() + return self + def __exit__(self,typ,exn,trace): + self.send() + self.drain() + def reopen(self): + self.c = HTTPConnection(server) + self.send() + def append(self,url,recv,diemessage): + self.sent = False + super().append(Penguin(url,recv,diemessage)) + if len(self) > self.threshold: + self.send() + self.drain() + def trydrain(self): + for penguin in self: + print('drain',penguin.url) + try: + penguin.response.begin() + penguin.recv(penguin.response) + except BadStatusLine as e: + print('derped requesting',penguin.url) + return False + except HTTPException as e: + die(penguin.diemessage+' '+repr(e)+' (url='+penguin.url+')') + self.clear() + return True + def drain(self): + print('draining pipeline...',len(self)) + assert self.sent, "Can't drain without sending the requests!" + self.sent = False + while self.trydrain() is not True: + self.c.close() + print('drain failed, trying again') + time.sleep(1) + self.reopen() + def trysend(self): + for penguin in pipeline: + print('fill',penguin.url) + try: + self.c.request("GET", penguin.url) + self.c._HTTPConnection__state = _CS_IDLE + penguin.response = self.c.response_class(self.c.sock, + method="GET") + # begin LATER so we can send multiple requests w/out response headers + except BadStatusLine: + return False + except HTTPException as e: + die(diemessage+' because of a '+repr(e)) + return True + def send(self): + if self.sent: return + print('filling pipeline...',len(self)) + while self.trysend() is not True: + self.c.close() + print('derped resending') + time.sleep(1) + self.reopen() + self.sent = True + +with Pipeline() as pipeline: + # two connections is okay, right? one for json, one for preview images + c = HTTPConnection(server) + def addpage(page): + global curskin, pages + print("Page: " + str(page)) + r = 0 + try: + c.request("GET", "/api/get.json.php?getlist&page=" + str(page) + "&outformat=base64") + r = c.getresponse() + except Exception: + if r != 0: + if r.status != 200: + die("Error", r.status) + return + + data = r.read().decode() + l = json.loads(data) + if not l["success"]: + die("Success != True") + r = 0 + pages = int(l["pages"]) + foundOne = False + for s in l["skins"]: + # make sure to increment this, even if the preview exists! + curskin = curskin + 1 + previewbase = "character_" + str(curskin) + "_preview.png" + preview = os.path.join(skinsdir, previewbase) + if os.path.exists(preview): + print('skin',curskin,'already retrieved') + continue + print('updating skin',curskin,'id',s["id"]) + foundOne = True + @maybeReplace(skinsdir, "character_" + str(curskin) + ".png") + def go(f): + f.write(base64.b64decode(bytes(s["img"], 'utf-8'))) + f.close() + + @maybeReplace(metadir, "character_" + str(curskin) + ".txt", + encoding='utf-8') + def go(f): + f.write(str(s["name"]) + '\n') + f.write(str(s["author"]) + '\n') + f.write(str(s["license"])) + url = "/skins/1/" + str(s["id"]) + ".png" + def closure(skinsdir,previewbase,preview,s): + "explanation: python sucks" + def tryget(r): + print('replacing',s["id"]) + if r.status != 200: + print("Error", r.status) + return + @replace(skinsdir,previewbase,path=preview) + def go(f): + shutil.copyfileobj(r,f) + return tryget + + pipeline.append(url,closure(skinsdir,previewbase,preview,s), + "Couldn't get {} because of a".format( + s["id"])) + if not foundOne: + print("No skins updated on this page. Seems we're done?") + #raise SystemExit + addpage(curpage) + while pages > curpage: + curpage = curpage + 1 + addpage(curpage) + print("Skins have been updated!") + diff --git a/updater/update_skins_db.sh b/updater/update_skins_db.sh new file mode 100755 index 0000000..8028b42 --- /dev/null +++ b/updater/update_skins_db.sh @@ -0,0 +1,79 @@ +#!/bin/bash +#### +# Licenced under Attribution-NonCommercial-ShareAlike 4.0 International +# http://creativecommons.org/licenses/by-nc-sa/4.0/ +#### ATTENTION #### +## This script requires that jq and coreutils are installed on your system ## +## In Debian-based distros, open a terminal and run +## sudo apt-get install jq coreutils +################### + +# == Set variables === +# ==================== +NUMPAGES="1" # Number of pages. Default is 1 page +PERPAGE="2000" # Number of items per page. Default is 2000. +JSONURL="http://minetest.fensta.bplaced.net/api/get.json.php?getlist&page=$NUMPAGES&outformat=base64&per_page=$PERPAGE" # The URL to the database +PREVIEWURL="http://minetest.fensta.bplaced.net/skins/1/" # The url to the location of the previews. +curpath="$(dirname $0)" # all path are relative to this script place +temp="$curpath"/tmp # Where the temp folder will be. Default is $PWD/tmp, which means that the tmp folder will be put in the current folder +METADEST="$curpath"/../meta # This is the folder where the meta data will be saved +TEXTUREDEST="$curpath"/../textures # This is the folder where the skins and the previews will be saved + + +# === Make a bunch of folders and download the db === +# =================================================== +if [ -d "$temp" ]; then + rm -r $temp # If the temp dir exists we will remove it and its contents. +fi +mkdir $temp # Make a new temp dir. Redundant? No. We will get rid of it later. + +if [ ! -d "$METADEST" ]; then # Check to see if the meta dir exists, and if not, create it + mkdir $METADEST +fi + +if [ ! -d "$TEXTUREDEST" ]; then # Check to see if the textures dir exists, and if not, create it + mkdir $TEXTUREDEST +fi + +wget $JSONURL -O $temp/rawdb.txt # Download the entire database + + +# === Do the JSON thing === +# ========================= +i="0" # This will be the counter. +while [ "$ID" != "null" ] # Repeat for as long as there is data to process + do + ID=$(cat $temp/rawdb.txt | jq ".skins[$i].id") + + # The next lines are kinda complex. sed is being used to strip the quotes from the variables. I had help... + meta_name=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].name") | sed -e 's/^"//' -e 's/"$//') + meta_author=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].author") | sed -e 's/^"//' -e 's/"$//') + meta_license=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].license") | sed -e 's/^"//' -e 's/"$//') + + if [[ "$ID" != "null" ]]; then # Check to see if ID has a value + echo "#"$ID "name:" $meta_name "author:" $meta_author "license:" $meta_license # Verbosity to show that the script is working. + + echo $meta_name > $METADEST/character_$ID.txt # Save the meta data to files, this line overwrites the data inside the file + echo $meta_author >> $METADEST/character_$ID.txt # Save the meta data to files, this line is added to the file + echo $meta_license >> $METADEST/character_$ID.txt # Save the meta data to files, and this line is added to the file as well. + + + # === Extract and save the image from the JSON file === + # ====================================================== + skin=$(echo $(cat $temp/rawdb.txt | jq ".skins[$i].img") | sed -e 's/^"//' -e 's/"$//') # Strip the quotes from the base64 encoded string + echo $skin | base64 --decode > $TEXTUREDEST"/character_"$ID".png" # Decode the string, and save it as a .png file + + + # === Download a preview image whilst we're at it === + # ==================================================== + wget -nv $PREVIEWURL/$ID".png" -O $TEXTUREDEST"/character_"$ID"_preview.png" # Downloads a preview of the skin that we just saved. + + fi + i=$[$i+1] # Increase the counter by one. + done + +# === Now we'll clean up the mess === +# =================================== +rm -r $temp # Remove the temp dir and its contents. + +exit # Not strictly needed, but i like to use it to wrap things up. -- cgit v1.2.3