Compare commits

..

10 Commits

Author SHA1 Message Date
SmallJoker
3cf80c9272 skinlist: Allow textures containing '-' characters
Some checks failed
build / lint (push) Has been cancelled
Fixes issue #110
Thanks to Bastrabun for the code suggestion
2024-09-28 11:12:31 +02:00
SmallJoker
df62f2042d skinlist: avoid 'Too many open files' errors
Due to application-specific file descriptor limits,
the garbage collector cannot close the descriptors in time,
resulting in unexpected file open errors.
2024-07-22 18:41:15 +02:00
SmallJoker
b7cd514cea
API: make skins.register_skin public (#106) 2024-07-07 18:51:48 +02:00
SmallJoker
11bb5bad0e
Add migration code to player skins using '.' delimiters (#105)
Previously, the players would have their selected skin reset after renaming
the skin textures to the dot separator. This commit implements skin name
migration to ease the transition for server owners.

See 'skins.__fuzzy_match_skin_name' for a detailed explanation.
2024-06-15 15:16:52 +02:00
SmallJoker
71f803e2fb Fix private skin variations not showing up
'player.playername.43.png' skins were not recognized
2024-06-10 18:30:25 +02:00
SmallJoker
312780c82e
Clean up skin listing (#100)
Supersedes the 'fsep' setting by automatically detecting
the texture name in both, the skin list and the updater
scripts.
New, automatically fetched skins will now always use the
'.' delimiter to avoid player name issues.
In case of ambiguous texture names, a warning is logged.
2024-06-05 17:55:55 +02:00
Jaidyn Ann
cd27e24b6f Added Esperanto (eo) translation 2024-01-06 12:26:52 +01:00
Jaidyn Ann
58739e9f9f Make command parameters translatable 2024-01-06 12:26:52 +01:00
Andrey Andreyevich Bienkowski
e1cd937470
Fix typo privat -> private (#94) 2023-12-21 17:08:17 +01:00
Andrey Andreyevich Bienkowski
616262de4b
Update instructions for downloading skins (#93)
Corrected URL and updater instructions.
2023-12-17 10:05:53 +01:00
21 changed files with 410 additions and 218 deletions

27
API.md
View File

@ -27,6 +27,31 @@ Get all allowed skins for player. All public and all player's private skins. If
Get all skins with metadata key is set to value. Example:
skins.get_skinlist_with_meta("playername", playername) - Get all private skins (w.o. public) for playername
## skins.register_skin(path, filename)
Registers a new skin based on the texture file path specified by `path` and `filename`.
* `path` (string): points to the parent directory of the texture `filename`.
Generally, this should be in the format `mymod.modpath .. "/textures"`.
* `filename` (string): full file name, without any path specifications.
The file name must adhere to [one of the accepted naming formats](textures/readme.txt).
Note: this function takes the following files into consideration:
1. `<path>/<filename>` (required)
* Main skin texture
2. `<path>/<filenamestem><separator>preview.png` (optional)
* Pre-generated preview image
3. `<path>/../meta/<filenamestem>.txt` (optional)
* Metadata regarding the skin
Return values:
* On failure: `false, reason`
* `reason` (string): human readable reason string (similar to `io.open` errors)
* On success: `true, key`
* `key`: unique skins key for use with e.g. `skins.get(key)` for subsequent
fine-tuning of the skin registration.
## skins.new(key, object)
Create and register a new skin object for given key
@ -91,7 +116,7 @@ The next metadata keys are filled or/and used interally in skinsdb framework
- name - A name for the skin
- author - The skin author
- license - THe skin texture license
- assignment - (obsolete) is "player:playername" in case the skin is assigned to be privat for a player
- assignment - (obsolete) is "player:playername" in case the skin is assigned to be private for a player
- playername - Player assignment for private skin. Set false for skins not usable by all players (like NPC-Skins), true or nothing for all player skins
- in_inventory_list - If set to false the skin is not visible in inventory skins selection but can be still applied to the player
- _sort_id - Thi skins lists are sorted by this field for output (internal key)

View File

@ -20,15 +20,16 @@ This Minetest mod offers changeable player skins with a graphical interface for
## Installing skins
### Download from the [database](http://minetest.fensta.bplaced.net/)
### Download from the [database](https://skinsdb.terraqueststudios.net/)
#### Ingame Downloader
1) Get Minetest 5.1.0-dev-cb00632 or newer
2) Start your world
3) Run `/skinsdb_download_skins <skindb start page> <amount of pages>`
4) Wait for the Minetest server to shut down
5) Start the server again
2) In the settings menu show advanced options, find the "Developer Options" tab and add "skinsdb" to "Trusted mods" (secure.trusted_mods in minetest.conf)
3) Start your world
4) Run `/skinsdb_download_skins <skindb start page> <amount of pages>`
5) Wait for the Minetest server to shut down
6) Start the server again
You might want to run `minetest` in a Terminal/Console window to check the log output instantly.

18
api.lua
View File

@ -2,14 +2,26 @@
local storage = minetest.get_mod_storage()
function skins.get_player_skin(player)
local player_name = player:get_player_name()
local meta = player:get_meta()
if meta:get("skinsdb:skin_key") then
-- Move player data prior July 2018 to mod storage
storage:set_string(player:get_player_name(), meta:get_string("skinsdb:skin_key"))
storage:set_string(player_name, meta:get_string("skinsdb:skin_key"))
meta:set_string("skinsdb:skin_key", "")
end
local skin = storage:get_string(player:get_player_name())
return skins.get(skin) or skins.get(skins.default)
local skin_name = storage:get_string(player_name)
local skin = skins.get(skin_name)
if #skin_name > 0 and not skin then
-- Migration step to convert `_`-delimited skins to `.` (if possible)
skin = skins.__fuzzy_match_skin_name(player_name, skin_name, true)
if skin then
storage:set_string(player_name, skin:get_key())
else
storage:set_string(player_name, "")
end
end
return skin or skins.get(skins.default)
end
-- Assign skin to player

View File

@ -11,7 +11,7 @@ end
minetest.register_chatcommand("skinsdb", {
params = "[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]",
params = S("[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]"),
description = S("Show, list or set player's skin"),
func = function(name, param)
local player = minetest.get_player_by_name(name)

View File

@ -8,12 +8,6 @@ skins = {}
skins.modpath = minetest.get_modpath(minetest.get_current_modname())
skins.default = "character"
-- see skindsdb/textures/readme.txt to avoid playername with underscore problem
skins.fsep = minetest.settings:get("skinsdb_fsep") or "_"
if skins.fsep == "_" then
minetest.log("warning", "skinsdb filename seperator is set to " .. skins.fsep .. ", see skindsdb/textures/readme.txt to avoid problems with playernames containing underscore")
end
dofile(skins.modpath.."/skin_meta_api.lua")
dofile(skins.modpath.."/api.lua")
dofile(skins.modpath.."/skinlist.lua")
@ -117,3 +111,6 @@ minetest.register_allow_player_inventory_action(function(player, action, inv, da
return 0
end
end)
--dofile(skins.modpath.."/unittest.lua")

View File

@ -1,19 +1,19 @@
# textdomain: skinsdb
# Translation by Xanthin
Raw texture=Rohtextur
Name=Name
Author=Autor
Change=Wechseln
Page=Seite
License=Lizenz
Description=Beschreibung
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Anzeigen oder setzen der Spieler-Skins
Player not found=Spieler nicht da
unknown command=unbekannter Befehl
see /help skinsdb for supported parameters=Lese /help für erlaubte Parameter
skin set to=Skin ist jetzt
invalid skin=unbekannter Skin
unknown parameter=unbekannter Parameter
Skins=Aussehen
Requires skin key=Benötigt Skin-Name
unknown parameter=unbekannter Parameter
Raw texture=Rohtextur
Page=Seite
Name=Name
Author=Autor
License=Lizenz
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=
Change=Wechseln
Skins=Aussehen

19
locale/skinsdb.eo.tr Normal file
View File

@ -0,0 +1,19 @@
# textdomain: skinsdb
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=[set] <haŭtonomo> | show [<haŭtonomo>] | list | list private | list public | [ui]
Show, list or set player's skin=Montri haŭtojn («show»), listigi haŭtojn («list»), aŭ agordi onian haŭton («set»)
Player not found=Ludanto ne trovita
unknown command=nekonata ordono
see /help skinsdb for supported parameters=rulu «/help skinsdb» por vidi subtenatajn parametrojn
skin set to=haŭto agordita al
invalid skin=nevalida haŭto
Requires skin key=Postulas haŭtonomon
unknown parameter=nekonata parametro
Raw texture=Kruda bildo
Page=Paĝo
Name=Nomo
Author=Aŭtoro
License=Permesilo
<skindb start page> <amount of pages>=<komenca paĝo> <paĝokvanto>
Downloads the specified range of skins and shuts down the server=Elŝutinte haŭtojn el skindb inter la donitaj paĝoj, restartigas la servilon
Change=Ŝanĝi
Skins=Haŭtoj

View File

@ -1,18 +1,19 @@
# textdomain: skinsdb
Raw texture=Texture
Name=Nom
Author=Auteur
Change=Changer
Page=Page
License=Licence
Description=Description
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Afficher, lister ou définir le skin du joueur
Player not found=Joueur non trouvé
unknown command=commande inconnue
see /help skinsdb for supported parameters=voir /help skinsdb pour les paramètres supportés
skin set to=skin définie sur
invalid skin=skin peau invalide
Requires skin key=
unknown parameter=paramètre inconnu
unknown skin=skin inconnue
Raw texture=Texture
Page=Page
Name=Nom
Author=Auteur
License=Licence
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=
Change=Changer
Skins=

View File

@ -1,18 +1,19 @@
# textdomain: skinsdb
# Malay translation by muhdnurhidayat
Raw texture=Tekstur mentah
Name=Nama
Author=Pencipta
Change=Ubah
Page=Halaman
License=Lesen
Description=Keterangan
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Tunjukkan, senaraikan atau tetapkan kulit pemain
Player not found=Pemain tidak dijumpai
unknown command=perintah tidak diketahui
see /help skinsdb for supported parameters=lihat /help skinsdb untuk parameter yang disokong
skin set to=kulit ditetapkan kepada
invalid skin=kulit tidak sah
Requires skin key=
unknown parameter=parameter tidak diketahui
unknown skin=kulit tidak diketahui
Raw texture=Tekstur mentah
Page=Halaman
Name=Nama
Author=Pencipta
License=Lesen
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=
Change=Ubah
Skins=

View File

@ -1,20 +1,19 @@
# textdomain: skinsdb
Raw texture=Textura crua
Name=Nome
Author=Autor
Change=Mudar
Page=Página
License=Licença
Description=Descrição
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Mostrar, listar ou definir a skin do jogador
Player not found=Jogador não encontrado
unknown command=Comando desconhecido
see /help skinsdb for supported parameters= consulte /help skinsdb para obter os parâmetros suportados
skin set to=Aparência definida para
invalid skin=Aparência inválida
unknown parameter=parâmetro desconhecido
unknown skin=Aparência desconhecida
Downloads thespecified range of skins and shuts down the server= Baixa o intervalo especificado de capas e desliga o servidor
Skins=Aparência
Requires skin key=Requer chave de aparência
unknown parameter=parâmetro desconhecido
Raw texture=Textura crua
Page=Página
Name=Nome
Author=Autor
License=Licença
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=Baixa o intervalo especificado de capas e desliga o servidor
Change=Mudar
Skins=Aparência

View File

@ -1,21 +1,19 @@
# textdomain: skinsdb
# Translation by Baytuch
Raw texture=Текстура
Name=Имя
Author=Автор
Change=Изменить
Page=Страница
License=Лицензия
Description=Описание
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Показать скин, список скинов, установить скин игрока
Player not found=Игрок не найден
unknown command=неизвестная команда
see /help skinsdb for supported parameters=смотрите /help skinsdb для просмотра параметров
skin set to=установлено скин
invalid skin=некорректный скин
unknown parameter=неопределенный параметр
unknown skin=неопределенный скин
Downloads the specified range of skins and shuts down the server=Загрузить массив скинов и остановить сервер
Skins=Скины
Requires skin key=Зависимый идентификатор скина
unknown parameter=неопределенный параметр
Raw texture=Текстура
Page=Страница
Name=Имя
Author=Автор
License=Лицензия
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=Загрузить массив скинов и остановить сервер
Change=Изменить
Skins=Скины

View File

@ -1,21 +1,19 @@
# textdomain: skinsdb
# Translation by Baytuch
Raw texture=Текстура
Name=Ім'я
Author=Автор
Change=Змінити
Page=Сторінка
License=Ліцензія
Description=Опис
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=Показати скін, список скінів, встановити скін гравця
Player not found=Гравець не знайдений
unknown command=невідома команда
see /help skinsdb for supported parameters=дивіться /help skinsdb для перегляду параметрів
skin set to=встановлено скін
invalid skin=некоректний скін
unknown parameter=невизначений параметр
unknown skin=невизначений скін
Downloads the specified range of skins and shuts down the server=Завантажити масив скінів та зупинити сервер
Skins=Скіни
Requires skin key=Залежний ідентифікатор скіна
unknown parameter=невизначений параметр
Raw texture=Текстура
Page=Сторінка
Name=Ім'я
Author=Автор
License=Ліцензія
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=Завантажити масив скінів та зупинити сервер
Change=Змінити
Skins=Скіни

View File

@ -1,20 +1,19 @@
# textdomain: skinsdb
#Translation by IFRFSX(BingFengFSX)
Raw texture=自然状态的纹理
Name=名称
Author=作者
Change=更换
Page=页面
License=许可证
Description=说明
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=显示,列出或者设置玩家的皮肤
Player not found=玩家未找到
unknown command=未知命令
see /help skinsdb for supported parameters=有关skinsdb支持的参数参见 /help
skin set to=皮肤设置为
invalid skin=无效皮肤
Requires skin key=
unknown parameter=未知参数
unknown skin=未知皮肤
Raw texture=自然状态的纹理
Page=页面
Name=名称
Author=作者
License=许可证
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=下载指定范围的皮肤并关闭服务器
Change=更换
Skins=皮肤

View File

@ -1,19 +1,19 @@
# textdomain: skinsdb
#Translation by IFRFSXBingFengFSX
Raw texture=自然狀態的紋理
Name=名稱
Author=作者
Change=更換
Page=頁面
License=許可證
Description=說明
Showlist or set player's skin=顯示,列出或者設定玩家的皮膚
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=顯示,列出或者設定玩家的皮膚
Player not found=玩家未找到
unknown command=未知命令
see /help skinsdb for supported parameters=有關skinsdb支持的參數參見/help
skin set to=皮膚設定為
invalid skin=無效皮膚
Requires skin key=
unknown parameter=未知參數
unknown skin=未知皮膚
Raw texture=自然狀態的紋理
Page=頁面
Name=名稱
Author=作者
License=許可證
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=下載指定範圍的皮膚並關閉服務器
Change=更換
Skins=皮膚

View File

@ -1,20 +1,19 @@
# textdomain: skinsdb
Raw texture=
Name=
Author=
Change=
Page=
License=
Description=
[set] <skin key> | show [<skin key>] | list | list private | list public | [ui]=
Show, list or set player's skin=
Player not found=
unknown command=
see /help skinsdb for supported parameters=
skin set to=
invalid skin=
unknown parameter=
unknown skin=
Downloads the specified range of skins and shuts down the server=
Skins=
Requires skin key=
unknown parameter=
Raw texture=
Page=
Name=
Author=
License=
<skindb start page> <amount of pages>=
Downloads the specified range of skins and shuts down the server=
Change=
Skins=

View File

@ -1,3 +0,0 @@
# texture filename seperator, default "_"
# see skindsdb/textures/readme.txt to avoid playername with underscore problem
skinsdb_fsep (texture filename seperator) enum _ _,.

View File

@ -1,73 +1,151 @@
local skins_dir_list = minetest.get_dir_list(skins.modpath.."/textures")
local dbgprint = false and print or function() end
for _, fn in pairs(skins_dir_list) do
local name, sort_id, is_preview, playername
local nameparts = string.gsub(fn, "[.]", skins.fsep):split(skins.fsep)
--- @param path Path to the "textures" directory, without tailing slash.
--- @param filename Current file name, such as "player.groot.17.png".
--- @return On error: false, error message. On success: true, skin key
function skins.register_skin(path, filename)
-- See "textures/readme.txt" for allowed formats
-- check allowed prefix and file extension
if (nameparts[1] == 'player' or nameparts[1] == 'character') and
nameparts[#nameparts]:lower() == 'png' then
local prefix, sep, identifier, extension = filename:match("^(%a+)([_.])([%w_.-]+)%.(%a+)$")
--[[
prefix: "character" or "player"
sep: "." (new) or "_" (legacy)
identifier: number, name or (name + sep + number)
^ previews are explicity skipped
extension: "png" only due `skins.get_skin_format`
]]
-- cut filename extension
table.remove(nameparts, #nameparts)
-- Filter out files that do not match the allowed patterns
if not extension or extension:lower() ~= "png" then
return false, "invalid skin name"
end
if prefix ~= "player" and prefix ~= "character" then
return false, "unknown type"
end
-- check preview suffix
if nameparts[#nameparts] == 'preview' then
is_preview = true
table.remove(nameparts, #nameparts)
local preview_suffix = sep .. "preview"
if identifier:sub(-#preview_suffix) == preview_suffix then
-- The preview texture is added by the main skin texture (if exists)
return false, "preview texture"
end
assert(path)
if path == ":UNITTEST:" then
path = nil
end
dbgprint("Found skin", prefix, identifier, extension)
local sort_id -- number, sorting "rank" in the skin list
local playername -- string, if player-specific
if prefix == "player" then
-- Allow "player.PLAYERNAME.png" and "player.PLAYERNAME.123.png"
local splits = identifier:split(sep)
playername = splits[1]
-- Put in front
sort_id = 0 + (tonumber(splits[2]) or 0)
if #splits > 1 and sep == "_" then
minetest.log("warning", "skinsdb: The skin name '" .. filename .. "' is ambigous." ..
" Please use the separator '.' to lock it down to the correct player name.")
end
else -- Public skin "character*"
-- Less priority
sort_id = 5000 + (tonumber(identifier) or 0)
end
-- Build technically skin name
name = table.concat(nameparts, '_')
local filename_noext = prefix .. sep .. identifier
-- Handle metadata from file name
if not is_preview then
-- Get player name
if nameparts[1] == "player" then
playername = nameparts[2]
table.remove(nameparts, 1)
sort_id = 0
else
sort_id = 5000
end
dbgprint("Register skin", filename_noext, playername, sort_id)
-- Get sort index
if tonumber(nameparts[#nameparts]) then
sort_id = sort_id + nameparts[#nameparts]
end
-- Register skin texture
local skin_obj = skins.get(filename_noext) or skins.new(filename_noext)
skin_obj:set_texture(filename)
skin_obj:set_meta("_sort_id", sort_id)
if sep ~= "_" then
skin_obj._legacy_name = filename_noext:gsub("[._]+", "_")
end
if playername then
skin_obj:set_meta("assignment", "player:"..playername)
skin_obj:set_meta("playername", playername)
end
if path then
-- Get type of skin based on dimensions
local file = io.open(path .. "/" .. filename, "r")
local skin_format = skins.get_skin_format(file)
skin_obj:set_meta("format", skin_format)
file:close()
end
skin_obj:set_hand_from_texture()
skin_obj:set_meta("name", identifier)
if path then
-- Optional skin information
local file = io.open(path .. "/../meta/" .. filename_noext .. ".txt", "r")
if file then
dbgprint("Found meta")
local data = string.split(file:read("*all"), "\n", 3)
skin_obj:set_meta("name", data[1])
skin_obj:set_meta("author", data[2])
skin_obj:set_meta("license", data[3])
file:close() -- do not rely on delayed GC
end
end
local skin_obj = skins.get(name) or skins.new(name)
if is_preview then
skin_obj:set_preview(fn)
else
skin_obj:set_texture(fn)
skin_obj:set_meta("_sort_id", sort_id)
if playername then
skin_obj:set_meta("assignment", "player:"..playername)
skin_obj:set_meta("playername", playername)
end
local file = io.open(skins.modpath.."/textures/"..fn, "r")
local skin_format = skins.get_skin_format(file)
skin_obj:set_meta("format", skin_format)
file:close()
skin_obj:set_hand_from_texture()
file = io.open(skins.modpath.."/meta/"..name..".txt", "r")
if file then
local data = string.split(file:read("*all"), "\n", 3)
file:close()
skin_obj:set_meta("name", data[1])
skin_obj:set_meta("author", data[2])
skin_obj:set_meta("license", data[3])
else
-- remove player / character prefix if further naming given
if nameparts[2] and not tonumber(nameparts[2]) then
table.remove(nameparts, 1)
end
skin_obj:set_meta("name", table.concat(nameparts, ' '))
end
if path then
-- Optional preview texture
local preview_name = filename_noext .. sep .. "preview.png"
local fh = io.open(path .. "/" .. preview_name)
if fh then
dbgprint("Found preview", preview_name)
skin_obj:set_preview(preview_name)
fh:close() -- do not rely on delayed GC
end
end
return true, skin_obj:get_key()
end
--- Internal function. Fallback/migration code for `.`-delimited skin names that
--- were equipped between d3c7fa7 and 312780c (master branch).
--- During this period, `.`-delimited skin names were internally registered with
--- `_` delimiters. This function tries to find a matching skin.
--- @param player_name (string)
--- @param skin_name (string) e.g. `player_foo_mc_bar`
--- @param be_noisy (boolean) whether to print a warning in case of mismatches`
--- @return On match, the new skin (skins.skin_class) or `nil` if nothing matched.
function skins.__fuzzy_match_skin_name(player_name, skin_name, be_noisy)
if select(2, skin_name:gsub("%.", "")) > 0 then
-- Not affected by ambiguity
return
end
for _, skin in pairs(skins.meta) do
if skin._legacy_name == skin_name then
dbgprint("Match", skin_name, skin:get_key())
return skin
end
--dbgprint("Try match", skin_name, skin:get_key(), skin._legacy_name)
end
if be_noisy then
minetest.log("warning", "skinsdb: cannot find matching skin '" ..
skin_name .. "' for player '" .. player_name .. "'.")
end
end
do
-- Load skins from the current mod directory
local skins_path = skins.modpath.."/textures"
local skins_dir_list = minetest.get_dir_list(skins_path)
for _, fn in pairs(skins_dir_list) do
skins.register_skin(skins_path, fn)
end
end
local function skins_sort(skinslist)

View File

@ -20,7 +20,7 @@ if not ie or not http then
end
minetest.register_chatcommand("skinsdb_download_skins", {
params = "<skindb start page> <amount of pages>",
params = S("<skindb start page> <amount of pages>"),
description = S("Downloads the specified range of skins and shuts down the server"),
privs = {server=true},
func = function(name, param)
@ -50,9 +50,9 @@ end
local root_url = "http://skinsdb.terraqueststudios.net"
local page_url = root_url .. "/api/v1/content?client=mod&page=%i" -- [1] = Page#
local mod_path = skins.modpath
local meta_path = mod_path .. "/meta/"
local skins_path = mod_path .. "/textures/"
local download_path = skins.modpath
local meta_path = download_path .. "/meta/"
local skins_path = download_path .. "/textures/"
-- Fancy debug wrapper to download an URL
local function fetch_url(url, callback)
@ -80,14 +80,22 @@ local function unsafe_file_write(path, contents)
end
-- Takes a valid skin table from the Skins Database and saves it
local function safe_single_skin(skin)
local function save_single_skin(skin)
local meta = {
skin.name,
skin.author,
skin.license
}
local name = "character" .. skins.fsep .. skin.id
local name = "character." .. skin.id
do
local legacy_name = "character_" .. skin.id
local fh = ie.io.open(skins_path .. legacy_name .. ".png", "r")
-- Use the old name if either the texture ...
if fh then
name = legacy_name
end
end
-- core.safe_file_write does not work here
unsafe_file_write(
@ -128,7 +136,7 @@ internal.fetch_function = function(pages_total, start_page, len)
assert(skin.id ~= "")
if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb
safe_single_skin(skin)
save_single_skin(skin)
end
end

View File

@ -1,25 +1,33 @@
In this folder the skin files could be placed according the following file naming convention.
This location is where you can put your custom skins.
skinsdb uses an underscore as default seperator for filename splitting which can cause problems with playernames containing "_",
see https://github.com/minetest-mods/skinsdb/issues/54.
The config setting skinsdb_fsep (texture filename seperator) was added as a workaround which also offers "."(dot) as seperator,
dot is the only character which is allowed in textures but not in playernames.
To keep compatibility with older versions underscore is the default value.
fresh install:
you should change the seperator to "." to avoid that problem.
existing install:
- change the filenames according to the naming convention with dot as seperator instead of underscore
- change the texture filename seperator in settings or add "skinsdb_fsep = ." to your minetest.conf before starting your server
List of accepted texture names
------------------------------
Public skin available for all users:
character_[number-or-name].png
character.[number or name].png
^ The allowed characters in "[number or name]" are "[A-z0-9_.-]+".
One or multiple private skins for player "nick":
player_[nick].png or
player_[nick]_[number-or-name].png
One or multiple private skins for player "[nick]":
player.[nick].png
player.[nick].[number or name].png
Preview files for public and private skins.
Optional, overrides the generated preview
character_*_preview.png or
player_*_*_preview.png
Skin previews for public and private skins:
character.[number or name].preview.png
player.[nick].preview.png
player.[nick].[number or name].preview.png
Note: This is optional and overrides automatically generated preciewws.
Legacy texture names
--------------------
The character `_` is accepted in player names, thus it is not recommended to
use such file names. For compatibility reasons, they are still recognized.
character_[number or name].png
player_[nick].png
player_[nick]_[number or name].png
... and corresponding previews that end in `_preview.png`.

51
unittest.lua Normal file
View File

@ -0,0 +1,51 @@
local function get_skin(skin_name)
local skin = skins.get(skin_name)
or skins.__fuzzy_match_skin_name("(unittest)", skin_name, true)
return skin and skin:get_key() or nil
end
local function run_unittest()
local PATH = ":UNITTEST:"
-- -----
-- `.`: Simple register + retrieve operations
assert(skins.register_skin(PATH, "player.DotSep.png"))
assert(skins.register_skin(PATH, "player._DotSep_666_.1.png"))
assert(skins.register_skin(PATH, "character._DotSep_With-Dash-.png"))
assert(get_skin("player.DotSep"))
assert(get_skin("player._DotSep_666_.1"))
assert(get_skin("player.DotSep.1") == nil)
assert(get_skin("character._DotSep_With-Dash-"))
-- -----
-- Ambiguous skin names (filenames without extension). Register + retrieve
skins.new("player_AmbSki")
skins.new("player_AmbSki_1")
skins.new("player_AmbSki_666_1")
assert(get_skin("player_AmbSki"))
assert(get_skin("player_AmbSki_") == nil)
assert(get_skin("player_AmbSki_1"))
assert(get_skin("player_AmbSki_666_1"))
-- There are no `__` patterns as they were silently removed by string.split
-- -----
-- Mod Storage backwards compatibility
-- Match the old `_` notation to `.`-separated skins
skins.register_skin(PATH, "player.ComPat42.png")
skins.register_skin(PATH, "player.ComPat42.5.png")
skins.register_skin(PATH, "player._Com_Pat_42.png")
skins.register_skin(PATH, "player._Com_Pat_42.1.png")
assert(get_skin("player_ComPat42") == "player.ComPat42")
assert(get_skin("player_ComPat42_5") == "player.ComPat42.5")
assert(get_skin("player_Com_Pat_42") == "player._Com_Pat_42")
assert(get_skin("player_Com_Pat_42_1") == "player._Com_Pat_42.1")
error("Unittest passed! Please disable them now.")
end
run_unittest()

View File

@ -1,9 +1,4 @@
import sys, requests, base64
# filename seperator to use, either default "-" or ".". see skinsdb/textures/readme.txt
#fsep = "_"
fsep = "."
import os.path, sys, requests, base64
print("Downloading skins from skinsdb.terraqueststudio.net ...")
@ -22,21 +17,27 @@ print("Writing skins")
for json in data["skins"]:
id = str(json["id"])
name = "character." + id
if True:
legacy_name = "character_" + id
if os.path.exists("../textures/" + legacy_name + ".png"):
name = legacy_name
# Texture file
raw_data = base64.b64decode(json["img"])
file = open("../textures/character" + fsep + id + ".png", "wb")
file = open("../textures/" + name + ".png", "wb")
file.write(bytearray(raw_data))
file.close()
# Meta file
name = str(json["name"])
author = str(json["author"])
license = str(json["license"])
file = open("../meta/character_" + id + ".txt", "w")
file.write(name + "\n" + author + "\n" + license + "\n")
meta_name = str(json["name"])
meta_author = str(json["author"])
meta_license = str(json["license"])
file = open("../meta/" + name + ".txt", "w")
file.write(meta_name + "\n" + meta_author + "\n" + meta_license + "\n")
file.close()
print("Added #%s Name: %s Author: %s License: %s" % (id, name, author, license))
print("Added #%s Name: %s Author: %s License: %s" % (id, meta_name, meta_author, meta_license))
count += 1
print("Fetched " + str(count) + " skins!")