write something there
14
mods/skinsdb/.luacheckrc
Normal 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",
|
||||
}
|
128
mods/skinsdb/API.md
Normal file
|
@ -0,0 +1,128 @@
|
|||
# Skinsdb Interface
|
||||
|
||||
## skins.get_player_skin(player)
|
||||
Return the skin object assigned to the player. Returns default if nothing assigned
|
||||
|
||||
## skins.assign_player_skin(player, skin)
|
||||
Check if allowed and assign the skin for the player without visual updates. The "skin" parameter could be the skin key or the skin object
|
||||
Returns false if skin is not valid or applicable to player
|
||||
|
||||
## skins.update_player_skin(player)
|
||||
Update selected skin visuals on player
|
||||
|
||||
## skins.set_player_skin(player, skin)
|
||||
Function for external usage on skin selection. This function assign the skin, call the skin:set_skin(player) hook to update dynamic skins, then update the visuals
|
||||
|
||||
## skins.get_skin_format(file)
|
||||
Returns the skin format version ("1.0" or "1.8"). File is an open file handle to the texture file
|
||||
|
||||
|
||||
## skins.get_skinlist(assignment, select_unassigned)
|
||||
Obsolete - use get_skinlist_for_player() or get_skinlist_with_meta() instead
|
||||
|
||||
## skins.get_skinlist_for_player(playername)
|
||||
Get all allowed skins for player. All public and all player's private skins. If playername not given only public skins returned
|
||||
|
||||
## skins.get_skinlist_with_meta(key, value)
|
||||
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
|
||||
- key: Unique skins key, like "character_1"
|
||||
- object: Optional. Could be a prepared object with redefinitions
|
||||
|
||||
## skins.get(key)
|
||||
Get existing skin object
|
||||
|
||||
HINT: During build-up phase maybe the next statement is usefull
|
||||
```
|
||||
local skin = skins.get(name) or skins.new(name)
|
||||
```
|
||||
|
||||
|
||||
# Skin object
|
||||
|
||||
## skin:get_key()
|
||||
Get the unique skin key
|
||||
|
||||
## skin:set_texture(texture)
|
||||
Set the skin texture - usually at the init time only
|
||||
|
||||
## skin:get_texture()
|
||||
Get the skin texture for any reason. Note to apply them the skin:set_skin() should be used
|
||||
|
||||
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
|
||||
|
||||
## skin:get_preview()
|
||||
Get the skin preview
|
||||
|
||||
Could be redefined for dynamic preview texture generation
|
||||
|
||||
## skin:set_skin(player)
|
||||
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)
|
||||
Apply the skin to the player. Called in skins.update_player_skin() to update visuals
|
||||
|
||||
## skin:set_meta(key, value)
|
||||
Add a meta information to the skin object
|
||||
|
||||
Note: the information is not stored, therefore should be filled each time during skins registration
|
||||
|
||||
## skin:get_meta(key)
|
||||
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 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)
|
||||
|
||||
## skin:get_meta_string(key)
|
||||
Same as get_meta() but does return "" instead of nil if the meta key does not exists
|
||||
|
||||
## skin:is_applicable_for_player(playername)
|
||||
Returns whether this skin is applicable for player "playername" or not, like private skins
|
14
mods/skinsdb/CREDITS.md
Normal file
|
@ -0,0 +1,14 @@
|
|||
The character textures in this mod are listed/credited below by license and author. Textures and metadata were downloaded from [the online SkinsDB service](https://skinsdb.terraqueststudios.net/). Changes were not made to these works; they are distributed in their original form.
|
||||
|
||||
- Characters released under [CC-BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)
|
||||
- "Farmer1" by sdzen (character_1.png)
|
||||
- "Male" by TenPlus1 (character_2.png)
|
||||
- "Female" by TenPlus1 (character_3.png)
|
||||
- "Adventer girl" by lovehart (character_5.png)
|
||||
- "Oliver_MV" by hansuke123 (character_6.png)
|
||||
- "Flower Girl" by julito (character_7.png)
|
||||
- "Trader 1" by TenPlus1 (character_8.png)
|
||||
- "Angelo" by Extex (character_9.png)
|
||||
- "santa" by jordan4ibanez (character_10.png)
|
||||
- Characters released under [CC0](https://creativecommons.org/publicdomain/zero/1.0/)
|
||||
- "mbb_old" by mbb (character_4.png)
|
68
mods/skinsdb/README.md
Normal file
|
@ -0,0 +1,68 @@
|
|||
# skinsdb
|
||||
|
||||
This Minetest mod offers changeable player skins with a graphical interface for multiple inventory mods.
|
||||
|
||||
## Features
|
||||
|
||||
- Flexible skins API to manage the database
|
||||
- [character_creator](https://github.com/minetest-mods/character_creator) support for custom skins
|
||||
- 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](https://skinsdb.terraqueststudios.net/)
|
||||
|
||||
#### Ingame Downloader
|
||||
|
||||
1) Get Minetest 5.1.0-dev-cb00632 or newer
|
||||
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.
|
||||
|
||||
#### Python Download script
|
||||
|
||||
**Requirements:**
|
||||
|
||||
* Python 3
|
||||
* `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
|
||||
|
||||
1) Copy your skin textures to `textures` as documented in `textures/readme.txt`
|
||||
2) Create `meta/character_<name>.txt` with the following fields (separated by new lines):
|
||||
* Skin name
|
||||
* Author
|
||||
* Skin license
|
||||
|
||||
|
||||
## License:
|
||||
- GPLv3
|
||||
- skin texture licenses: See "meta" folder
|
||||
- hand model: CC0
|
||||
|
||||
### Credits
|
||||
|
||||
- RealBadAngel (unified_inventory)
|
||||
- Zeg9 (skinsdb)
|
||||
- cornernote (source code)
|
||||
- Krock (source code)
|
||||
- bell07 (source code)
|
||||
- stujones11 (player models)
|
||||
- jordan4ibanez (1st person view hand)
|
94
mods/skinsdb/api.lua
Normal file
|
@ -0,0 +1,94 @@
|
|||
-- get current skin
|
||||
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_name, meta:get_string("skinsdb:skin_key"))
|
||||
meta:set_string("skinsdb:skin_key", "")
|
||||
end
|
||||
|
||||
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
|
||||
function skins.assign_player_skin(player, skin)
|
||||
local skin_obj
|
||||
if type(skin) == "string" then
|
||||
skin_obj = skins.get(skin)
|
||||
else
|
||||
skin_obj = skin
|
||||
end
|
||||
|
||||
if not skin_obj then
|
||||
return false
|
||||
end
|
||||
|
||||
if skin_obj:is_applicable_for_player(player:get_player_name()) then
|
||||
local skin_key = skin_obj:get_key()
|
||||
if skin_key == skins.default then
|
||||
skin_key = ""
|
||||
end
|
||||
storage:set_string(player:get_player_name(), skin_key)
|
||||
else
|
||||
return false
|
||||
end
|
||||
return true, skin_obj
|
||||
end
|
||||
|
||||
-- update visuals
|
||||
function skins.update_player_skin(player)
|
||||
if skins.armor_loaded then
|
||||
-- all needed is wrapped and implemented in 3d_armor mod
|
||||
armor:set_player_armor(player)
|
||||
else
|
||||
-- do updates manually without 3d_armor
|
||||
skins.get_player_skin(player):apply_skin_to_player(player)
|
||||
if minetest.global_exists("sfinv") and sfinv.enabled then
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Assign and update - should be used on selection externally
|
||||
function skins.set_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
|
||||
|
||||
-- Check Skin format (code stohlen from stu's multiskin)
|
||||
function skins.get_skin_format(file)
|
||||
file:seek("set", 1)
|
||||
if file:read(3) == "PNG" then
|
||||
file:seek("set", 16)
|
||||
local ws = file:read(4)
|
||||
local hs = file:read(4)
|
||||
local w = ws:sub(3, 3):byte() * 256 + ws:sub(4, 4):byte()
|
||||
local h = hs:sub(3, 3):byte() * 256 + hs:sub(4, 4):byte()
|
||||
if w >= 64 then
|
||||
if w == h then
|
||||
return "1.8"
|
||||
elseif w == h * 2 then
|
||||
return "1.0"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
103
mods/skinsdb/chatcommands.lua
Normal file
|
@ -0,0 +1,103 @@
|
|||
local S = minetest.get_translator("skinsdb")
|
||||
|
||||
local function show_selection_formspec(player)
|
||||
local context = skins.get_formspec_context(player)
|
||||
local name = player:get_player_name()
|
||||
local skin = skins.get_player_skin(player)
|
||||
local formspec = "size[8,8]"..skins.get_skin_info_formspec(skin)
|
||||
formspec = formspec..skins.get_skin_selection_formspec(player, context, 3.5)
|
||||
minetest.show_formspec(name, 'skinsdb_show_ui', formspec)
|
||||
end
|
||||
|
||||
|
||||
minetest.register_chatcommand("skinsdb", {
|
||||
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)
|
||||
if not player then
|
||||
return false, S("Player not found")
|
||||
end
|
||||
|
||||
-- parse command line
|
||||
local command, parameter
|
||||
local words = param:split(" ")
|
||||
local word = words[1]
|
||||
if word == 'set' or word == 'list' or word == 'show' or word == 'ui' then
|
||||
command = word
|
||||
parameter = words[2]
|
||||
elseif skins.get(word) then
|
||||
command = 'set'
|
||||
parameter = word
|
||||
elseif not word then
|
||||
command = 'ui'
|
||||
else
|
||||
return false, S("unknown command").." "..word..", "..S("see /help skinsdb for supported parameters")
|
||||
end
|
||||
|
||||
if command == "set" then
|
||||
if parameter then
|
||||
local success = skins.set_player_skin(player, parameter)
|
||||
if success then
|
||||
return true, S("skin set to").." "..parameter
|
||||
else
|
||||
return false, S("invalid skin").." "..parameter
|
||||
end
|
||||
else return false, S("Requires skin key")
|
||||
end
|
||||
elseif command == "list" then
|
||||
local list
|
||||
if parameter == "private" then
|
||||
list = skins.get_skinlist_with_meta("playername", name)
|
||||
elseif parameter == "public" then
|
||||
list = skins.get_skinlist_for_player()
|
||||
elseif not parameter then
|
||||
list = skins.get_skinlist_for_player(name)
|
||||
else
|
||||
return false, S("unknown parameter"), parameter
|
||||
end
|
||||
|
||||
local current_skin_key = skins.get_player_skin(player):get_key()
|
||||
for _, skin in ipairs(list) do
|
||||
local info = skin:get_key().." - "
|
||||
..S("Name").."="..skin:get_meta_string("name").." "
|
||||
..S("Author").."="..skin:get_meta_string("author").." "
|
||||
..S("License").."="..skin:get_meta_string("license")
|
||||
if skin:get_key() == current_skin_key then
|
||||
info = minetest.colorize("#00FFFF", info)
|
||||
end
|
||||
minetest.chat_send_player(name, info)
|
||||
end
|
||||
elseif command == "show" then
|
||||
local skin
|
||||
if parameter then
|
||||
skin = skins.get(parameter)
|
||||
else
|
||||
skin = skins.get_player_skin(player)
|
||||
end
|
||||
if not skin then
|
||||
return false, S("invalid skin")
|
||||
end
|
||||
local formspec = "size[8,3]"..skins.get_skin_info_formspec(skin)
|
||||
minetest.show_formspec(name, 'skinsdb_show_skin', formspec)
|
||||
elseif command == "ui" then
|
||||
show_selection_formspec(player)
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= "skinsdb_show_ui" then
|
||||
return
|
||||
end
|
||||
|
||||
local context = skins.get_formspec_context(player)
|
||||
|
||||
local action = skins.on_skin_selection_receive_fields(player, context, fields)
|
||||
if action == 'set' then
|
||||
minetest.close_formspec(player:get_player_name(), formname)
|
||||
elseif action == 'page' then
|
||||
show_selection_formspec(player)
|
||||
end
|
||||
end)
|
172
mods/skinsdb/formspecs.lua
Normal file
|
@ -0,0 +1,172 @@
|
|||
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
|
||||
local playername = player:get_player_name()
|
||||
skins.ui_context[playername] = skins.ui_context[playername] or {}
|
||||
return skins.ui_context[playername]
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
|
||||
-- Show skin info
|
||||
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")
|
||||
local m_license = skin:get_meta_string("license")
|
||||
local m_format = skin:get_meta("format")
|
||||
-- overview page
|
||||
local raw_size = m_format == "1.8" and "2,2" or "2,1"
|
||||
|
||||
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["..rxoffs..",.5;"..S("Raw texture")..":]"
|
||||
.."image["..rxoffs..",1;"..raw_size..";"..texture.."]"
|
||||
end
|
||||
if m_name ~= "" then
|
||||
formspec = formspec.."label["..cxoffs..",.5;"..S("Name")..": "..minetest.formspec_escape(m_name).."]"
|
||||
end
|
||||
if m_author ~= "" then
|
||||
formspec = formspec.."label["..cxoffs..",1;"..S("Author")..": "..minetest.formspec_escape(m_author).."]"
|
||||
end
|
||||
if m_license ~= "" then
|
||||
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, 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) / maxdisp)+1
|
||||
skin:set_meta("inv_page", page)
|
||||
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
|
||||
context.dropdown_values = nil
|
||||
|
||||
local page = context.skins_page
|
||||
local formspec = ""
|
||||
|
||||
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) * xspc + xoffs
|
||||
local y
|
||||
if index_p > 8 then
|
||||
y = yoffs + yspc
|
||||
else
|
||||
y = yoffs
|
||||
end
|
||||
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
|
||||
|
||||
if context.total_pages > 1 then
|
||||
local page_prev = page - 1
|
||||
local page_next = page + 1
|
||||
if page_prev < 1 then
|
||||
page_prev = context.total_pages
|
||||
end
|
||||
if page_next > context.total_pages then
|
||||
page_next = 1
|
||||
end
|
||||
local page_list = ""
|
||||
context.dropdown_values = {}
|
||||
for pg=1, context.total_pages do
|
||||
local pagename = S("Page").." "..pg.."/"..context.total_pages
|
||||
context.dropdown_values[pagename] = pg
|
||||
if pg > 1 then page_list = page_list.."," end
|
||||
page_list = page_list..pagename
|
||||
end
|
||||
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
|
||||
|
||||
function skins.on_skin_selection_receive_fields(player, context, fields)
|
||||
for field, _ in pairs(fields) do
|
||||
local current = string.split(field, "$", 2)
|
||||
if current[1] == "skins_set" then
|
||||
skins.set_player_skin(player, context.skins_list[tonumber(current[2])])
|
||||
return 'set'
|
||||
elseif current[1] == "skins_page" then
|
||||
context.skins_page = tonumber(current[2])
|
||||
return 'page'
|
||||
end
|
||||
end
|
||||
if fields.skins_selpg then
|
||||
context.skins_page = tonumber(context.dropdown_values[fields.skins_selpg])
|
||||
return 'page'
|
||||
end
|
||||
end
|
116
mods/skinsdb/init.lua
Normal file
|
@ -0,0 +1,116 @@
|
|||
-- Unified Skins for Minetest - based modified Bags from unfied_inventory and skins from inventory_plus
|
||||
|
||||
-- Copyright (c) 2012 cornernote, Dean Montgomery
|
||||
-- Rework 2017 by bell07
|
||||
-- License: GPLv3
|
||||
|
||||
skins = {}
|
||||
skins.modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||
skins.default = "character"
|
||||
|
||||
dofile(skins.modpath.."/skin_meta_api.lua")
|
||||
dofile(skins.modpath.."/api.lua")
|
||||
dofile(skins.modpath.."/skinlist.lua")
|
||||
dofile(skins.modpath.."/formspecs.lua")
|
||||
dofile(skins.modpath.."/chatcommands.lua")
|
||||
-- Unified inventory page/integration
|
||||
if minetest.get_modpath("unified_inventory") then
|
||||
dofile(skins.modpath.."/unified_inventory_page.lua")
|
||||
end
|
||||
|
||||
if minetest.get_modpath("sfinv") then
|
||||
dofile(skins.modpath.."/sfinv_page.lua")
|
||||
end
|
||||
|
||||
-- ie.loadfile does not exist?
|
||||
skins.ie = minetest.request_insecure_environment()
|
||||
skins.http = minetest.request_http_api()
|
||||
dofile(skins.modpath.."/skins_updater.lua")
|
||||
skins.ie = nil
|
||||
skins.http = nil
|
||||
|
||||
-- 3d_armor compatibility
|
||||
if minetest.global_exists("armor") then
|
||||
skins.armor_loaded = true
|
||||
armor.get_player_skin = function(self, name)
|
||||
local skin = skins.get_player_skin(minetest.get_player_by_name(name))
|
||||
return skin:get_texture()
|
||||
end
|
||||
armor.get_preview = function(self, name)
|
||||
local skin = skins.get_player_skin(minetest.get_player_by_name(name))
|
||||
return skin:get_preview()
|
||||
end
|
||||
armor.update_player_visuals = function(self, player)
|
||||
if not player then
|
||||
return
|
||||
end
|
||||
local skin = skins.get_player_skin(player)
|
||||
skin:apply_skin_to_player(player)
|
||||
armor:run_callbacks("on_update", player)
|
||||
end
|
||||
end
|
||||
|
||||
if minetest.global_exists("clothing") and clothing.player_textures then
|
||||
skins.clothing_loaded = true
|
||||
clothing:register_on_update(skins.update_player_skin)
|
||||
end
|
||||
|
||||
-- Update skin on join
|
||||
skins.ui_context = {}
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
skins.update_player_skin(player)
|
||||
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)
|
||||
|
||||
player_api.register_model("skinsdb_3d_armor_character_5.b3d", {
|
||||
animation_speed = 30,
|
||||
textures = {
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png",
|
||||
"blank.png"
|
||||
},
|
||||
animations = {
|
||||
stand = {x=0, y=79},
|
||||
lay = {x=162, y=166},
|
||||
walk = {x=168, y=187},
|
||||
mine = {x=189, y=198},
|
||||
walk_mine = {x=200, y=219},
|
||||
sit = {x=81, y=160},
|
||||
-- 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},
|
||||
},
|
||||
})
|
||||
|
||||
-- Register default character.png if not part of this mod
|
||||
local default_skin_obj = skins.get(skins.default)
|
||||
if not default_skin_obj then
|
||||
default_skin_obj = skins.new(skins.default)
|
||||
default_skin_obj:set_texture("character.png")
|
||||
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")
|
||||
|
19
mods/skinsdb/locale/skinsdb.de.tr
Normal 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=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
|
||||
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
mods/skinsdb/locale/skinsdb.eo.tr
Normal 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
|
19
mods/skinsdb/locale/skinsdb.fr.tr
Normal 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=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
|
||||
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=
|
19
mods/skinsdb/locale/skinsdb.ms.tr
Normal 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=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
|
||||
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
mods/skinsdb/locale/skinsdb.pt_BR.tr
Normal 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
mods/skinsdb/locale/skinsdb.ru.tr
Normal 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
mods/skinsdb/locale/skinsdb.uk.tr
Normal 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
mods/skinsdb/locale/skinsdb.zh_CN.tr
Normal 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=有关skinsdb支持的参数,参见 /help
|
||||
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
mods/skinsdb/locale/skinsdb.zh_TW.tr
Normal 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=有關skinsdb支持的參數,參見/help
|
||||
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
mods/skinsdb/locale/template.txt
Normal 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=
|
||||
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=
|
3
mods/skinsdb/meta/character_1.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Farmer1
|
||||
sdzen
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_10.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
santa
|
||||
jordan4ibanez
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_2.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Male
|
||||
TenPlus1
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_3.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Female
|
||||
TenPlus1
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_4.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
mbb_old
|
||||
mbb
|
||||
CC 0 (1.0)
|
3
mods/skinsdb/meta/character_5.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Adventer girl
|
||||
lovehart
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_6.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Oliver_MV
|
||||
hansuke123
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_7.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Flower Girl
|
||||
julito
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_8.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Trader 1
|
||||
TenPlus1
|
||||
CC BY-SA 3.0
|
3
mods/skinsdb/meta/character_9.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
Angelo
|
||||
Extex
|
||||
CC BY-SA 3.0
|
5
mods/skinsdb/meta/readme.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
For each skin in textures folder a metadata file can be applied with "txt" suffilx. See character.txt for skin character.png for reference.
|
||||
The file contains:
|
||||
Skin name
|
||||
Author
|
||||
License
|
5
mods/skinsdb/mod.conf
Normal file
|
@ -0,0 +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,hand_monoid
|
||||
min_minetest_version = 5.4.0
|
BIN
mods/skinsdb/models/skinsdb_3d_armor_character_5.b3d
Normal file
BIN
mods/skinsdb/models/skinsdb_hand.b3d
Normal file
BIN
mods/skinsdb/models/skinsdb_hand_18.b3d
Normal file
21
mods/skinsdb/sfinv_page.lua
Normal file
|
@ -0,0 +1,21 @@
|
|||
local S = minetest.get_translator("skinsdb")
|
||||
|
||||
-- generate the current formspec
|
||||
local function get_formspec(player, context)
|
||||
local skin = skins.get_player_skin(player)
|
||||
local formspec = skins.get_skin_info_formspec(skin)
|
||||
formspec = formspec..skins.get_skin_selection_formspec(player, context, 4)
|
||||
return formspec
|
||||
end
|
||||
|
||||
sfinv.register_page("skins:overview", {
|
||||
title = S("Skins"),
|
||||
get = function(self, player, context)
|
||||
-- collect skins data
|
||||
return sfinv.make_formspec(player, context, get_formspec(player, context))
|
||||
end,
|
||||
on_player_receive_fields = function(self, player, context, fields)
|
||||
skins.on_skin_selection_receive_fields(player, context, fields)
|
||||
sfinv.set_player_inventory_formspec(player)
|
||||
end
|
||||
})
|
240
mods/skinsdb/skin_meta_api.lua
Normal file
|
@ -0,0 +1,240 @@
|
|||
skins.meta = {}
|
||||
|
||||
local has_hand_monoid = minetest.get_modpath("hand_monoid")
|
||||
|
||||
local skin_class = {}
|
||||
skin_class.__index = skin_class
|
||||
skins.skin_class = skin_class
|
||||
-----------------------
|
||||
-- Class methods
|
||||
-----------------------
|
||||
-- constructor
|
||||
function skins.new(key, object)
|
||||
assert(key, 'Unique skins key required, like "character_1"')
|
||||
local self = object or {}
|
||||
setmetatable(self, skin_class)
|
||||
self.__index = skin_class
|
||||
|
||||
self._key = key
|
||||
self._sort_id = 0
|
||||
skins.meta[key] = self
|
||||
return self
|
||||
end
|
||||
|
||||
-- getter
|
||||
function skins.get(key)
|
||||
return skins.meta[key]
|
||||
end
|
||||
|
||||
-- Skin methods
|
||||
-- In this implementation it is just access to attrubutes wrapped
|
||||
-- but this way allow to redefine the functionality for more complex skins provider
|
||||
function skin_class:get_key()
|
||||
return self._key
|
||||
end
|
||||
|
||||
function skin_class:set_meta(key, value)
|
||||
self[key] = value
|
||||
end
|
||||
|
||||
function skin_class:get_meta(key)
|
||||
return self[key]
|
||||
end
|
||||
|
||||
function skin_class:get_meta_string(key)
|
||||
return tostring(self:get_meta(key) or "")
|
||||
end
|
||||
|
||||
function skin_class:set_texture(value)
|
||||
self._texture = value
|
||||
end
|
||||
|
||||
function skin_class:get_texture()
|
||||
return self._texture
|
||||
end
|
||||
|
||||
function skin_class:set_hand(hand)
|
||||
self._hand = hand
|
||||
end
|
||||
|
||||
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 = {}
|
||||
for k,v in pairs(minetest.registered_items[""]) do
|
||||
if k ~= "mod_origin" and k ~= "type" and k ~= "wield_image" then
|
||||
hand_def[k] = v
|
||||
end
|
||||
end
|
||||
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
|
||||
minetest.register_node(hand, hand_def)
|
||||
self:set_hand(hand)
|
||||
end
|
||||
|
||||
function skin_class:get_hand()
|
||||
return self._hand
|
||||
end
|
||||
|
||||
function skin_class:set_preview(value)
|
||||
self._preview = value
|
||||
end
|
||||
|
||||
function skin_class:get_preview()
|
||||
if self._preview then
|
||||
return self._preview
|
||||
end
|
||||
|
||||
local player_skin = "("..self:get_texture()..")"
|
||||
local skin = ""
|
||||
|
||||
-- Consistent on both sizes:
|
||||
--Chest
|
||||
skin = skin .. "([combine:16x32:-16,-12=" .. player_skin .. "^[mask:skindb_mask_chest.png)^"
|
||||
--Head
|
||||
skin = skin .. "([combine:16x32:-4,-8=" .. player_skin .. "^[mask:skindb_mask_head.png)^"
|
||||
--Hat
|
||||
skin = skin .. "([combine:16x32:-36,-8=" .. player_skin .. "^[mask:skindb_mask_head.png)^"
|
||||
--Right Arm
|
||||
skin = skin .. "([combine:16x32:-44,-12=" .. player_skin .. "^[mask:skindb_mask_rarm.png)^"
|
||||
--Right Leg
|
||||
skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^"
|
||||
|
||||
-- 64x skins have non-mirrored arms and legs
|
||||
local left_arm
|
||||
local left_leg
|
||||
|
||||
if self:get_meta("format") == "1.8" then
|
||||
left_arm = "([combine:16x32:-24,-44=" .. player_skin .. "^[mask:(skindb_mask_rarm.png^[transformFX))^"
|
||||
left_leg = "([combine:16x32:-12,-32=" .. player_skin .. "^[mask:(skindb_mask_rleg.png^[transformFX))^"
|
||||
else
|
||||
left_arm = "([combine:16x32:-44,-12=" .. player_skin .. "^[mask:skindb_mask_rarm.png^[transformFX)^"
|
||||
left_leg = "([combine:16x32:0,0=" .. player_skin .. "^[mask:skindb_mask_rleg.png^[transformFX)^"
|
||||
end
|
||||
|
||||
-- Left Arm
|
||||
skin = skin .. left_arm
|
||||
--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))"
|
||||
|
||||
-- Full Preview
|
||||
skin = "(((" .. skin .. ")^[resize:64x128)^[mask:skindb_transform.png)"
|
||||
|
||||
return skin
|
||||
end
|
||||
|
||||
function skin_class:apply_skin_to_player(player)
|
||||
|
||||
local function concat_texture(base, ext)
|
||||
if base == "blank.png" then
|
||||
return ext
|
||||
elseif ext == "blank.png" then
|
||||
return base
|
||||
else
|
||||
return base .. "^" .. ext
|
||||
end
|
||||
end
|
||||
|
||||
local playername = player:get_player_name()
|
||||
local ver = self:get_meta("format") or "1.0"
|
||||
|
||||
player_api.set_model(player, "skinsdb_3d_armor_character_5.b3d")
|
||||
|
||||
local v10_texture = "blank.png"
|
||||
local v18_texture = "blank.png"
|
||||
local armor_texture = "blank.png"
|
||||
local wielditem_texture = "blank.png"
|
||||
|
||||
if ver == "1.8" then
|
||||
v18_texture = self:get_texture()
|
||||
else
|
||||
v10_texture = self:get_texture()
|
||||
end
|
||||
|
||||
-- Support for clothing
|
||||
if skins.clothing_loaded and clothing.player_textures[playername] then
|
||||
local cape = clothing.player_textures[playername].cape
|
||||
local layers = {}
|
||||
for k, v in pairs(clothing.player_textures[playername]) do
|
||||
if k ~= "skin" and k ~= "cape" then
|
||||
table.insert(layers, v)
|
||||
end
|
||||
end
|
||||
local overlay = table.concat(layers, "^")
|
||||
v10_texture = concat_texture(v10_texture, cape)
|
||||
v18_texture = concat_texture(v18_texture, overlay)
|
||||
end
|
||||
|
||||
-- Support for armor
|
||||
if skins.armor_loaded then
|
||||
local armor_textures = armor.textures[playername]
|
||||
if armor_textures then
|
||||
armor_texture = concat_texture(armor_texture, armor_textures.armor)
|
||||
wielditem_texture = concat_texture(wielditem_texture, armor_textures.wielditem)
|
||||
end
|
||||
end
|
||||
|
||||
player_api.set_textures(player, {
|
||||
v10_texture,
|
||||
v18_texture,
|
||||
armor_texture,
|
||||
wielditem_texture,
|
||||
})
|
||||
|
||||
player:set_properties({
|
||||
visual_size = {
|
||||
x = self:get_meta("visual_size_x") or 1,
|
||||
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)
|
||||
-- The set_skin is used on skins selection
|
||||
-- This means the method could be redefined to start an furmslec
|
||||
-- See character_creator for example
|
||||
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())
|
||||
end
|
201
mods/skinsdb/skinlist.lua
Normal file
|
@ -0,0 +1,201 @@
|
|||
local dbgprint = false and print or function() end
|
||||
|
||||
--- @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
|
||||
|
||||
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`
|
||||
]]
|
||||
|
||||
-- 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
|
||||
|
||||
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
|
||||
|
||||
local filename_noext = prefix .. sep .. identifier
|
||||
|
||||
dbgprint("Register skin", filename_noext, playername, sort_id)
|
||||
|
||||
-- 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
|
||||
|
||||
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)
|
||||
table.sort(skinslist, function(a,b)
|
||||
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_id < b_id
|
||||
else
|
||||
return (a:get_meta("name") or 'ZZ') < (b:get_meta("name") or 'ZZ')
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
-- (obsolete) get skinlist. If assignment given ("mod:wardrobe" or "player:bell07") select skins matches the assignment. select_unassigned selects the skins without any assignment too
|
||||
function skins.get_skinlist(assignment, select_unassigned)
|
||||
minetest.log("deprecated", "skins.get_skinlist() is deprecated. Use skins.get_skinlist_for_player() instead")
|
||||
local skinslist = {}
|
||||
for _, skin in pairs(skins.meta) do
|
||||
if not assignment or
|
||||
assignment == skin:get_meta("assignment") or
|
||||
(select_unassigned and skin:get_meta("assignment") == nil) then
|
||||
table.insert(skinslist, skin)
|
||||
end
|
||||
end
|
||||
skins_sort(skinslist)
|
||||
return skinslist
|
||||
end
|
||||
|
||||
-- Get skinlist for player. If no player given, public skins only selected
|
||||
function skins.get_skinlist_for_player(playername)
|
||||
local skinslist = {}
|
||||
for _, skin in pairs(skins.meta) do
|
||||
if skin:is_applicable_for_player(playername) and skin:get_meta("in_inventory_list") ~= false then
|
||||
table.insert(skinslist, skin)
|
||||
end
|
||||
end
|
||||
skins_sort(skinslist)
|
||||
return skinslist
|
||||
end
|
||||
|
||||
-- Get skinlist selected by metadata
|
||||
function skins.get_skinlist_with_meta(key, value)
|
||||
assert(key, "key parameter for skins.get_skinlist_with_meta() missed")
|
||||
local skinslist = {}
|
||||
for _, skin in pairs(skins.meta) do
|
||||
if skin:get_meta(key) == value then
|
||||
table.insert(skinslist, skin)
|
||||
end
|
||||
end
|
||||
skins_sort(skinslist)
|
||||
return skinslist
|
||||
end
|
151
mods/skinsdb/skins_updater.lua
Normal file
|
@ -0,0 +1,151 @@
|
|||
-- Skins update script
|
||||
|
||||
local S = minetest.get_translator("skinsdb")
|
||||
local _ID_ = "Lua Skins Updater"
|
||||
|
||||
local internal = {}
|
||||
internal.errors = {}
|
||||
|
||||
-- Binary downloads are required
|
||||
if not core.features.httpfetch_binary_data then
|
||||
internal.errors[#internal.errors + 1] =
|
||||
"Feature 'httpfetch_binary_data' is missing. Update Minetest."
|
||||
end
|
||||
|
||||
-- Insecure environment for saving textures and meta
|
||||
local ie, http = skins.ie, skins.http
|
||||
if not ie or not http then
|
||||
internal.errors[#internal.errors + 1] = "Insecure environment is required. " ..
|
||||
"Please add skinsdb to `secure.trusted_mods` in minetest.conf"
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("skinsdb_download_skins", {
|
||||
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)
|
||||
if #internal.errors > 0 then
|
||||
return false, "Cannot run " .. _ID_ .. ":\n\t" ..
|
||||
table.concat(internal.errors, "\n\t")
|
||||
end
|
||||
|
||||
local parts = string.split(param, " ")
|
||||
local start = tonumber(parts[1])
|
||||
local len = tonumber(parts[2])
|
||||
if not (start and len and len > 0) then
|
||||
return false, "Invalid page number or amount of pages"
|
||||
end
|
||||
|
||||
internal.get_pages_count(internal.fetch_function, start, len)
|
||||
return true, "Started downloading..."
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
if #internal.errors > 0 then
|
||||
return -- Nonsense to load something that's not working
|
||||
end
|
||||
|
||||
-- http://minetest.fensta.bplaced.net/api/apidoku.md
|
||||
local root_url = "http://skinsdb.terraqueststudios.net"
|
||||
local page_url = root_url .. "/api/v1/content?client=mod&page=%i" -- [1] = Page#
|
||||
|
||||
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)
|
||||
http.fetch({
|
||||
url = url,
|
||||
user_agent = _ID_
|
||||
}, function(result)
|
||||
if result.succeeded then
|
||||
if result.code ~= 200 then
|
||||
core.log("warning", ("%s: STATUS=%i URL=%s"):format(
|
||||
_ID_, result.code, url))
|
||||
end
|
||||
return callback(result.data)
|
||||
end
|
||||
core.log("warning", ("%s: Failed to download URL=%s"):format(
|
||||
_ID_, url))
|
||||
end)
|
||||
end
|
||||
|
||||
-- Insecure workaround since meta/ and textures/ cannot be written to
|
||||
local function unsafe_file_write(path, contents)
|
||||
local f = ie.io.open(path, "wb")
|
||||
f:write(contents)
|
||||
f:close()
|
||||
end
|
||||
|
||||
-- Takes a valid skin table from the Skins Database and saves it
|
||||
local function save_single_skin(skin)
|
||||
local meta = {
|
||||
skin.name,
|
||||
skin.author,
|
||||
skin.license
|
||||
}
|
||||
|
||||
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(
|
||||
meta_path .. name .. ".txt",
|
||||
table.concat(meta, "\n")
|
||||
)
|
||||
|
||||
unsafe_file_write(
|
||||
skins_path .. name .. ".png",
|
||||
core.decode_base64(skin.img)
|
||||
)
|
||||
core.log("action", ("%s: Completed skin %s"):format(_ID_, name))
|
||||
end
|
||||
|
||||
-- Get total pages since it'll just return the last page all over again
|
||||
internal.get_pages_count = function(callback, ...)
|
||||
local vars = {...}
|
||||
fetch_url(page_url:format(1) .. "&per_page=1", function(data)
|
||||
local list = core.parse_json(data)
|
||||
-- "per_page" defaults to 20 if left away (docs say something else, though)
|
||||
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)
|
||||
local end_page = math.min(start_page + len - 1, pages_total)
|
||||
|
||||
for page_n = start_page, end_page do
|
||||
local page_cpy = page_n
|
||||
fetch_url(page_url:format(page_n), function(data)
|
||||
core.log("action", ("%s: Page %i"):format(_ID_, page_cpy))
|
||||
|
||||
local list = core.parse_json(data)
|
||||
for i, skin in pairs(list.skins) do
|
||||
assert(skin.type == "image/png")
|
||||
assert(skin.id ~= "")
|
||||
|
||||
if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb
|
||||
save_single_skin(skin)
|
||||
end
|
||||
end
|
||||
|
||||
if page_cpy == end_page then
|
||||
local log = _ID_ .. " finished downloading all skins. " ..
|
||||
"Shutting down server to reload media cache"
|
||||
core.log("action", log)
|
||||
core.request_shutdown(log, true, 3 --[[give some time for pending requests]])
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
BIN
mods/skinsdb/textures/character_1.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
mods/skinsdb/textures/character_10.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
mods/skinsdb/textures/character_2.png
Normal file
After Width: | Height: | Size: 901 B |
BIN
mods/skinsdb/textures/character_3.png
Normal file
After Width: | Height: | Size: 1,018 B |
BIN
mods/skinsdb/textures/character_4.png
Normal file
After Width: | Height: | Size: 4 KiB |
BIN
mods/skinsdb/textures/character_5.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
mods/skinsdb/textures/character_6.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
mods/skinsdb/textures/character_7.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
mods/skinsdb/textures/character_8.png
Normal file
After Width: | Height: | Size: 783 B |
BIN
mods/skinsdb/textures/character_9.png
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
mods/skinsdb/textures/character_nora_flat.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
mods/skinsdb/textures/character_nora_half.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
33
mods/skinsdb/textures/readme.txt
Normal file
|
@ -0,0 +1,33 @@
|
|||
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
|
||||
^ The allowed characters in "[number or name]" are "[A-z0-9_.-]+".
|
||||
|
||||
One or multiple private skins for player "[nick]":
|
||||
player.[nick].png
|
||||
player.[nick].[number or name].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`.
|
BIN
mods/skinsdb/textures/skindb_mask_chest.png
Normal file
After Width: | Height: | Size: 114 B |
BIN
mods/skinsdb/textures/skindb_mask_head.png
Normal file
After Width: | Height: | Size: 139 B |
BIN
mods/skinsdb/textures/skindb_mask_rarm.png
Normal file
After Width: | Height: | Size: 118 B |
BIN
mods/skinsdb/textures/skindb_mask_rleg.png
Normal file
After Width: | Height: | Size: 118 B |
BIN
mods/skinsdb/textures/skindb_transform.png
Normal file
After Width: | Height: | Size: 191 B |
BIN
mods/skinsdb/textures/skins_button.png
Normal file
After Width: | Height: | Size: 250 B |
52
mods/skinsdb/unified_inventory_page.lua
Normal file
|
@ -0,0 +1,52 @@
|
|||
local S = minetest.get_translator("skinsdb")
|
||||
|
||||
unified_inventory.register_page("skins", {
|
||||
get_formspec = function(player, perplayer_formspec)
|
||||
local skin = skins.get_player_skin(player)
|
||||
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,
|
||||
})
|
||||
|
||||
unified_inventory.register_button("skins", {
|
||||
type = "image",
|
||||
image = "skins_button.png",
|
||||
tooltip = S("Skins"),
|
||||
})
|
||||
|
||||
local function get_formspec(player, perplayer_formspec)
|
||||
local context = skins.get_formspec_context(player)
|
||||
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, perplayer_formspec)
|
||||
return {formspec=get_formspec(player, perplayer_formspec)}
|
||||
end
|
||||
})
|
||||
|
||||
-- click button handlers
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if fields.skins then
|
||||
unified_inventory.set_inventory_formspec(player, "craft")
|
||||
return
|
||||
end
|
||||
|
||||
if formname ~= "" then
|
||||
return
|
||||
end
|
||||
|
||||
local context = skins.get_formspec_context(player)
|
||||
local action = skins.on_skin_selection_receive_fields(player, context, fields)
|
||||
if action == 'set' then
|
||||
unified_inventory.set_inventory_formspec(player, "skins")
|
||||
elseif action == 'page' then
|
||||
unified_inventory.set_inventory_formspec(player, "skins_page")
|
||||
end
|
||||
end)
|
51
mods/skinsdb/unittest.lua
Normal 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()
|
||||
|