diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c47320..dd26a6e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [2.0.3] - 2024-08-20
+### Added
+- Added `firstJoinSkinProvider` config option (see [wiki](https://github.com/Suiranoil/SkinRestorer/wiki/Config#firstjoinskinprovider))
+### Fixed
+- Fixed ability to set config values to `null`
+
## [2.0.2] - 2024-08-04
### Added
- Backported to minecraft 1.20-1.20.2
diff --git a/CHANGELOG_LATEST.md b/CHANGELOG_LATEST.md
index 4b6fca1..a253c55 100644
--- a/CHANGELOG_LATEST.md
+++ b/CHANGELOG_LATEST.md
@@ -1,5 +1,4 @@
### Added
-- Backported to minecraft 1.20-1.20.2
-- [Forge] Added support for Forge
-### Changed
-- Changed minimum java version to 17 for minecraft 1.20-1.20.4
+- Added `firstJoinSkinProvider` config option (see [wiki](https://github.com/Suiranoil/SkinRestorer/wiki/Config#firstjoinskinprovider))
+### Fixed
+- Fixed ability to set config values to `null`
diff --git a/README.md b/README.md
index b6a8be3..67e227a 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,100 @@
# SkinRestorer
-
-
-[](https://www.curseforge.com/minecraft/mc-mods/skinrestorer)
-[](https://modrinth.com/mod/skinrestorer)
+
+
-SkinRestorer is a **server-side** only mod for Fabric that allows players to use and change skins on servers running in
-offline/insecure mode.
+A server-side mod for managing and restoring player skins.
-## Features
+## ✨ Features
-- **Set Skins from Mojang Account**: Fetch and apply skins using a valid Minecraft account name.
-- **Set Skins from URL**: Apply skins from any image URL, supporting both classic (Steve) and slim (Alex) skin models.
+- **Set skins from Mojang Account**: Fetch and apply skins using a valid Minecraft username.
+- **Set skins from Ely.by**: Fetch and apply skins using a valid [Ely.by](https://ely.by/) username.
+- **Set skins from URL**: Fetch and apply skins from any image URL, supporting both classic (Steve) and slim (Alex) skin models.
+- **Automatic skin fetching**: Automatically fetch skin from Mojang/Ely.by when a player joins the server running in offline/insecure mode ([configurable](https://github.com/Suiranoil/SkinRestorer/wiki/Config)).
+- **Singleplayer support**: Apply skins individually for each world.
+- **Permissions API support**
-## Command Usage Guide
+## 📜 Command Usage Guide
-### Set Mojang Skin
+### Set Mojang skin
+
+Fetch and apply skins using a valid Minecraft username.
```
/skin set mojang []
```
-- **Parameters:**
- - ``: Minecraft account name to fetch the skin from.
+- **Parameters**
+ - ``: Minecraft username to fetch the skin from.
- `[]`: (Optional, server operators only) Player(s) to apply the skin to.
-### Set Web Skin
+### Set Ely.by skin
+
+Fetch and apply skins using a valid [Ely.by](https://ely.by/) username.
+
+```
+/skin set ely.by []
+```
+
+- **Parameters**
+ - ``: Ely.by username to fetch the skin from.
+ - `[]`: (Optional, server operators only) Player(s) to apply the skin to.
+
+### Set Web skin
+
+Fetch and apply skins from any image URL, supporting both classic (Steve) and slim (Alex) skin models.
+
+Uses [mineskin api](https://mineskin.org/) under the hood.
```
/skin set web (classic|slim) "" []
```
-- **Parameters:**
+- **Parameters**
- `(classic|slim)`: Type of the skin model (`classic` for Steve model, `slim` for Alex model).
- `""`: URL pointing to the skin image file (ensure it follows Minecraft's skin size and format requirements).
- `[]`: (Optional, server operators only) Player(s) to apply the skin to.
-### Clear Skin
+### Refresh skin
+
+Refetch and reapply the currently applied skins.
+
+```
+/skin refresh []
+```
+
+- **Parameters**
+ - `[]`: (Optional, server operators only) Player(s) to refresh the skin for.
+
+### Clear skin
+
+Remove the currently applied skins.
```
/skin clear []
```
-- **Parameters:**
+- **Parameters**
- `[]`: (Optional, server operators only) Player(s) to clear the skin for.
-### Notes:
+### Reset skin
+
+Reset to the default skin or remove any custom skin.
+
+If [automatic skin fetching](https://github.com/Suiranoil/SkinRestorer/wiki/Config#fetchskinonfirstjoin) is enabled, a new skin will be fetched when the player rejoins the server.
+
+```
+/skin reset []
+```
+
+- **Parameters**
+ - `[]`: (Optional, server operators only) Player(s) to reset the skin for.
+
+### Notes
- If `targets` is not specified, the command will apply to the player executing the command.
-### Examples:
+### Examples
```
/skin set mojang Notch
diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/Config.java b/common/src/main/java/net/lionarius/skinrestorer/config/Config.java
index 91d0acd..3366568 100644
--- a/common/src/main/java/net/lionarius/skinrestorer/config/Config.java
+++ b/common/src/main/java/net/lionarius/skinrestorer/config/Config.java
@@ -15,6 +15,8 @@ public final class Config {
private boolean fetchSkinOnFirstJoin = true;
+ private FirstJoinSkinProvider firstJoinSkinProvider = FirstJoinSkinProvider.MOJANG;
+
private String proxy = "";
private long requestTimeout = 10;
@@ -27,6 +29,10 @@ public final class Config {
return this.fetchSkinOnFirstJoin;
}
+ public FirstJoinSkinProvider getFirstJoinSkinProvider() {
+ return this.firstJoinSkinProvider;
+ }
+
public String getProxy() {
return this.proxy;
}
@@ -48,8 +54,24 @@ public final class Config {
if (config == null)
config = new Config();
+ config.verifyAndFix();
+
FileUtils.writeFile(path.resolve(Config.CONFIG_FILENAME), JsonUtils.toJson(config));
return config;
}
+
+ private void verifyAndFix() {
+ if (this.language == null || this.language.isEmpty())
+ this.language = "en_us";
+
+ if (this.firstJoinSkinProvider == null)
+ this.firstJoinSkinProvider = FirstJoinSkinProvider.MOJANG;
+
+ if (this.proxy == null)
+ this.proxy = "";
+
+ if (this.requestTimeout <= 0)
+ this.requestTimeout = 10;
+ }
}
diff --git a/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java b/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java
new file mode 100644
index 0000000..120f8de
--- /dev/null
+++ b/common/src/main/java/net/lionarius/skinrestorer/config/FirstJoinSkinProvider.java
@@ -0,0 +1,22 @@
+package net.lionarius.skinrestorer.config;
+
+import com.google.gson.annotations.SerializedName;
+import net.lionarius.skinrestorer.skin.provider.ElyBySkinProvider;
+import net.lionarius.skinrestorer.skin.provider.MojangSkinProvider;
+
+public enum FirstJoinSkinProvider {
+ @SerializedName(value = "MOJANG", alternate = {"mojang"})
+ MOJANG(MojangSkinProvider.PROVIDER_NAME),
+ @SerializedName(value = "ELY.BY", alternate = {"ely.by", "ELY_BY", "ely_by"})
+ ELY_BY(ElyBySkinProvider.PROVIDER_NAME);
+
+ private final String name;
+
+ FirstJoinSkinProvider(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+}
diff --git a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java
index 59d772d..c607893 100644
--- a/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java
+++ b/common/src/main/java/net/lionarius/skinrestorer/mixin/ServerLoginPacketListenerImplMixin.java
@@ -3,7 +3,6 @@ package net.lionarius.skinrestorer.mixin;
import com.mojang.authlib.GameProfile;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.skin.SkinValue;
-import net.lionarius.skinrestorer.skin.provider.MojangSkinProvider;
import net.lionarius.skinrestorer.skin.provider.SkinProviderContext;
import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
@@ -48,7 +47,11 @@ public abstract class ServerLoginPacketListenerImplMixin {
if (originalSkin == null && SkinRestorer.getConfig().fetchSkinOnFirstJoin()) {
SkinRestorer.LOGGER.debug("Fetching {}'s skin", authenticatedProfile.getName());
- var context = new SkinProviderContext(MojangSkinProvider.PROVIDER_NAME, authenticatedProfile.getName(), null);
+ var context = new SkinProviderContext(
+ SkinRestorer.getConfig().getFirstJoinSkinProvider().getName(),
+ authenticatedProfile.getName(),
+ null
+ );
var result = SkinRestorer.getProvider(context.name()).map(
provider -> provider.getSkin(context.argument(), context.variant())
).orElse(Result.ofNullable(null));
diff --git a/gradle.properties b/gradle.properties
index 956b603..84772a9 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -11,7 +11,7 @@ minecraft_version_list=1.20.2
minecraft_version_range=1.20.2
mod_id=skinrestorer
mod_name=SkinRestorer
-mod_version=2.0.2
+mod_version=2.0.3
mod_author=Lionarius
mod_homepage=https://www.curseforge.com/minecraft/mc-mods/skinrestorer
mod_sources=https://github.com/Suiranoil/SkinRestorer