14 Commits

Author SHA1 Message Date
1fcd3c4a8c Add python skin update script (#48) 2020-06-07 13:39:57 +02:00
55704e1657 fix chrash when using /skinsdb set without parameter or /skinsdb list public 2020-05-25 12:15:09 +02:00
ec30267223 Fix corrupted data in Windows systems (#47)
The base64 data from the decoder to the file writer is in binary, using only the "w" flag would work in most unix systems but its incorrect and for all systems with crazy line_feed/Carrier_return or any character conversion the right way to save a binary data is using the "wb" flag.
2020-05-23 08:56:30 +02:00
55dc55f2dd revert texture name parsing change 2020-02-12 08:09:17 +01:00
ada930c099 translate unified_inventory button mouseover text 2020-02-08 19:12:09 +01:00
918df53f3b Autogenerate skin previews and other @GreenDimond changes 2020-02-08 19:12:09 +01:00
664a4a8d81 Add traditional Chinese translation 2020-02-08 18:37:15 +01:00
9525b87c4d move dependencies to mod.conf 2020-01-16 16:50:57 +01:00
abb5422948 Update skinsdb.zh_CN.tr 2020-01-15 07:25:33 +01:00
aa82f804b8 add translation 2020-01-15 07:25:33 +01:00
165c20fcad fix translation for unified_inventory
Take sfinv "Skins" page name translateable
2020-01-14 14:57:45 +01:00
555d682f04 remove MT-0.4 support 2020-01-08 08:07:20 +01:00
e5714f272c fix missed adjustments for MT5 game translation 2020-01-08 08:00:44 +01:00
fa92207f14 MT 5 game translation (#37) 2020-01-08 07:45:05 +01:00
36 changed files with 286 additions and 184 deletions

View File

@ -18,7 +18,9 @@ This Minetest mod offers changeable player skins with a graphical interface for
## Installing skins ## Installing skins
### Download from the database ### Download from the [database](http://minetest.fensta.bplaced.net/)
#### 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) Start your world
@ -28,6 +30,16 @@ This Minetest mod offers changeable player skins with a graphical interface for
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.
#### 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 ### Manual addition
1) Copy your skin textures to `textures` as documented in `textures/readme.txt` 1) Copy your skin textures to `textures` as documented in `textures/readme.txt`

View File

@ -1,4 +1,4 @@
local S = skins.S local S = minetest.get_translator("skinsdb")
local function show_selection_formspec(player) local function show_selection_formspec(player)
local context = skins.get_formspec_context(player) local context = skins.get_formspec_context(player)
@ -36,11 +36,14 @@ minetest.register_chatcommand("skinsdb", {
end end
if command == "set" then if command == "set" then
local success = skins.set_player_skin(player, parameter) if parameter then
if success then local success = skins.set_player_skin(player, parameter)
return true, S("skin set to").." "..parameter if success then
else return true, S("skin set to").." "..parameter
return false, S("invalid skin").." "..parameter else
return false, S("invalid skin").." "..parameter
end
else return false, S("Requires skin key")
end end
elseif command == "list" then elseif command == "list" then
local list local list

View File

@ -1,6 +0,0 @@
default
intllib?
unified_inventory?
3d_armor?
clothing?
sfinv?

View File

@ -1,4 +1,4 @@
local S = skins.S local S = minetest.get_translator("skinsdb")
function skins.get_formspec_context(player) function skins.get_formspec_context(player)
if player then if player then
@ -16,11 +16,14 @@ function skins.get_skin_info_formspec(skin)
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")
local m_license = skin:get_meta_string("license") local m_license = skin:get_meta_string("license")
local m_format = skin:get_meta("format")
-- overview page -- overview page
local formspec = "image[0,.75;1,2;"..skin:get_preview().."]" 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()).."]"
if texture then if texture then
formspec = formspec.."label[6,.5;"..S("Raw texture")..":]" formspec = formspec.."label[6,.5;"..S("Raw texture")..":]"
.."image[6,1;2,1;"..skin:get_texture().."]" .."image[6,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[2,.5;"..S("Name")..": "..minetest.formspec_escape(m_name).."]"
@ -63,7 +66,7 @@ function skins.get_skin_selection_formspec(player, context, y_delta)
y = y_delta y = y_delta
end end
formspec = formspec.."image_button["..x..","..y..";1,2;".. formspec = formspec.."image_button["..x..","..y..";1,2;"..
skin:get_preview()..";skins_set$"..i..";]".. minetest.formspec_escape(skin:get_preview())..";skins_set$"..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

View File

@ -3,19 +3,11 @@
-- Copyright (c) 2012 cornernote, Dean Montgomery -- Copyright (c) 2012 cornernote, Dean Montgomery
-- Rework 2017 by bell07 -- Rework 2017 by bell07
-- License: GPLv3 -- License: GPLv3
-- Boilerplate to support localized strings if intllib mod is installed.
skins = {} skins = {}
skins.modpath = minetest.get_modpath(minetest.get_current_modname()) skins.modpath = minetest.get_modpath(minetest.get_current_modname())
skins.default = "character" skins.default = "character"
local S
if minetest.get_modpath("intllib") then
skins.S = intllib.Getter()
else
skins.S = function(s) return s end
end
dofile(skins.modpath.."/skin_meta_api.lua") dofile(skins.modpath.."/skin_meta_api.lua")
dofile(skins.modpath.."/api.lua") dofile(skins.modpath.."/api.lua")
dofile(skins.modpath.."/skinlist.lua") dofile(skins.modpath.."/skinlist.lua")
@ -73,42 +65,30 @@ minetest.register_on_leaveplayer(function(player)
skins.ui_context[player:get_player_name()] = nil skins.ui_context[player:get_player_name()] = nil
end) end)
if minetest.global_exists("player_api") then player_api.register_model("skinsdb_3d_armor_character_5.b3d", {
-- Minetest-5 and above compatible animation_speed = 30,
player_api.register_model("skinsdb_3d_armor_character_5.b3d", { textures = {
animation_speed = 30, "blank.png",
textures = { "blank.png",
"blank.png", "blank.png",
"blank.png", "blank.png"
"blank.png", },
"blank.png" animations = {
}, stand = {x=0, y=79},
animations = { lay = {x=162, y=166},
stand = {x=0, y=79}, walk = {x=168, y=187},
lay = {x=162, y=166}, mine = {x=189, y=198},
walk = {x=168, y=187}, walk_mine = {x=200, y=219},
mine = {x=189, y=198}, sit = {x=81, y=160},
walk_mine = {x=200, y=219}, },
sit = {x=81, y=160}, })
},
}) -- Register default character.png if not part of this mod
else local default_skin_obj = skins.get(skins.default)
-- Minetest-0.4 compatible if not default_skin_obj then
default.player_register_model("skinsdb_3d_armor_character.b3d", { default_skin_obj = skins.new(skins.default)
animation_speed = 30, default_skin_obj:set_texture("character.png")
textures = { default_skin_obj:set_meta("format", '1.0')
"blank.png", default_skin_obj:set_meta("_sort_id", 0)
"blank.png", default_skin_obj:set_meta("name", "Sam")
"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},
},
})
end end

View File

@ -1,16 +0,0 @@
# Translation by Xanthin
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
Player not found = Spieler nicht da
unknown command = unbekannter Befehl
see /help skinsdb for supported parameters = Lese /help für erlaubte Parameter
skin set to = Skin ist jetzt
invalid skin = unbekannter Skin
unknown parameter = unbekannter Parameter

View File

@ -1,18 +0,0 @@
# Template
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
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
unknown parameter = paramètre inconnu
unknown skin = skin inconnue

View File

@ -1,17 +0,0 @@
# Malay translation by muhdnurhidayat
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
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
unknown parameter = parameter tidak diketahui
unknown skin = kulit tidak diketahui

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

@ -0,0 +1,19 @@
# textdomain: skinsdb
# Translation by Xanthin
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
Player not found=Spieler nicht da
unknown command=unbekannter Befehl
see /help skinsdb for supported parameters=Lese /help für erlaubte Parameter
skin set to=Skin ist jetzt
invalid skin=unbekannter Skin
unknown parameter=unbekannter Parameter
Skins=Aussehen
Requires skin key=Benötigt Skin-Name

18
locale/skinsdb.fr.tr Normal file
View File

@ -0,0 +1,18 @@
# textdomain: skinsdb
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
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
unknown parameter=paramètre inconnu
unknown skin=skin inconnue

18
locale/skinsdb.ms.tr Normal file
View File

@ -0,0 +1,18 @@
# textdomain: skinsdb
# Malay translation by muhdnurhidayat
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
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
unknown parameter=parameter tidak diketahui
unknown skin=kulit tidak diketahui

20
locale/skinsdb.zh_CN.tr Normal file
View File

@ -0,0 +1,20 @@
# textdomain: skinsdb
#Translation by IFRFSX(BingFengFSX)
Raw texture=自然状态的纹理
Name=名称
Author=作者
Change=更换
Page=页面
License=许可证
Description=说明
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=无效皮肤
unknown parameter=未知参数
unknown skin=未知皮肤
Downloads the specified range of skins and shuts down the server=下载指定范围的皮肤并关闭服务器
Skins=皮肤

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

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

View File

@ -1,17 +1,20 @@
# Template # textdomain: skinsdb
Raw texture = Raw texture=
Name = Name=
Author = Author=
Change = Change=
Page = Page=
License = License=
Description = 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 parameter=
unknown skin = unknown skin=
Downloads the specified range of skins and shuts down the server=
Skins=
Requires skin key=

View File

@ -1,18 +0,0 @@
#Translation by IFRFSX(BingFengFSX)
Raw texture = 自然状态的纹理
Name = 名称
Author = 作者
Change = 更换
Page = 页面
License = 许可证
Description = 说明
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 = 无效皮肤
unknown parameter = 未知参数
unknown skin = 未知皮肤
Downloads the specified range of skins and shuts down the server = 下载指定范围的皮肤并关闭服务器

View File

@ -1,3 +0,0 @@
Sam 0
Jordach
CC BY-SA 3.0

View File

@ -1 +1,4 @@
name = skinsdb name = skinsdb
description = Player skin mod, supporting unified_inventory, sfinv and smart_inventory
depends = default
optional_depends = unified_inventory,3d_armor,clothing,sfinv

View File

@ -1,4 +1,4 @@
local S = skins.S local S = minetest.get_translator("skinsdb")
-- generate the current formspec -- generate the current formspec
local function get_formspec(player, context) local function get_formspec(player, context)
@ -9,7 +9,7 @@ local function get_formspec(player, context)
end end
sfinv.register_page("skins:overview", { sfinv.register_page("skins:overview", {
title = "Skins", title = S("Skins"),
get = function(self, player, context) get = function(self, player, context)
-- collect skins data -- collect skins data
return sfinv.make_formspec(player, context, get_formspec(player, context)) return sfinv.make_formspec(player, context, get_formspec(player, context))

View File

@ -56,7 +56,58 @@ function skin_class:set_preview(value)
end end
function skin_class:get_preview() function skin_class:get_preview()
return self._preview or "player.png" 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 end
function skin_class:apply_skin_to_player(player) function skin_class:apply_skin_to_player(player)
@ -73,13 +124,9 @@ function skin_class:apply_skin_to_player(player)
local playername = player:get_player_name() local playername = player:get_player_name()
local ver = self:get_meta("format") or "1.0" local ver = self:get_meta("format") or "1.0"
if minetest.global_exists("player_api") then
-- Minetest-5 compatible player_api.set_model(player, "skinsdb_3d_armor_character_5.b3d")
player_api.set_model(player, "skinsdb_3d_armor_character_5.b3d")
else
-- Minetest-0.4 compatible
default.player_set_model(player, "skinsdb_3d_armor_character.b3d")
end
local v10_texture = "blank.png" local v10_texture = "blank.png"
local v18_texture = "blank.png" local v18_texture = "blank.png"
local armor_texture = "blank.png" local armor_texture = "blank.png"
@ -114,23 +161,12 @@ function skin_class:apply_skin_to_player(player)
end end
end end
if minetest.global_exists("player_api") then player_api.set_textures(player, {
-- Minetest-5 compatible v10_texture,
player_api.set_textures(player, { v18_texture,
v10_texture, armor_texture,
v18_texture, wielditem_texture,
armor_texture, })
wielditem_texture,
})
else
-- Minetest-0.4 compatible
default.player_set_textures(player, {
v10_texture,
v18_texture,
armor_texture,
wielditem_texture,
})
end
player:set_properties({ player:set_properties({
visual_size = { visual_size = {
@ -149,5 +185,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
(assigned_player:lower() == playername:lower()) playername and (minetest.check_player_privs(playername, {server=true}) or
assigned_player:lower() == playername:lower())
end end

View File

@ -1,4 +1,3 @@
local skins_dir_list = minetest.get_dir_list(skins.modpath.."/textures") local skins_dir_list = minetest.get_dir_list(skins.modpath.."/textures")
for _, fn in pairs(skins_dir_list) do for _, fn in pairs(skins_dir_list) do

View File

@ -1,6 +1,6 @@
-- Skins update script -- Skins update script
local S = skins.S local S = minetest.get_translator("skinsdb")
local _ID_ = "Lua Skins Updater" local _ID_ = "Lua Skins Updater"
local internal = {} local internal = {}
@ -75,7 +75,7 @@ end
-- Insecure workaround since meta/ and textures/ cannot be written to -- Insecure workaround since meta/ and textures/ cannot be written to
local function unsafe_file_write(path, contents) local function unsafe_file_write(path, contents)
local f = ie.io.open(path, "w") local f = ie.io.open(path, "wb")
f:write(contents) f:write(contents)
f:close() f:close()
end end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

View File

@ -7,6 +7,7 @@ One or multiple private skins for player "nick":
player_[nick].png or player_[nick].png or
player_[nick]_[number-or-name].png player_[nick]_[number-or-name].png
Preview files for public and private skins: Preview files for public and private skins.
Optional, overrides the generated preview
character_*_preview.png or character_*_preview.png or
player_*_*_preview.png player_*_*_preview.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 B

After

Width:  |  Height:  |  Size: 250 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@ -1,4 +1,4 @@
local S = skins.S local S = minetest.get_translator("skinsdb")
unified_inventory.register_page("skins", { unified_inventory.register_page("skins", {
get_formspec = function(player) get_formspec = function(player)
@ -12,6 +12,7 @@ unified_inventory.register_page("skins", {
unified_inventory.register_button("skins", { unified_inventory.register_button("skins", {
type = "image", type = "image",
image = "skins_button.png", image = "skins_button.png",
tooltip = S("Skins"),
}) })
local function get_formspec(player) local function get_formspec(player)

44
updater/update_skins.py Normal file
View File

@ -0,0 +1,44 @@
import sys, requests, base64
print("Downloading skins from minetest.fensta.bplaced.net ...")
# Requesting all skins and their raw texture using the API
r = requests.get('http://minetest.fensta.bplaced.net/api/v2/get.json.php?getlist&page=1&per_page=999999999')
if r.status_code != 200:
sys.exit("Request failed!")
data = r.json()
count = 0
print("Writing to file and downloading previews ...")
for json in data["skins"]:
id = str(json["id"])
# Downloading the preview of the skin
r2 = requests.get('http://minetest.fensta.bplaced.net/skins/1/' + id + ".png")
if r.status_code == 200:
preview = r2.content
# Read meta datas
name = str(json["name"])
author = str(json["author"])
license = str(json["license"])
# Texture file
raw_data = base64.b64decode(json["img"])
file = open("../textures/character_" + id + ".png", "wb")
file.write(bytearray(raw_data))
file.close()
# Preview file
file = open("../textures/character_" + id + "_preview.png", "wb")
file.write(bytearray(preview))
file.close()
# Meta file
file = open("../meta/character_" + id + ".txt", "w")
file.write(name + "\n" + author + "\n" + license + "\n")
file.close()
print("Added #%s Name: %s Author: %s License: %s" % (id, name, author, license))
count += 1
else:
print("Failed to download skin #" + id)
print("Fetched " + str(count) + " skins!")