64 Commits

Author SHA1 Message Date
8d5870c54c Merge branch 'master' into contentdb 2025-04-16 19:36:38 +02:00
bc69b03d0b Update hand overrides to v5.12.0-dev changes
Luanti commit e037873 changed how the tool range and caps are taken into account.
Hence, skin hands now overwrite the long-range hand (`""`) provided by the creative mod.
We want to preserve the tool range and capabilities, thus this commit.
2025-04-11 17:20:05 +02:00
247bcdd058 Fix client-side out-of-bounds '[combine' warnings 2025-03-16 18:49:54 +01:00
a0ed992121 Update player_api model registration
Ensures correct eye position and collision box.
Values taken from the player_api 'character.b3d' model.
2025-03-16 18:38:45 +01:00
bde2f1e3f2 Merge branch 'master' into contentdb 2024-09-28 11:15:10 +02:00
3cf80c9272 skinlist: Allow textures containing '-' characters
Fixes issue #110
Thanks to Bastrabun for the code suggestion
2024-09-28 11:12:31 +02:00
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
b7cd514cea API: make skins.register_skin public (#106) 2024-07-07 18:51:48 +02:00
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
71f803e2fb Fix private skin variations not showing up
'player.playername.43.png' skins were not recognized
2024-06-10 18:30:25 +02:00
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
59f0dcaf3f Merge branch 'master' into contentdb 2024-01-06 12:33:21 +01:00
cd27e24b6f Added Esperanto (eo) translation 2024-01-06 12:26:52 +01:00
58739e9f9f Make command parameters translatable 2024-01-06 12:26:52 +01:00
e1cd937470 Fix typo privat -> private (#94) 2023-12-21 17:08:17 +01:00
616262de4b Update instructions for downloading skins (#93)
Corrected URL and updater instructions.
2023-12-17 10:05:53 +01:00
271c109073 Replace skinsdb API source (#91)
The old API is no longer reachable, thus got replaced.
This also removes the obsolete skin preview download because the mod is capable of generating them automatically.

Co-authored-by: CallMeDax <142984237+CallMeDax@users.noreply.github.com>
2023-09-06 16:39:11 +02:00
d3c7fa71c0 Filename seperator setting to fix #54 (#83)
Adds a filename split seperator setting that lets you choose between old style `_` and `.` because dot is the only char that isn´t allowed in playername but in texturenames.
Default value is `_` to keep it compatible with older versions, while `.` offers a solution to fix #54.
2023-05-10 21:41:35 +02:00
c6f9a26970 Explain what this branch is 2023-04-02 20:44:19 +02:00
353f36d551 Merge branch 'master' into contentdb 2023-04-02 20:42:50 +02:00
1d1053dbc2 Remove mesh hand node when player exits (#84) 2023-02-15 21:07:27 +01:00
d4b398eae2 Merge remote-tracking branch 'origin/master' into contentdb 2023-01-16 07:47:27 +01:00
b49907c4b1 Add hand_monoid support (#81) 2023-01-15 14:16:44 +01:00
6db9a55f0c Add compatibility w/ animations of the emote mod (#82) 2023-01-15 14:15:45 +01:00
ea7aacc9e7 update filenames of default textures to reflect metadata
Signed-off-by: Nissa <and-nissa@protonmail.com>
2022-12-31 09:21:40 +01:00
743e2cfc9c rename male default textures in consistency with female textures
Signed-off-by: Nissa <and-nissa@protonmail.com>
2022-12-31 09:21:40 +01:00
abc3df86e3 Merge remote-tracking branch 'origin/master' into HEAD 2022-12-30 11:19:46 +01:00
c0b6f761dc Add portuguese Brazilian language (#76)
Adds to the mod a language that translates to Portuguese
2022-12-15 18:48:51 +01:00
e275159dd0 Add uv wrapped 1.8 hand model (#74) 2022-10-05 21:15:03 +02:00
b769824d24 Add Continuous Integration and LuaCheck (#71)
All warnings have been fixed as well.
2022-06-16 12:06:35 +02:00
41524201d1 Merge remote-tracking branch 'origin/master' into contentdb 2022-03-31 08:18:52 +02:00
03d424fea7 fix hand node not respected the range 2022-03-31 08:14:33 +02:00
41be19fc7a Merge remote-tracking branch 'origin/master' into contentdb 2022-03-06 11:56:53 +01:00
c9fa5d92df fix Undeclared global variable "unified_inventory" accessed 2022-03-06 11:51:20 +01:00
af5e6fcecc adjustments from code review 2022-03-06 11:51:20 +01:00
2e39651821 implement skinned 1st person view hand 2022-03-06 11:51:20 +01:00
ad189102d5 fix Undeclared global variable "unified_inventory" accessed 2022-03-05 12:53:55 +01:00
e0765da839 adjustments from code review 2022-03-05 12:44:48 +01:00
482a13b495 implement skinned 1st person view hand 2022-02-16 10:24:03 +01:00
da820d06cb fix skin list without sort key or name 2022-02-16 10:19:47 +01:00
cf712e5b54 fix skins.set_player_skin call with skin name parameter
this fix the homedecor_wardrobe compatibility
2022-02-15 14:29:45 +01:00
56353133e1 Added russian (ru) translation 2022-02-03 07:38:25 +01:00
443cda2183 Added ukrainian (uk) translation 2022-02-03 07:38:25 +01:00
fab418b56f Log player skin changes (#64) 2022-02-03 07:38:25 +01:00
c51905d44b Added russian (ru) translation 2022-02-02 18:58:21 +01:00
b3ea5a9e1f Added ukrainian (uk) translation 2022-02-02 18:58:21 +01:00
e479e8e745 Log player skin changes (#64) 2022-02-02 18:58:06 +01:00
00f6a8137e Merge branch 'master' into contentdb 2022-01-04 11:42:00 +01:00
716a9a3f9a fix get_string() on wrong variable 2021-07-02 07:42:10 +02:00
c53158d159 Update to work with Unified Inventory v2
i.e. the formspec v4 rewrite
Requires UI "version-2" tag or commit a7556c50 or later and
and Minetest v5.4.0 or later

This also makes it work properly in UI's "lite" mode.
2021-03-27 16:09:38 +01:00
da6905fd02 Mention i3 support (#55) 2021-01-03 09:36:20 +01:00
9b8fb79030 update_skins.py
Remove leading zeros again
2020-09-10 14:29:07 +02:00
02323fc427 updater/update_skins.py: Do not download preview files by default 2020-09-10 14:29:07 +02:00
3aebed6fed api.lua: Fix deprecation warnings 2020-09-10 14:29:07 +02:00
63555ed0be Add python skin update script (#48) 2020-09-10 14:29:07 +02:00
7b59880736 fix chrash when using /skinsdb set without parameter or /skinsdb list public 2020-09-10 14:29:07 +02:00
8e1e525340 Fix corrupted data in Windows systems (#47)
The base64 data from the decoder to the file writer is in binary, using only the "w" flag would work in most unix systems but its incorrect and for all systems with crazy line_feed/Carrier_return or any character conversion the right way to save a binary data is using the "wb" flag.
2020-09-10 14:29:07 +02:00
af8212a1bd Merge branch 'master' into contentdb 2020-02-12 08:10:09 +01:00
361d0222ca remove _preview.png files 2020-02-10 07:13:49 +01:00
436809f8fa Merge branch 'master' into contentdb 2020-02-10 07:12:23 +01:00
04b43a5969 Merge branch 'master' into contentdb 2020-01-14 14:58:47 +01:00
dcca582ef3 Merge branch 'master' into contentdb 2020-01-08 08:13:16 +01:00
f1d0f0789d add skins credit to README.md 2019-08-09 07:00:02 +02:00
5c13010014 add sirrobzeroone skins for contentdb 2019-08-09 06:57:34 +02:00
48 changed files with 734 additions and 237 deletions

11
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,11 @@
on: [push, pull_request]
name: build
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: lint
uses: Roang-zero1/factorio-mod-luacheck@master
with:
luacheckrc_url: ""

4
.gitignore vendored
View File

@ -1,4 +1,4 @@
textures/character_*.png
#textures/character_*.png
textures/player_*.png
meta/character_*.txt
#meta/character_*.txt
meta/player_*.txt

14
.luacheckrc Normal file
View File

@ -0,0 +1,14 @@
unused_args = false
allow_defined_top = true
max_line_length = 999
globals = {
"minetest", "unified_inventory", "core",
"player_api", "clothing", "armor", "sfinv",
}
read_globals = {
string = {fields = {"split", "trim"}},
table = {fields = {"copy", "getn"}},
"hand_monoid",
}

40
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
@ -55,6 +80,17 @@ Get the skin texture for any reason. Note to apply them the skin:set_skin() shou
Could be redefined for dynamic texture generation
## skin:set_hand(hand_node)
Set the hand node to be used with this skin
## skin:set_hand_from_texture()
Register and set hand node based on skin texture.
Uses different model depending on get_meta("format") ("1.0" or "1.8")
Only works on mod load
## skin:get_hand()
Get hand node. Returns ItemStack
## skin:set_preview(texture)
Set the skin preview - usually at the init time only
@ -67,7 +103,7 @@ Could be redefined for dynamic preview texture generation
Hook for dynamic skins updates on select. Is called in skins.set_player_skin()
In skinsdb the default implementation for this function is empty.
skin:apply_skin_to_player(player)
## skin:apply_skin_to_player(player)
Apply the skin to the player. Called in skins.update_player_skin() to update visuals
## skin:set_meta(key, value)
@ -80,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

@ -1,4 +1,4 @@
# skinsdb
# skinsdb - User-facing branch that includes a few basic skins
This Minetest mod offers changeable player skins with a graphical interface for multiple inventory mods.
@ -9,24 +9,27 @@ This Minetest mod offers changeable player skins with a graphical interface for
- Skin change menu for sfinv (in minetest_game) and [unified_inventory](https://forum.minetest.net/viewtopic.php?t=12767)
- Skins change menu and command line using chat command /skinsdb (set | show | list | list private | list public | ui)
- Supported by [smart_inventory](https://forum.minetest.net/viewtopic.php?t=16597) for the skin selection
- Supported by [i3](https://github.com/minetest-mods/i3) inventory mod
- Skin previews supported in selection
- Additional information for each skin
- Support for different skins lists: public and a per-player list are currently implemented
- Full [3d_armor](https://forum.minetest.net/viewtopic.php?t=4654) support
- Compatible to 1.0 and 1.8 Minecraft skins format
- Skinned hand in 1st person view (1.0 skins only)
## 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.
@ -35,9 +38,9 @@ You might want to run `minetest` in a Terminal/Console window to check the log o
**Requirements:**
* Python 3
* `requests` library: `pip3 install requests`
Go to the updater folder of this mod and run `python3 update_skins.py`
* `requests` library: `pip3 install requests`
Go to the updater folder of this mod and run `python3 update_skins.py`
The Script will download all the skins from the database for you.
### Manual addition
@ -52,6 +55,7 @@ The Script will download all the skins from the database for you.
## License:
- GPLv3
- skin texture licenses: See "meta" folder
- hand model: CC0
### Credits
@ -61,3 +65,5 @@ The Script will download all the skins from the database for you.
- Krock (source code)
- bell07 (source code)
- stujones11 (player models)
- jordan4ibanez (1st person view hand)
- sirrobzeroone (skins)

23
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(), player: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
@ -34,7 +46,7 @@ function skins.assign_player_skin(player, skin)
else
return false
end
return true
return true, skin_obj
end
-- update visuals
@ -53,10 +65,11 @@ end
-- Assign and update - should be used on selection externally
function skins.set_player_skin(player, skin)
local success = skins.assign_player_skin(player, skin)
local success, skin_obj = skins.assign_player_skin(player, skin)
if success then
skins.get_player_skin(player):set_skin(player)
skins.update_player_skin(player)
minetest.log("action", player:get_player_name().." set skin to "..skin_obj:get_key(""))
end
return success
end

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

@ -1,4 +1,5 @@
local S = minetest.get_translator("skinsdb")
local ui = minetest.global_exists("unified_inventory") and unified_inventory
function skins.get_formspec_context(player)
if player then
@ -11,7 +12,7 @@ function skins.get_formspec_context(player)
end
-- Show skin info
function skins.get_skin_info_formspec(skin)
function skins.get_skin_info_formspec(skin, perplayer_formspec)
local texture = skin:get_texture()
local m_name = skin:get_meta_string("name")
local m_author = skin:get_meta_string("author")
@ -20,30 +21,82 @@ function skins.get_skin_info_formspec(skin)
-- overview page
local raw_size = m_format == "1.8" and "2,2" or "2,1"
local formspec = "image[0.8,.6;1,2;"..minetest.formspec_escape(skin:get_preview()).."]"
local lxoffs = 0.8
local cxoffs = 2
local rxoffs = 5.5
if type(perplayer_formspec) == "table" then -- we're using Unified Inventory
lxoffs = 1.5
cxoffs = 3.75
rxoffs = 7.5
end
local formspec = "image["..lxoffs..",.6;1,2;"..minetest.formspec_escape(skin:get_preview()).."]"
if texture then
formspec = formspec.."label[6,.5;"..S("Raw texture")..":]"
.."image[6,1;"..raw_size..";"..texture.."]"
formspec = formspec.."label["..rxoffs..",.5;"..S("Raw texture")..":]"
.."image["..rxoffs..",1;"..raw_size..";"..texture.."]"
end
if m_name ~= "" then
formspec = formspec.."label[2,.5;"..S("Name")..": "..minetest.formspec_escape(m_name).."]"
formspec = formspec.."label["..cxoffs..",.5;"..S("Name")..": "..minetest.formspec_escape(m_name).."]"
end
if m_author ~= "" then
formspec = formspec.."label[2,1;"..S("Author")..": "..minetest.formspec_escape(m_author).."]"
formspec = formspec.."label["..cxoffs..",1;"..S("Author")..": "..minetest.formspec_escape(m_author).."]"
end
if m_license ~= "" then
formspec = formspec.."label[2,1.5;"..S("License")..": "..minetest.formspec_escape(m_license).."]"
formspec = formspec.."label["..cxoffs..",1.5;"..S("License")..": "..minetest.formspec_escape(m_license).."]"
end
return formspec
end
function skins.get_skin_selection_formspec(player, context, y_delta)
function skins.get_skin_selection_formspec(player, context, perplayer_formspec)
context.skins_list = skins.get_skinlist_for_player(player:get_player_name())
context.total_pages = 1
local xoffs = 0
local yoffs = 4
local xspc = 1
local yspc = 2
local skinwidth = 1
local skinheight = 2
local xscale = 1 -- luacheck: ignore
local btn_y = 8.15
local drop_y = 8
local btn_width = 1
local droppos = 1
local droplen = 6.25
local btn_right = 7
local maxdisp = 16
local ctrls_height = 0.5
if type(perplayer_formspec) == "table" then -- it's being used under Unified Inventory
xoffs = perplayer_formspec.std_inv_x
xspc = ui.imgscale
yspc = ui.imgscale*2
skinwidth = ui.imgscale*0.9
skinheight = ui.imgscale*1.9
xscale = ui.imgscale
btn_width = ui.imgscale
droppos = xoffs + btn_width + 0.1
droplen = ui.imgscale * 6 - 0.2
btn_right = droppos + droplen + 0.1
if perplayer_formspec.pagecols == 4 then -- and we're in lite mode
yoffs = 1
maxdisp = 8
drop_y = yoffs + skinheight + 0.1
else
yoffs = 0.2
drop_y = yoffs + skinheight*2 + 0.2
end
btn_y = drop_y
end
for i, skin in ipairs(context.skins_list ) do
local page = math.floor((i-1) / 16)+1
local page = math.floor((i-1) / maxdisp)+1
skin:set_meta("inv_page", page)
skin:set_meta("inv_page_index", (i-1)%16+1)
skin:set_meta("inv_page_index", (i-1)%maxdisp+1)
context.total_pages = page
end
context.skins_page = context.skins_page or skins.get_player_skin(player):get_meta("inv_page") or 1
@ -51,22 +104,25 @@ function skins.get_skin_selection_formspec(player, context, y_delta)
local page = context.skins_page
local formspec = ""
for i = (page-1)*16+1, page*16 do
for i = (page-1)*maxdisp+1, page*maxdisp do
local skin = context.skins_list[i]
if not skin then
break
end
local index_p = skin:get_meta("inv_page_index")
local x = (index_p-1) % 8
local x = ((index_p-1) % 8) * xspc + xoffs
local y
if index_p > 8 then
y = y_delta + 1.9
y = yoffs + yspc
else
y = y_delta
y = yoffs
end
formspec = formspec.."image_button["..x..","..y..";1,2;"..
minetest.formspec_escape(skin:get_preview())..";skins_set$"..i..";]"..
formspec = formspec..
string.format("image_button[%f,%f;%f,%f;%s;skins_set$%i;]",
x, y, skinwidth, skinheight,
minetest.formspec_escape(skin:get_preview()), i)..
"tooltip[skins_set$"..i..";"..minetest.formspec_escape(skin:get_meta_string("name")).."]"
end
@ -87,10 +143,13 @@ function skins.get_skin_selection_formspec(player, context, y_delta)
if pg > 1 then page_list = page_list.."," end
page_list = page_list..pagename
end
formspec = formspec
.."button[0,"..(y_delta+4.0)..";1,.5;skins_page$"..page_prev..";<<]"
.."dropdown[0.9,"..(y_delta+3.88)..";6.5,.5;skins_selpg;"..page_list..";"..page.."]"
.."button[7,"..(y_delta+4.0)..";1,.5;skins_page$"..page_next..";>>]"
formspec = formspec..
string.format("button[%f,%f;%f,%f;skins_page$%i;<<]",
xoffs, btn_y, btn_width, ctrls_height, page_prev)..
string.format("button[%f,%f;%f,%f;skins_page$%i;>>]",
btn_right, btn_y, btn_width, ctrls_height, page_next)..
string.format("dropdown[%f,%f;%f,%f;skins_selpg;%s;%i]",
droppos, drop_y, droplen, ctrls_height, page_list, page)
end
return formspec
end

View File

@ -63,8 +63,16 @@ end)
minetest.register_on_leaveplayer(function(player)
skins.ui_context[player:get_player_name()] = nil
player:get_inventory():set_size("hand", 0)
end)
minetest.register_on_shutdown(function()
for _, player in pairs(minetest.get_connected_players()) do
player:get_inventory():set_size("hand", 0)
end
end)
-- See also: 3d_armor/init.lua
player_api.register_model("skinsdb_3d_armor_character_5.b3d", {
animation_speed = 30,
textures = {
@ -75,12 +83,21 @@ player_api.register_model("skinsdb_3d_armor_character_5.b3d", {
},
animations = {
stand = {x=0, y=79},
lay = {x=162, y=166},
lay = {x=162, y=166, eye_height = 0.3, override_local = true,
collisionbox = {-0.6, 0.0, -0.6, 0.6, 0.3, 0.6}},
walk = {x=168, y=187},
mine = {x=189, y=198},
walk_mine = {x=200, y=219},
sit = {x=81, y=160},
sit = {x=81, y=160, eye_height = 0.8, override_local = true,
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.0, 0.3}},
-- compatibility w/ the emote mod
wave = {x = 192, y = 196, override_local = true},
point = {x = 196, y = 196, override_local = true},
freeze = {x = 205, y = 205, override_local = true},
},
collisionbox = {-0.3, 0.0, -0.3, 0.3, 1.7, 0.3},
-- stepheight: use default
eye_height = 1.47,
})
-- Register default character.png if not part of this mod
@ -91,4 +108,15 @@ if not default_skin_obj then
default_skin_obj:set_meta("format", '1.0')
default_skin_obj:set_meta("_sort_id", 0)
default_skin_obj:set_meta("name", "Sam")
default_skin_obj:set_hand_from_texture()
end
-- Secure hand inventory slot
minetest.register_allow_player_inventory_action(function(player, action, inv, data)
if data.to_list == "hand" or data.from_list == "hand" or data.listname == "hand" then
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=

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

@ -0,0 +1,19 @@
# textdomain: skinsdb
[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
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

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

@ -0,0 +1,19 @@
# textdomain: skinsdb
[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=некорректный скин
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=Скины

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

@ -0,0 +1,19 @@
# textdomain: skinsdb
[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=некоректний скін
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

@ -0,0 +1,3 @@
Castaway female
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Castaway male
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Farmer female
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Farmer male
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Prince
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Princess
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Rogue female
sirrobzeroone
CC0/Public Domain

View File

@ -0,0 +1,3 @@
Rogue male
sirrobzeroone
CC0/Public Domain

View File

@ -1,4 +1,5 @@
name = skinsdb
description = Player skin mod, supporting unified_inventory, sfinv and smart_inventory
depends = player_api
optional_depends = unified_inventory,3d_armor,clothing,sfinv
optional_depends = unified_inventory, 3d_armor, clothing, creative, sfinv, hand_monoid
min_minetest_version = 5.4.0

BIN
models/skinsdb_hand.b3d Normal file

Binary file not shown.

BIN
models/skinsdb_hand.blend Normal file

Binary file not shown.

BIN
models/skinsdb_hand_18.b3d Normal file

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,7 @@
skins.meta = {}
local has_hand_monoid = minetest.get_modpath("hand_monoid")
local skin_class = {}
skin_class.__index = skin_class
skins.skin_class = skin_class
@ -47,10 +49,70 @@ function skin_class:set_texture(value)
self._texture = value
end
--- Retrieves the character texture
function skin_class:get_texture()
return self._texture
end
--- Assigns an existing hand item (/node) name to this skin
function skin_class:set_hand(hand)
self._hand = hand
end
function skin_class:get_hand()
return self._hand
end
--- Registers a new hand item based on the skin meta
local ALPHA_CLIP = minetest.features.use_texture_alpha_string_modes and "clip" or true
function skin_class:set_hand_from_texture()
local hand = core.get_current_modname()..':'..self._texture:gsub('[%p%c%s]', '')
local hand_def = {}
hand_def.tiles = {self:get_texture()}
hand_def.visual_scale = 1
hand_def.wield_scale = {x=1,y=1,z=1}
hand_def.paramtype = "light"
hand_def.drawtype = "mesh"
if(self:get_meta("format") == "1.0") then
hand_def.mesh = "skinsdb_hand.b3d"
else
hand_def.mesh = "skinsdb_hand_18.b3d"
end
hand_def.use_texture_alpha = ALPHA_CLIP
core.register_node(hand, table.copy(hand_def))
self._hand_def = hand_def -- for wieldhand overrides
self:set_hand(hand)
end
-- creative (and other mods?) may overwrite the wieldhand very late.
-- Grab the most recent definition and use them as default for our skin hands.
core.register_on_mods_loaded(function()
local default_hand_def = {}
for k, v in pairs(core.registered_items[""]) do
if k ~= "mod_origin"
and k ~= "name"
and k ~= "type"
and k ~= "wield_image"
and string.sub(k, 1, 1) ~= "_" then
default_hand_def[k] = v
end
end
for _, meta in pairs(skins.meta) do
local def = core.registered_nodes[meta._hand]
if def then
local new_def = table.copy(default_hand_def)
-- Overwrite the hand with our fields from `set_hand_from_texture`
for k, v in pairs(meta._hand_def) do
new_def[k] = v
end
core.override_item(meta._hand, new_def)
end
meta._hand_def = nil -- no longer needed, free up RAM
end
end)
function skin_class:set_preview(value)
self._preview = value
end
@ -75,7 +137,7 @@ function skin_class:get_preview()
--Right Leg
skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^"
-- 64x skins have non-mirrored arms and legs
-- 64x64 skins have non-mirrored arms and legs
local left_arm
local left_leg
@ -92,17 +154,21 @@ function skin_class:get_preview()
--Left Leg
skin = skin .. left_leg
-- Add overlays for 64x skins. these wont appear if skin is 32x because it will be cropped out
--Chest Overlay
skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:skindb_mask_chest.png)^"
--Right Arm Overlay
skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:skindb_mask_rarm.png)^"
--Right Leg Overlay
skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^"
--Left Arm Overlay
skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(skindb_mask_rarm.png^[transformFX))^"
--Left Leg Overlay
skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(skindb_mask_rleg.png^[transformFX))"
if self:get_meta("format") == "1.8" then
-- Add overlays for 64x64 skins. This check is needed to avoid
-- client-side out-of-bounds "[combine" warnings.
--Chest Overlay
skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:skindb_mask_chest.png)^"
--Right Arm Overlay
skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:skindb_mask_rarm.png)^"
--Right Leg Overlay
skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^"
--Left Arm Overlay
skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(skindb_mask_rarm.png^[transformFX))^"
--Left Leg Overlay
skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(skindb_mask_rleg.png^[transformFX))"
end
-- Full Preview
skin = "(((" .. skin .. ")^[resize:64x128)^[mask:skindb_transform.png)"
@ -174,6 +240,22 @@ function skin_class:apply_skin_to_player(player)
y = self:get_meta("visual_size_y") or 1
}
})
local hand = self:get_hand()
if has_hand_monoid then
if hand then
hand_monoid.monoid:add_change(player, {name = hand}, "skinsdb:hand")
else
hand_monoid.monoid:del_change(player, "skinsdb:hand")
end
else
if hand then
player:get_inventory():set_size("hand", 1)
player:get_inventory():set_stack("hand", 1, hand)
else
player:get_inventory():set_stack("hand", 1, "")
end
end
end
function skin_class:set_skin(player)
@ -185,6 +267,6 @@ end
function skin_class:is_applicable_for_player(playername)
local assigned_player = self:get_meta("playername")
return assigned_player == nil or assigned_player == true or
playername and (minetest.check_player_privs(playername, {server=true}) or
assigned_player:lower() == playername:lower())
playername and (minetest.check_player_privs(playername, {server=true}) or
assigned_player:lower() == playername:lower())
end

View File

@ -1,71 +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, assignment, is_preview, playername
local nameparts = string.gsub(fn, "[.]", "_"):split("_")
--- @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")
skin_obj:set_meta("format", skins.get_skin_format(file))
file:close()
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)
@ -73,9 +153,9 @@ local function skins_sort(skinslist)
local a_id = a:get_meta("_sort_id") or 10000
local b_id = b:get_meta("_sort_id") or 10000
if a_id ~= b_id then
return a:get_meta("_sort_id") < b:get_meta("_sort_id")
return a_id < b_id
else
return a:get_meta("name") < b:get_meta("name")
return (a:get_meta("name") or 'ZZ') < (b:get_meta("name") or 'ZZ')
end
end)
end

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)
@ -47,13 +47,12 @@ if #internal.errors > 0 then
end
-- http://minetest.fensta.bplaced.net/api/apidoku.md
local root_url = "http://minetest.fensta.bplaced.net"
local page_url = root_url .. "/api/v2/get.json.php?getlist&page=%i&outformat=base64" -- [1] = Page#
local preview_url = root_url .. "/skins/1/%i.png" -- [1] = ID
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)
@ -81,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_" .. 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(
@ -100,9 +107,6 @@ local function safe_single_skin(skin)
skins_path .. name .. ".png",
core.decode_base64(skin.img)
)
fetch_url(preview_url:format(skin.id), function(preview)
unsafe_file_write(skins_path .. name .. "_preview.png", preview)
end)
core.log("action", ("%s: Completed skin %s"):format(_ID_, name))
end
@ -115,7 +119,7 @@ internal.get_pages_count = function(callback, ...)
callback(math.ceil(list.pages / 20), unpack(vars))
end)
end
-- Function to fetch a range of pages
internal.fetch_function = function(pages_total, start_page, len)
start_page = math.max(start_page, 1)
@ -132,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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@ -1,13 +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.
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`.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1,10 +1,13 @@
local S = minetest.get_translator("skinsdb")
unified_inventory.register_page("skins", {
get_formspec = function(player)
get_formspec = function(player, perplayer_formspec)
local skin = skins.get_player_skin(player)
local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]"..skins.get_skin_info_formspec(skin)..
"button[.75,3;6.5,.5;skins_page;"..S("Change").."]"
local boffs = (type(perplayer_formspec) == "table") and 2 or 0.75
local formspec = perplayer_formspec.standard_inv_bg..
skins.get_skin_info_formspec(skin, perplayer_formspec)..
"button["..boffs..",3;6.5,.5;skins_page;"..S("Change").."]"
return {formspec=formspec}
end,
})
@ -15,16 +18,16 @@ unified_inventory.register_button("skins", {
tooltip = S("Skins"),
})
local function get_formspec(player)
local function get_formspec(player, perplayer_formspec)
local context = skins.get_formspec_context(player)
local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]"..
skins.get_skin_selection_formspec(player, context, -0.2)
local formspec = perplayer_formspec.standard_inv_bg..
skins.get_skin_selection_formspec(player, context, perplayer_formspec)
return formspec
end
unified_inventory.register_page("skins_page", {
get_formspec = function(player)
return {formspec=get_formspec(player)}
get_formspec = function(player, perplayer_formspec)
return {formspec=get_formspec(player, perplayer_formspec)}
end
})

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,11 +1,9 @@
import sys, requests, base64
download_preview = ( len (sys.argv) > 1 and sys.argv[1] == "with_preview" )
import os.path, sys, requests, base64
print("Downloading skins from minetest.fensta.bplaced.net ...")
print("Downloading skins from skinsdb.terraqueststudio.net ...")
# Requesting all skins and their raw texture using the API
r = requests.get('http://minetest.fensta.bplaced.net/api/v2/get.json.php?getlist&page=1&per_page=999999999')
r = requests.get('http://skinsdb.terraqueststudios.net/api/v1/content?client=script&page=1&per_page=10000')
if r.status_code != 200:
sys.exit("Request failed!")
@ -13,42 +11,33 @@ if r.status_code != 200:
data = r.json()
count = 0
if download_preview:
print("Writing to file and downloading previews ...")
else:
print("Writing skins")
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_" + 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
if download_preview:
# Downloading the preview of the skin
r2 = requests.get('http://minetest.fensta.bplaced.net/skins/1/' + id + ".png")
if r2.status_code == 200:
# Preview file
preview = r2.content
file = open("../textures/character_" + id + "_preview.png", "wb")
file.write(bytearray(preview))
file.close()
else:
print("Failed to download skin preview #" + id)
print("Fetched " + str(count) + " skins!")