Compare commits
	
		
			64 Commits
		
	
	
		
			8048cb08f1
			...
			contentdb
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | 8d5870c54c | ||
|  | bc69b03d0b | ||
|  | 247bcdd058 | ||
|  | a0ed992121 | ||
|  | bde2f1e3f2 | ||
|  | 3cf80c9272 | ||
|  | df62f2042d | ||
|  | b7cd514cea | ||
|  | 11bb5bad0e | ||
|  | 71f803e2fb | ||
|  | 312780c82e | ||
|  | 59f0dcaf3f | ||
|  | cd27e24b6f | ||
|  | 58739e9f9f | ||
|  | e1cd937470 | ||
|  | 616262de4b | ||
|  | 271c109073 | ||
|  | d3c7fa71c0 | ||
|  | c6f9a26970 | ||
|  | 353f36d551 | ||
|  | 1d1053dbc2 | ||
|  | d4b398eae2 | ||
|  | b49907c4b1 | ||
|  | 6db9a55f0c | ||
|  | ea7aacc9e7 | ||
|  | 743e2cfc9c | ||
|  | abc3df86e3 | ||
|  | c0b6f761dc | ||
|  | e275159dd0 | ||
|  | b769824d24 | ||
|  | 41524201d1 | ||
|  | 03d424fea7 | ||
|  | 41be19fc7a | ||
|  | c9fa5d92df | ||
|  | af5e6fcecc | ||
|  | 2e39651821 | ||
|  | ad189102d5 | ||
|  | e0765da839 | ||
|  | 482a13b495 | ||
|  | da820d06cb | ||
|  | cf712e5b54 | ||
|  | 56353133e1 | ||
|  | 443cda2183 | ||
|  | fab418b56f | ||
|  | c51905d44b | ||
|  | b3ea5a9e1f | ||
|  | e479e8e745 | ||
|  | 00f6a8137e | ||
|  | 716a9a3f9a | ||
|  | c53158d159 | ||
|  | da6905fd02 | ||
|  | 9b8fb79030 | ||
|  | 02323fc427 | ||
|  | 3aebed6fed | ||
|  | 63555ed0be | ||
|  | 7b59880736 | ||
|  | 8e1e525340 | ||
|  | af8212a1bd | ||
|  | 361d0222ca | ||
|  | 436809f8fa | ||
|  | 04b43a5969 | ||
|  | dcca582ef3 | ||
|  | f1d0f0789d | ||
|  | 5c13010014 | 
							
								
								
									
										11
									
								
								.github/workflows/build.yml
									
									
									
									
										vendored
									
									
										Normal 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
									
									
								
							
							
						
						| @@ -1,4 +1,4 @@ | |||||||
| textures/character_*.png | #textures/character_*.png | ||||||
| textures/player_*.png | textures/player_*.png | ||||||
| meta/character_*.txt | #meta/character_*.txt | ||||||
| meta/player_*.txt | meta/player_*.txt | ||||||
|   | |||||||
							
								
								
									
										14
									
								
								.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", | ||||||
|  | } | ||||||
							
								
								
									
										40
									
								
								API.md
									
									
									
									
									
								
							
							
						
						| @@ -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: | 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.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) | ## skins.new(key, object) | ||||||
| Create and register a new skin object for given key | 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 | 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) | ## skin:set_preview(texture) | ||||||
| Set the skin preview - usually at the init time only | 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() | Hook for dynamic skins updates on select. Is called in skins.set_player_skin() | ||||||
| In skinsdb the default implementation for this function is empty. | 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 | Apply the skin to the player. Called in skins.update_player_skin() to update visuals | ||||||
|  |  | ||||||
| ## skin:set_meta(key, value) | ## 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 |   - name - A name for the skin | ||||||
|   - author - The skin author |   - author - The skin author | ||||||
|   - license - THe skin texture license |   - 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 |   - 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 |   - 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) |   - _sort_id - Thi skins lists are sorted by this field for output (internal key) | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						| @@ -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. | 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) | - 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) | - 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 [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 | - Skin previews supported in selection | ||||||
| - Additional information for each skin | - Additional information for each skin | ||||||
| - Support for different skins lists: public and a per-player list are currently implemented | - 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 | - Full [3d_armor](https://forum.minetest.net/viewtopic.php?t=4654) support | ||||||
| - Compatible to 1.0 and 1.8 Minecraft skins format | - Compatible to 1.0 and 1.8 Minecraft skins format | ||||||
|  | - Skinned hand in 1st person view (1.0 skins only) | ||||||
|  |  | ||||||
|  |  | ||||||
| ## Installing skins | ## Installing skins | ||||||
|  |  | ||||||
| ### Download from the [database](http://minetest.fensta.bplaced.net/) | ### Download from the [database](https://skinsdb.terraqueststudios.net/) | ||||||
|  |  | ||||||
| #### Ingame Downloader | #### Ingame Downloader | ||||||
|  |  | ||||||
| 1) Get Minetest 5.1.0-dev-cb00632 or newer | 1) Get Minetest 5.1.0-dev-cb00632 or newer | ||||||
| 2) Start your world | 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) Run `/skinsdb_download_skins <skindb start page> <amount of pages>` | 3) Start your world | ||||||
| 4) Wait for the Minetest server to shut down | 4) Run `/skinsdb_download_skins <skindb start page> <amount of pages>` | ||||||
| 5) Start the server again | 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. | You might want to run `minetest` in a Terminal/Console window to check the log output instantly. | ||||||
|  |  | ||||||
| @@ -52,6 +55,7 @@ The Script will download all the skins from the database for you. | |||||||
| ## License: | ## License: | ||||||
| - GPLv3 | - GPLv3 | ||||||
| - skin texture licenses: See "meta" folder | - skin texture licenses: See "meta" folder | ||||||
|  | - hand model: CC0 | ||||||
|  |  | ||||||
| ### Credits | ### Credits | ||||||
|  |  | ||||||
| @@ -61,3 +65,5 @@ The Script will download all the skins from the database for you. | |||||||
| - Krock (source code) | - Krock (source code) | ||||||
| - bell07 (source code) | - bell07 (source code) | ||||||
| - stujones11 (player models) | - stujones11 (player models) | ||||||
|  | - jordan4ibanez (1st person view hand) | ||||||
|  | - sirrobzeroone (skins) | ||||||
|   | |||||||
							
								
								
									
										23
									
								
								api.lua
									
									
									
									
									
								
							
							
						
						| @@ -2,14 +2,26 @@ | |||||||
| local storage = minetest.get_mod_storage() | local storage = minetest.get_mod_storage() | ||||||
|  |  | ||||||
| function skins.get_player_skin(player) | function skins.get_player_skin(player) | ||||||
|  | 	local player_name = player:get_player_name() | ||||||
| 	local meta = player:get_meta() | 	local meta = player:get_meta() | ||||||
| 	if meta:get("skinsdb:skin_key") then | 	if meta:get("skinsdb:skin_key") then | ||||||
| 		-- Move player data prior July 2018 to mod storage | 		-- 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", "") | 		meta:set_string("skinsdb:skin_key", "") | ||||||
| 	end | 	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 | end | ||||||
|  |  | ||||||
| -- Assign skin to player | -- Assign skin to player | ||||||
| @@ -34,7 +46,7 @@ function skins.assign_player_skin(player, skin) | |||||||
| 	else | 	else | ||||||
| 		return false | 		return false | ||||||
| 	end | 	end | ||||||
| 	return true | 	return true, skin_obj | ||||||
| end | end | ||||||
|  |  | ||||||
| -- update visuals | -- update visuals | ||||||
| @@ -53,10 +65,11 @@ end | |||||||
|  |  | ||||||
| -- Assign and update - should be used on selection externally | -- Assign and update - should be used on selection externally | ||||||
| function skins.set_player_skin(player, skin) | 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 | 	if success then | ||||||
| 		skins.get_player_skin(player):set_skin(player) | 		skins.get_player_skin(player):set_skin(player) | ||||||
| 		skins.update_player_skin(player) | 		skins.update_player_skin(player) | ||||||
|  | 		minetest.log("action", player:get_player_name().." set skin to "..skin_obj:get_key("")) | ||||||
| 	end | 	end | ||||||
| 	return success | 	return success | ||||||
| end | end | ||||||
|   | |||||||
| @@ -11,7 +11,7 @@ end | |||||||
|  |  | ||||||
|  |  | ||||||
| minetest.register_chatcommand("skinsdb", { | 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"), | 	description = S("Show, list or set player's skin"), | ||||||
| 	func = function(name, param) | 	func = function(name, param) | ||||||
| 		local player = minetest.get_player_by_name(name) | 		local player = minetest.get_player_by_name(name) | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| local S = minetest.get_translator("skinsdb") | local S = minetest.get_translator("skinsdb") | ||||||
|  | local ui = minetest.global_exists("unified_inventory") and unified_inventory | ||||||
|  |  | ||||||
| function skins.get_formspec_context(player) | function skins.get_formspec_context(player) | ||||||
| 	if player then | 	if player then | ||||||
| @@ -11,7 +12,7 @@ function skins.get_formspec_context(player) | |||||||
| end | end | ||||||
|  |  | ||||||
| -- Show skin info | -- 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 texture = skin:get_texture() | ||||||
| 	local m_name = skin:get_meta_string("name") | 	local m_name = skin:get_meta_string("name") | ||||||
| 	local m_author = skin:get_meta_string("author") | 	local m_author = skin:get_meta_string("author") | ||||||
| @@ -20,30 +21,82 @@ function skins.get_skin_info_formspec(skin) | |||||||
| 	-- overview page | 	-- overview page | ||||||
| 	local raw_size = m_format == "1.8" and "2,2" or "2,1" | 	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 | 	if texture then | ||||||
| 		formspec = formspec.."label[6,.5;"..S("Raw texture")..":]" | 		formspec = formspec.."label["..rxoffs..",.5;"..S("Raw texture")..":]" | ||||||
| 		.."image[6,1;"..raw_size..";"..texture.."]" | 		.."image["..rxoffs..",1;"..raw_size..";"..texture.."]" | ||||||
| 	end | 	end | ||||||
| 	if m_name ~= "" then | 	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 | 	end | ||||||
| 	if m_author ~= "" then | 	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 | 	end | ||||||
| 	if m_license ~= "" then | 	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 | 	end | ||||||
| 	return formspec | 	return formspec | ||||||
| end | 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.skins_list = skins.get_skinlist_for_player(player:get_player_name()) | ||||||
| 	context.total_pages = 1 | 	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 | 	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", 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 | 		context.total_pages = page | ||||||
| 	end | 	end | ||||||
| 	context.skins_page = context.skins_page or skins.get_player_skin(player):get_meta("inv_page") or 1 | 	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 page = context.skins_page | ||||||
| 	local formspec = "" | 	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] | 		local skin = context.skins_list[i] | ||||||
| 		if not skin then | 		if not skin then | ||||||
| 			break | 			break | ||||||
| 		end | 		end | ||||||
|  |  | ||||||
| 		local index_p = skin:get_meta("inv_page_index") | 		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 | 		local y | ||||||
| 		if index_p > 8 then | 		if index_p > 8 then | ||||||
| 			y = y_delta + 1.9 | 			y = yoffs + yspc | ||||||
| 		else | 		else | ||||||
| 			y = y_delta | 			y = yoffs | ||||||
| 		end | 		end | ||||||
| 		formspec = formspec.."image_button["..x..","..y..";1,2;".. | 		formspec = formspec.. | ||||||
| 			minetest.formspec_escape(skin:get_preview())..";skins_set$"..i..";]".. | 			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")).."]" | 			"tooltip[skins_set$"..i..";"..minetest.formspec_escape(skin:get_meta_string("name")).."]" | ||||||
| 	end | 	end | ||||||
|  |  | ||||||
| @@ -87,10 +143,13 @@ function skins.get_skin_selection_formspec(player, context, y_delta) | |||||||
| 			if pg > 1 then page_list = page_list.."," end | 			if pg > 1 then page_list = page_list.."," end | ||||||
| 			page_list = page_list..pagename | 			page_list = page_list..pagename | ||||||
| 		end | 		end | ||||||
| 		formspec = formspec | 		formspec = formspec.. | ||||||
| 			.."button[0,"..(y_delta+4.0)..";1,.5;skins_page$"..page_prev..";<<]" | 			string.format("button[%f,%f;%f,%f;skins_page$%i;<<]", | ||||||
| 			.."dropdown[0.9,"..(y_delta+3.88)..";6.5,.5;skins_selpg;"..page_list..";"..page.."]" | 				xoffs, btn_y, btn_width, ctrls_height, page_prev).. | ||||||
| 			.."button[7,"..(y_delta+4.0)..";1,.5;skins_page$"..page_next..";>>]" | 			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 | 	end | ||||||
| 	return formspec | 	return formspec | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										32
									
								
								init.lua
									
									
									
									
									
								
							
							
						
						| @@ -63,8 +63,16 @@ end) | |||||||
|  |  | ||||||
| minetest.register_on_leaveplayer(function(player) | minetest.register_on_leaveplayer(function(player) | ||||||
| 	skins.ui_context[player:get_player_name()] = nil | 	skins.ui_context[player:get_player_name()] = nil | ||||||
|  | 	player:get_inventory():set_size("hand", 0) | ||||||
| end) | 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", { | player_api.register_model("skinsdb_3d_armor_character_5.b3d", { | ||||||
| 	animation_speed = 30, | 	animation_speed = 30, | ||||||
| 	textures = { | 	textures = { | ||||||
| @@ -75,12 +83,21 @@ player_api.register_model("skinsdb_3d_armor_character_5.b3d", { | |||||||
| 	}, | 	}, | ||||||
| 	animations = { | 	animations = { | ||||||
| 		stand = {x=0, y=79}, | 		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}, | 		walk = {x=168, y=187}, | ||||||
| 		mine = {x=189, y=198}, | 		mine = {x=189, y=198}, | ||||||
| 		walk_mine = {x=200, y=219}, | 		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 | -- 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("format", '1.0') | ||||||
| 	default_skin_obj:set_meta("_sort_id", 0) | 	default_skin_obj:set_meta("_sort_id", 0) | ||||||
| 	default_skin_obj:set_meta("name", "Sam") | 	default_skin_obj:set_meta("name", "Sam") | ||||||
|  | 	default_skin_obj:set_hand_from_texture() | ||||||
| end | 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") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,19 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
| # Translation by Xanthin | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
|  |  | ||||||
| Raw texture=Rohtextur |  | ||||||
| Name=Name |  | ||||||
| Author=Autor |  | ||||||
| Change=Wechseln |  | ||||||
| Page=Seite |  | ||||||
| License=Lizenz |  | ||||||
| Description=Beschreibung |  | ||||||
| Show, list or set player's skin=Anzeigen oder setzen der Spieler-Skins | Show, list or set player's skin=Anzeigen oder setzen der Spieler-Skins | ||||||
| Player not found=Spieler nicht da | Player not found=Spieler nicht da | ||||||
| unknown command=unbekannter Befehl | unknown command=unbekannter Befehl | ||||||
| see /help skinsdb for supported parameters=Lese /help für erlaubte Parameter | see /help skinsdb for supported parameters=Lese /help für erlaubte Parameter | ||||||
| skin set to=Skin ist jetzt | skin set to=Skin ist jetzt | ||||||
| invalid skin=unbekannter Skin | invalid skin=unbekannter Skin | ||||||
| unknown parameter=unbekannter Parameter |  | ||||||
| Skins=Aussehen |  | ||||||
| Requires skin key=Benötigt Skin-Name | 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
									
								
							
							
						
						| @@ -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 | ||||||
| @@ -1,18 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
|  | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
| Raw texture=Texture |  | ||||||
| Name=Nom |  | ||||||
| Author=Auteur |  | ||||||
| Change=Changer |  | ||||||
| Page=Page |  | ||||||
| License=Licence |  | ||||||
| Description=Description |  | ||||||
| Show, list or set player's skin=Afficher, lister ou définir le skin du joueur | Show, list or set player's skin=Afficher, lister ou définir le skin du joueur | ||||||
| Player not found=Joueur non trouvé | Player not found=Joueur non trouvé | ||||||
| unknown command=commande inconnue | unknown command=commande inconnue | ||||||
| see /help skinsdb for supported parameters=voir /help skinsdb pour les paramètres supportés | see /help skinsdb for supported parameters=voir /help skinsdb pour les paramètres supportés | ||||||
| skin set to=skin définie sur | skin set to=skin définie sur | ||||||
| invalid skin=skin peau invalide | invalid skin=skin peau invalide | ||||||
|  | Requires skin key= | ||||||
| unknown parameter=paramètre inconnu | 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= | ||||||
|   | |||||||
| @@ -1,18 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
| # Malay translation by muhdnurhidayat | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
|  |  | ||||||
| Raw texture=Tekstur mentah |  | ||||||
| Name=Nama |  | ||||||
| Author=Pencipta |  | ||||||
| Change=Ubah |  | ||||||
| Page=Halaman |  | ||||||
| License=Lesen |  | ||||||
| Description=Keterangan |  | ||||||
| Show, list or set player's skin=Tunjukkan, senaraikan atau tetapkan kulit pemain | Show, list or set player's skin=Tunjukkan, senaraikan atau tetapkan kulit pemain | ||||||
| Player not found=Pemain tidak dijumpai | Player not found=Pemain tidak dijumpai | ||||||
| unknown command=perintah tidak diketahui | unknown command=perintah tidak diketahui | ||||||
| see /help skinsdb for supported parameters=lihat /help skinsdb untuk parameter yang disokong | see /help skinsdb for supported parameters=lihat /help skinsdb untuk parameter yang disokong | ||||||
| skin set to=kulit ditetapkan kepada | skin set to=kulit ditetapkan kepada | ||||||
| invalid skin=kulit tidak sah | invalid skin=kulit tidak sah | ||||||
|  | Requires skin key= | ||||||
| unknown parameter=parameter tidak diketahui | 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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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
									
								
							
							
						
						| @@ -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=Скіни | ||||||
| @@ -1,20 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
| #Translation by IFRFSX(BingFengFSX) | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
|  |  | ||||||
| Raw texture=自然状态的纹理 |  | ||||||
| Name=名称 |  | ||||||
| Author=作者 |  | ||||||
| Change=更换 |  | ||||||
| Page=页面 |  | ||||||
| License=许可证 |  | ||||||
| Description=说明 |  | ||||||
| Show, list or set player's skin=显示,列出或者设置玩家的皮肤 | Show, list or set player's skin=显示,列出或者设置玩家的皮肤 | ||||||
| Player not found=玩家未找到 | Player not found=玩家未找到 | ||||||
| unknown command=未知命令 | unknown command=未知命令 | ||||||
| see /help skinsdb for supported parameters=有关skinsdb支持的参数,参见 /help | see /help skinsdb for supported parameters=有关skinsdb支持的参数,参见 /help | ||||||
| skin set to=皮肤设置为 | skin set to=皮肤设置为 | ||||||
| invalid skin=无效皮肤 | invalid skin=无效皮肤 | ||||||
|  | Requires skin key= | ||||||
| unknown parameter=未知参数 | 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=下载指定范围的皮肤并关闭服务器 | Downloads the specified range of skins and shuts down the server=下载指定范围的皮肤并关闭服务器 | ||||||
|  | Change=更换 | ||||||
| Skins=皮肤 | Skins=皮肤 | ||||||
|   | |||||||
| @@ -1,19 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
| #Translation by IFRFSX(BingFengFSX) | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
| Raw texture=自然狀態的紋理 | Show, list or set player's skin=顯示,列出或者設定玩家的皮膚 | ||||||
| Name=名稱 |  | ||||||
| Author=作者 |  | ||||||
| Change=更換 |  | ||||||
| Page=頁面 |  | ||||||
| License=許可證 |  | ||||||
| Description=說明 |  | ||||||
| Show,list or set player's skin=顯示,列出或者設定玩家的皮膚 |  | ||||||
| Player not found=玩家未找到 | Player not found=玩家未找到 | ||||||
| unknown command=未知命令 | unknown command=未知命令 | ||||||
| see /help skinsdb for supported parameters=有關skinsdb支持的參數,參見/help | see /help skinsdb for supported parameters=有關skinsdb支持的參數,參見/help | ||||||
| skin set to=皮膚設定為 | skin set to=皮膚設定為 | ||||||
| invalid skin=無效皮膚 | invalid skin=無效皮膚 | ||||||
|  | Requires skin key= | ||||||
| unknown parameter=未知參數 | 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=下載指定範圍的皮膚並關閉服務器 | Downloads the specified range of skins and shuts down the server=下載指定範圍的皮膚並關閉服務器 | ||||||
|  | Change=更換 | ||||||
| Skins=皮膚 | Skins=皮膚 | ||||||
|   | |||||||
| @@ -1,20 +1,19 @@ | |||||||
| # textdomain: skinsdb | # textdomain: skinsdb | ||||||
|  | [set] <skin key> | show [<skin key>] | list | list private | list public | [ui]= | ||||||
| Raw texture= |  | ||||||
| Name= |  | ||||||
| Author= |  | ||||||
| Change= |  | ||||||
| Page= |  | ||||||
| License= |  | ||||||
| Description= |  | ||||||
| Show, list or set player's skin= | Show, list or set player's skin= | ||||||
| Player not found= | Player not found= | ||||||
| unknown command= | unknown command= | ||||||
| see /help skinsdb for supported parameters= | see /help skinsdb for supported parameters= | ||||||
| skin set to= | skin set to= | ||||||
| invalid skin= | invalid skin= | ||||||
| unknown parameter= |  | ||||||
| unknown skin= |  | ||||||
| Downloads the specified range of skins and shuts down the server= |  | ||||||
| Skins= |  | ||||||
| Requires skin key= | 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
									
								
								meta/character_castaway_female.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Castaway female | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_castaway_male.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Castaway male | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_farmer_female.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Farmer female | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_farmer_male.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Farmer male | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_prince.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Prince | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_princess.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Princess | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_rogue_female.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Rogue female | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								meta/character_rogue_male.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,3 @@ | |||||||
|  | Rogue male | ||||||
|  | sirrobzeroone | ||||||
|  | CC0/Public Domain | ||||||
							
								
								
									
										3
									
								
								mod.conf
									
									
									
									
									
								
							
							
						
						| @@ -1,4 +1,5 @@ | |||||||
| name = skinsdb | name = skinsdb | ||||||
| description = Player skin mod, supporting unified_inventory, sfinv and smart_inventory | description = Player skin mod, supporting unified_inventory, sfinv and smart_inventory | ||||||
| depends = player_api | 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
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/skinsdb_hand.blend
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/skinsdb_hand_18.b3d
									
									
									
									
									
										Normal file
									
								
							
							
						
						
							
								
								
									
										
											BIN
										
									
								
								models/skinsdb_hand_18.blend
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -1,5 +1,7 @@ | |||||||
| skins.meta = {} | skins.meta = {} | ||||||
|  |  | ||||||
|  | local has_hand_monoid = minetest.get_modpath("hand_monoid") | ||||||
|  |  | ||||||
| local skin_class = {} | local skin_class = {} | ||||||
| skin_class.__index = skin_class | skin_class.__index = skin_class | ||||||
| skins.skin_class = skin_class | skins.skin_class = skin_class | ||||||
| @@ -47,10 +49,70 @@ function skin_class:set_texture(value) | |||||||
| 	self._texture = value | 	self._texture = value | ||||||
| end | end | ||||||
|  |  | ||||||
|  | --- Retrieves the character texture | ||||||
| function skin_class:get_texture() | function skin_class:get_texture() | ||||||
| 	return self._texture | 	return self._texture | ||||||
| end | 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) | function skin_class:set_preview(value) | ||||||
| 	self._preview = value | 	self._preview = value | ||||||
| end | end | ||||||
| @@ -75,7 +137,7 @@ function skin_class:get_preview() | |||||||
| 	--Right Leg | 	--Right Leg | ||||||
| 	skin = skin .. "([combine:16x32:0,0=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^" | 	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_arm | ||||||
| 	local left_leg | 	local left_leg | ||||||
|  |  | ||||||
| @@ -92,17 +154,21 @@ function skin_class:get_preview() | |||||||
| 	--Left Leg | 	--Left Leg | ||||||
| 	skin = skin .. left_leg | 	skin = skin .. left_leg | ||||||
|  |  | ||||||
| 	-- Add overlays for 64x skins. these wont appear if skin is 32x because it will be cropped out | 	if self:get_meta("format") == "1.8" then | ||||||
| 	--Chest Overlay | 		-- Add overlays for 64x64 skins. This check is needed to avoid | ||||||
| 	skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:skindb_mask_chest.png)^" | 		-- client-side out-of-bounds "[combine" warnings. | ||||||
| 	--Right Arm Overlay |  | ||||||
| 	skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:skindb_mask_rarm.png)^" | 		--Chest Overlay | ||||||
| 	--Right Leg Overlay | 		skin = skin .. "([combine:16x32:-16,-28=" .. player_skin .. "^[mask:skindb_mask_chest.png)^" | ||||||
| 	skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^" | 		--Right Arm Overlay | ||||||
| 	--Left Arm Overlay | 		skin = skin .. "([combine:16x32:-44,-28=" .. player_skin .. "^[mask:skindb_mask_rarm.png)^" | ||||||
| 	skin = skin .. "([combine:16x32:-40,-44=" .. player_skin .. "^[mask:(skindb_mask_rarm.png^[transformFX))^" | 		--Right Leg Overlay | ||||||
| 	--Left Leg Overlay | 		skin = skin .. "([combine:16x32:0,-16=" .. player_skin .. "^[mask:skindb_mask_rleg.png)^" | ||||||
| 	skin = skin .. "([combine:16x32:4,-32=" .. player_skin .. "^[mask:(skindb_mask_rleg.png^[transformFX))" | 		--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 | 	-- Full Preview | ||||||
| 	skin = "(((" .. skin .. ")^[resize:64x128)^[mask:skindb_transform.png)" | 	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 | 			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 | end | ||||||
|  |  | ||||||
| function skin_class:set_skin(player) | function skin_class:set_skin(player) | ||||||
| @@ -185,6 +267,6 @@ end | |||||||
| function skin_class:is_applicable_for_player(playername) | function skin_class:is_applicable_for_player(playername) | ||||||
| 	local assigned_player = self:get_meta("playername") | 	local assigned_player = self:get_meta("playername") | ||||||
| 	return assigned_player == nil or assigned_player == true or | 	return assigned_player == nil or assigned_player == true or | ||||||
|  			playername and (minetest.check_player_privs(playername, {server=true}) or | 		playername and (minetest.check_player_privs(playername, {server=true}) or | ||||||
| 			assigned_player:lower() == playername:lower()) | 		assigned_player:lower() == playername:lower()) | ||||||
| end | end | ||||||
|   | |||||||
							
								
								
									
										196
									
								
								skinlist.lua
									
									
									
									
									
								
							
							
						
						| @@ -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 | --- @param path     Path to the "textures" directory, without tailing slash. | ||||||
| 	local name, sort_id, assignment, is_preview, playername | --- @param filename Current file name, such as "player.groot.17.png". | ||||||
| 	local nameparts = string.gsub(fn, "[.]", "_"):split("_") | --- @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 | 	local prefix, sep, identifier, extension = filename:match("^(%a+)([_.])([%w_.-]+)%.(%a+)$") | ||||||
| 	if (nameparts[1] == 'player' or nameparts[1] == 'character') and | 	--[[ | ||||||
| 			nameparts[#nameparts]:lower() == 'png' then | 		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 | 	-- Filter out files that do not match the allowed patterns | ||||||
| 		table.remove(nameparts, #nameparts) | 	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 | 	local preview_suffix = sep .. "preview" | ||||||
| 		if nameparts[#nameparts] == 'preview' then | 	if identifier:sub(-#preview_suffix) == preview_suffix then | ||||||
| 			is_preview = true | 		-- The preview texture is added by the main skin texture (if exists) | ||||||
| 			table.remove(nameparts, #nameparts) | 		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 | 		end | ||||||
|  | 	else -- Public skin "character*" | ||||||
|  | 		-- Less priority | ||||||
|  | 		sort_id = 5000 + (tonumber(identifier) or 0) | ||||||
|  | 	end | ||||||
|  |  | ||||||
| 		-- Build technically skin name | 	local filename_noext = prefix .. sep .. identifier | ||||||
| 		name = table.concat(nameparts, '_') |  | ||||||
|  |  | ||||||
| 		-- Handle metadata from file name | 	dbgprint("Register skin", filename_noext, playername, sort_id) | ||||||
| 		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 |  | ||||||
|  |  | ||||||
| 			-- Get sort index | 	-- Register skin texture | ||||||
| 			if tonumber(nameparts[#nameparts]) then | 	local skin_obj = skins.get(filename_noext) or skins.new(filename_noext) | ||||||
| 				sort_id = sort_id + nameparts[#nameparts] | 	skin_obj:set_texture(filename) | ||||||
| 			end | 	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 | ||||||
|  | 	end | ||||||
|  |  | ||||||
| 		local skin_obj = skins.get(name) or skins.new(name) | 	if path then | ||||||
| 		if is_preview then | 		-- Optional preview texture | ||||||
| 			skin_obj:set_preview(fn) | 		local preview_name = filename_noext .. sep .. "preview.png" | ||||||
| 		else | 		local fh = io.open(path .. "/" .. preview_name) | ||||||
| 			skin_obj:set_texture(fn) | 		if fh then | ||||||
| 			skin_obj:set_meta("_sort_id", sort_id) | 			dbgprint("Found preview", preview_name) | ||||||
| 			if playername then | 			skin_obj:set_preview(preview_name) | ||||||
| 				skin_obj:set_meta("assignment", "player:"..playername) | 			fh:close() -- do not rely on delayed GC | ||||||
| 				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 |  | ||||||
| 		end | 		end | ||||||
| 	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 | end | ||||||
|  |  | ||||||
| local function skins_sort(skinslist) | 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 a_id = a:get_meta("_sort_id") or 10000 | ||||||
| 		local b_id = b:get_meta("_sort_id") or 10000 | 		local b_id = b:get_meta("_sort_id") or 10000 | ||||||
| 		if a_id ~= b_id then | 		if a_id ~= b_id then | ||||||
| 			return a:get_meta("_sort_id") < b:get_meta("_sort_id") | 			return a_id < b_id | ||||||
| 		else | 		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) | 	end) | ||||||
| end | end | ||||||
|   | |||||||
| @@ -20,7 +20,7 @@ if not ie or not http then | |||||||
| end | end | ||||||
|  |  | ||||||
| minetest.register_chatcommand("skinsdb_download_skins", { | 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"), | 	description = S("Downloads the specified range of skins and shuts down the server"), | ||||||
| 	privs = {server=true}, | 	privs = {server=true}, | ||||||
| 	func = function(name, param) | 	func = function(name, param) | ||||||
| @@ -47,13 +47,12 @@ if #internal.errors > 0 then | |||||||
| end | end | ||||||
|  |  | ||||||
| -- http://minetest.fensta.bplaced.net/api/apidoku.md | -- http://minetest.fensta.bplaced.net/api/apidoku.md | ||||||
| local root_url = "http://minetest.fensta.bplaced.net" | local root_url = "http://skinsdb.terraqueststudios.net" | ||||||
| local page_url = root_url .. "/api/v2/get.json.php?getlist&page=%i&outformat=base64" -- [1] = Page# | local page_url = root_url .. "/api/v1/content?client=mod&page=%i" -- [1] = Page# | ||||||
| local preview_url = root_url .. "/skins/1/%i.png" -- [1] = ID |  | ||||||
|  |  | ||||||
| local mod_path = skins.modpath | local download_path = skins.modpath | ||||||
| local meta_path = mod_path .. "/meta/" | local meta_path = download_path .. "/meta/" | ||||||
| local skins_path = mod_path .. "/textures/" | local skins_path = download_path .. "/textures/" | ||||||
|  |  | ||||||
| -- Fancy debug wrapper to download an URL | -- Fancy debug wrapper to download an URL | ||||||
| local function fetch_url(url, callback) | local function fetch_url(url, callback) | ||||||
| @@ -81,14 +80,22 @@ local function unsafe_file_write(path, contents) | |||||||
| end | end | ||||||
|  |  | ||||||
| -- Takes a valid skin table from the Skins Database and saves it | -- 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 = { | 	local meta = { | ||||||
| 		skin.name, | 		skin.name, | ||||||
| 		skin.author, | 		skin.author, | ||||||
| 		skin.license | 		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 | 	-- core.safe_file_write does not work here | ||||||
| 	unsafe_file_write( | 	unsafe_file_write( | ||||||
| @@ -100,9 +107,6 @@ local function safe_single_skin(skin) | |||||||
| 		skins_path .. name .. ".png", | 		skins_path .. name .. ".png", | ||||||
| 		core.decode_base64(skin.img) | 		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)) | 	core.log("action", ("%s: Completed skin %s"):format(_ID_, name)) | ||||||
| end | end | ||||||
|  |  | ||||||
| @@ -132,7 +136,7 @@ internal.fetch_function = function(pages_total, start_page, len) | |||||||
| 				assert(skin.id ~= "") | 				assert(skin.id ~= "") | ||||||
|  |  | ||||||
| 				if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb | 				if skin.id ~= 1 then -- Skin 1 is bundled with skinsdb | ||||||
| 					safe_single_skin(skin) | 					save_single_skin(skin) | ||||||
| 				end | 				end | ||||||
| 			end | 			end | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								textures/character_castaway_female.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.6 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_castaway_male.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_farmer_female.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.4 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_farmer_male.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 2.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_prince.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_princess.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.7 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_rogue_female.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 1.3 KiB | 
							
								
								
									
										
											BIN
										
									
								
								textures/character_rogue_male.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						| After Width: | Height: | Size: 3.9 KiB | 
| @@ -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: | 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": | One or multiple private skins for player "[nick]": | ||||||
| 	player_[nick].png or | 	player.[nick].png | ||||||
| 	player_[nick]_[number-or-name].png | 	player.[nick].[number or name].png | ||||||
|  |  | ||||||
| Preview files for public and private skins. | Skin previews for public and private skins: | ||||||
| Optional, overrides the generated preview | 	character.[number or name].preview.png | ||||||
| 	character_*_preview.png or | 	player.[nick].preview.png | ||||||
| 	player_*_*_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`. | ||||||
|   | |||||||
| Before Width: | Height: | Size: 6.0 KiB | 
| @@ -1,10 +1,13 @@ | |||||||
| local S = minetest.get_translator("skinsdb") | local S = minetest.get_translator("skinsdb") | ||||||
|  |  | ||||||
| unified_inventory.register_page("skins", { | unified_inventory.register_page("skins", { | ||||||
| 	get_formspec = function(player) | 	get_formspec = function(player, perplayer_formspec) | ||||||
| 		local skin = skins.get_player_skin(player) | 		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).. | 		local boffs = (type(perplayer_formspec) == "table") and 2 or 0.75 | ||||||
| 				"button[.75,3;6.5,.5;skins_page;"..S("Change").."]" |  | ||||||
|  | 		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} | 		return {formspec=formspec} | ||||||
| 	end, | 	end, | ||||||
| }) | }) | ||||||
| @@ -15,16 +18,16 @@ unified_inventory.register_button("skins", { | |||||||
| 	tooltip = S("Skins"), | 	tooltip = S("Skins"), | ||||||
| }) | }) | ||||||
|  |  | ||||||
| local function get_formspec(player) | local function get_formspec(player, perplayer_formspec) | ||||||
| 	local context = skins.get_formspec_context(player) | 	local context = skins.get_formspec_context(player) | ||||||
| 	local formspec = "background[0.06,0.99;7.92,7.52;ui_misc_form.png]".. | 	local formspec = perplayer_formspec.standard_inv_bg.. | ||||||
| 			skins.get_skin_selection_formspec(player, context, -0.2) | 			skins.get_skin_selection_formspec(player, context, perplayer_formspec) | ||||||
| 	return formspec | 	return formspec | ||||||
| end | end | ||||||
|  |  | ||||||
| unified_inventory.register_page("skins_page", { | unified_inventory.register_page("skins_page", { | ||||||
| 	get_formspec = function(player) | 	get_formspec = function(player, perplayer_formspec) | ||||||
| 		return {formspec=get_formspec(player)} | 		return {formspec=get_formspec(player, perplayer_formspec)} | ||||||
| 	end | 	end | ||||||
| }) | }) | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										51
									
								
								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() | ||||||
|  |  | ||||||
| @@ -1,11 +1,9 @@ | |||||||
| import sys, requests, base64 | import os.path, sys, requests, base64 | ||||||
|  |  | ||||||
| download_preview = ( len (sys.argv) > 1 and sys.argv[1] == "with_preview" ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| 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 | # 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: | if r.status_code != 200: | ||||||
|     sys.exit("Request failed!") |     sys.exit("Request failed!") | ||||||
| @@ -13,42 +11,33 @@ if r.status_code != 200: | |||||||
| data = r.json() | data = r.json() | ||||||
| count = 0 | count = 0 | ||||||
|  |  | ||||||
| if download_preview: | print("Writing skins") | ||||||
|     print("Writing to file and downloading previews ...") |  | ||||||
| else: |  | ||||||
|     print("Writing skins") |  | ||||||
|  |  | ||||||
|  |  | ||||||
| for json in data["skins"]: | for json in data["skins"]: | ||||||
|     id = str(json["id"]) |     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 |     # Texture file | ||||||
|     raw_data = base64.b64decode(json["img"]) |     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.write(bytearray(raw_data)) | ||||||
|     file.close() |     file.close() | ||||||
|  |  | ||||||
|     # Meta file |     # Meta file | ||||||
|     name = str(json["name"]) |     meta_name = str(json["name"]) | ||||||
|     author = str(json["author"]) |     meta_author = str(json["author"]) | ||||||
|     license = str(json["license"]) |     meta_license = str(json["license"]) | ||||||
|     file = open("../meta/character_" + id + ".txt", "w") |     file = open("../meta/" + name + ".txt", "w") | ||||||
|     file.write(name + "\n" + author + "\n" + license + "\n") |     file.write(meta_name + "\n" + meta_author + "\n" + meta_license + "\n") | ||||||
|     file.close() |     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 |     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!") | print("Fetched " + str(count) + " skins!") | ||||||
|   | |||||||