mirror of
https://github.com/Suiranoil/SkinRestorer.git
synced 2026-01-16 04:42:12 +00:00
Compare commits
43 Commits
v2.2.0-1.2
...
c8a4674e0c
| Author | SHA1 | Date | |
|---|---|---|---|
|
c8a4674e0c
|
|||
|
d374c80659
|
|||
|
d85942041b
|
|||
|
1cafc91935
|
|||
|
5d28269b94
|
|||
|
2293fc2399
|
|||
|
3971f2efbf
|
|||
|
516601a899
|
|||
|
2e3b2649b3
|
|||
|
07d620ae2f
|
|||
|
ad2177848c
|
|||
|
8136354af1
|
|||
|
fee25d2d04
|
|||
|
33926b15e1
|
|||
|
226d3f7bcb
|
|||
|
c81adc7320
|
|||
|
583e59f9d8
|
|||
|
8081e443b9
|
|||
|
61e1ada760
|
|||
|
|
d04a3beecf | ||
|
|
89b06fcfc7 | ||
|
|
d7fffdab1b | ||
|
|
9e175ddabb | ||
|
|
74a571b62c | ||
|
|
8ae29eae89 | ||
|
|
9f0fac4b2a | ||
|
|
e68aa90953 | ||
|
143e95d7f2
|
|||
|
34de47e13e
|
|||
|
08e7f1792b
|
|||
|
6a7f5f2096
|
|||
|
4d6d4faafd
|
|||
|
823b2ea3f6
|
|||
|
a63b6c3867
|
|||
|
dfe6d7af42
|
|||
|
ebdbee480f
|
|||
|
33e5889657
|
|||
|
18ec6e87f8
|
|||
|
4c803cb369
|
|||
|
14f51e9224
|
|||
|
1d1aa94644
|
|||
|
5ddde27915
|
|||
|
cfdfee11b3
|
88
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
88
.github/ISSUE_TEMPLATE/bug_report.yaml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Bug Report
|
||||
description: Report a bug encountered with SkinRestorer
|
||||
title: "[Bug]: "
|
||||
labels: ["bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
🐛 **Before submitting:**
|
||||
- Ensure you're using the latest mod version
|
||||
- Ensure this bug is reproducible consistently
|
||||
- Check existing issues for duplicates
|
||||
|
||||
- type: input
|
||||
id: mod-version
|
||||
attributes:
|
||||
label: SkinRestorer Version
|
||||
description: Exact version of the mod
|
||||
placeholder: e.g., 2.2.1
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: minecraft-version
|
||||
attributes:
|
||||
label: Minecraft Version
|
||||
description: Full Minecraft version
|
||||
placeholder: e.g., 1.21.4
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: loader-version
|
||||
attributes:
|
||||
label: Mod Loader & Version
|
||||
description: Fabric/Quilt/Forge/NeoForge version
|
||||
placeholder: e.g., Fabric 0.15.7
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: java-version
|
||||
attributes:
|
||||
label: Java Version
|
||||
placeholder: e.g. 17, 21
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Bug Description
|
||||
description: What happened vs what you expected to happen
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Reproduction Steps
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: logs
|
||||
attributes:
|
||||
label: Relevant Logs
|
||||
description: |
|
||||
Please provide any relevant logs, especially error logs from your server console or `latest.log` file.
|
||||
**Important**: Paste the FULL log content. Use a service like [mclo.gs](https://mclo.gs/).
|
||||
render: shell
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: Confirmations
|
||||
options:
|
||||
- label: I've checked for existing issues
|
||||
required: true
|
||||
- label: This bug is reproducible consistently
|
||||
required: true
|
||||
- label: I've tested without other mods
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1 @@
|
||||
blank_issues_enabled: true
|
||||
52
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
52
.github/ISSUE_TEMPLATE/feature_request.yaml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Feature Request
|
||||
description: Suggest a new feature for SkinRestorer
|
||||
title: "[Feature Request]: "
|
||||
labels: ["enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
💡 **Before submitting:**
|
||||
- Check existing feature requests for duplicates
|
||||
- Be clear and concise
|
||||
- Explain the *why* and the *how*
|
||||
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Feature Description
|
||||
description: Detailed description of the feature
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: motivation
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: Why is this feature needed? What problem does it solve?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: use-cases
|
||||
attributes:
|
||||
label: Use Cases
|
||||
description: How would this feature be used and who would benefit?
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: implementation-ideas
|
||||
attributes:
|
||||
label: Implementation Ideas (Optional)
|
||||
description: Any thoughts on how it could be implemented?
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: checkboxes
|
||||
id: confirmations
|
||||
attributes:
|
||||
label: Confirmations
|
||||
options:
|
||||
- label: I've checked for existing feature requests
|
||||
required: true
|
||||
@@ -4,7 +4,11 @@ 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.2.0] - 2024-11-28
|
||||
## [2.2.1] - 2024-12-23
|
||||
### Fixed
|
||||
- Fixed game not closing because of mineskin working threads (closes [#41](https://github.com/Suiranoil/SkinRestorer/issues/41))
|
||||
|
||||
## [2.2.0] - 2024-12-02
|
||||
### Added
|
||||
- Added support for [SkinShuffle](https://modrinth.com/mod/skinshuffle) clients (requires FabricAPI on Fabric)
|
||||
(closes [#34](https://github.com/Suiranoil/SkinRestorer/issues/34))
|
||||
|
||||
@@ -1,10 +1,2 @@
|
||||
### Added
|
||||
- Added support for [SkinShuffle](https://modrinth.com/mod/skinshuffle) clients (requires FabricAPI on Fabric)
|
||||
(closes [#34](https://github.com/Suiranoil/SkinRestorer/issues/34))
|
||||
- Added `providers.mineskin.apiKey` config option (see [wiki](https://github.com/Suiranoil/SkinRestorer/wiki/Configuration#providersmineskin))
|
||||
### Changed
|
||||
- Migrated to MineSkin's new API V2
|
||||
### Fixed
|
||||
- Fixed `providers` config validation
|
||||
### Removed
|
||||
- Dropped support for NeoForge on Minecraft 1.20.2-1.20.4
|
||||
- Fixed game not closing because of mineskin working threads (closes [#41](https://github.com/Suiranoil/SkinRestorer/issues/41))
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# SkinRestorer
|
||||
|
||||
<a href="https://modrinth.com/mod/skinrestorer"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/minecraft/platform/modrinth/mini/badge.svg" alt="Modrinth" height="32"></a>
|
||||
<a href="https://www.curseforge.com/minecraft/mc-mods/skinrestorer"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/minecraft/platform/curseforge/mini/badge.svg" alt="CurseForge" height="32"></a>
|
||||
<a href="https://modrinth.com/mod/skinrestorer"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/minecraft/platform/modrinth/mini/badge.svg" alt="Modrinth"></a>
|
||||
<a href="https://www.curseforge.com/minecraft/mc-mods/skinrestorer"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/minecraft/platform/curseforge/mini/badge.svg" alt="CurseForge"></a>
|
||||
|
||||
A server-side mod for managing and restoring player skins.
|
||||
|
||||
@@ -33,7 +33,9 @@ the [commands wiki page](https://github.com/Suiranoil/SkinRestorer/wiki/Commands
|
||||
If you enjoy using **SkinRestorer** and would like to support its development, you can contribute through the following
|
||||
platforms:
|
||||
|
||||
[](https://ko-fi.com/lionarius)
|
||||
<a href="https://ko-fi.com/lionarius"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/donation/kofi/mini/badge.svg" alt="Ko-fi" height="32"></a>
|
||||
|
||||
<a href="https://boosty.to/lionarius"><img src="https://raw.githubusercontent.com/Suiranoil/badges/main/assets/donation/boosty/mini/badge.svg" alt="Boosty" height="32"></a>
|
||||
|
||||
Bitcoin (BTC): `1Ndbwny8pxdnWXFgadp95fp97y5JqMJKTX`
|
||||
USDT (TRC20): `TGXn8wrqku5KLzwPWQAeH7wgnV4UzwHEae`
|
||||
|
||||
10
build.gradle
10
build.gradle
@@ -1,15 +1,15 @@
|
||||
plugins {
|
||||
// see https://fabricmc.net/develop/ for new versions
|
||||
id 'fabric-loom' version '1.8-SNAPSHOT' apply false
|
||||
// see https://projects.neoforged.net/neoforged/neogradle for new versions
|
||||
id 'net.neoforged.gradle.userdev' version '7.0.153' apply false
|
||||
id 'fabric-loom' version '1.10-SNAPSHOT' apply false
|
||||
// see https://projects.neoforged.net/neoforged/moddevgradle for new versions
|
||||
id 'net.neoforged.moddev' version '2.0.80' apply false
|
||||
// see https://files.minecraftforge.net/net/minecraftforge/gradle/ForgeGradle/ for new versions
|
||||
id 'net.minecraftforge.gradle' version '6.0.29' apply false
|
||||
id 'net.minecraftforge.gradle' version '6.0.35' apply false
|
||||
id 'org.parchmentmc.librarian.forgegradle' version '1.+' apply false
|
||||
|
||||
id 'org.spongepowered.mixin' version '0.7-SNAPSHOT' apply false
|
||||
|
||||
id 'me.modmuss50.mod-publish-plugin' version '0.8.1' apply false
|
||||
id 'me.modmuss50.mod-publish-plugin' version '0.8.4' apply false
|
||||
}
|
||||
|
||||
allprojects {
|
||||
|
||||
@@ -5,6 +5,8 @@ plugins {
|
||||
publishMods {
|
||||
if (project.name == 'fabric')
|
||||
file = remapJar.archiveFile
|
||||
else if (project.name == 'neoforge')
|
||||
file = jar.archiveFile
|
||||
else
|
||||
file = tasks.named('jarJar').get().archiveFile
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ public final class SkinCommand {
|
||||
private SkinCommand() {}
|
||||
|
||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> base =
|
||||
var base =
|
||||
literal("skin")
|
||||
.then(buildSetSubcommand("clear", SkinValue.EMPTY::toProviderContext))
|
||||
.then(literal("reset")
|
||||
@@ -44,7 +44,7 @@ public final class SkinCommand {
|
||||
)))
|
||||
.then(literal("refresh").executes(context -> refreshSubcommand(context.getSource())));
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> set = literal("set");
|
||||
var set = literal("set");
|
||||
|
||||
var providers = SkinRestorer.getProvidersRegistry().getPublicProviders();
|
||||
for (var entry : providers)
|
||||
@@ -92,7 +92,7 @@ public final class SkinCommand {
|
||||
Collection<GameProfile> targets,
|
||||
boolean setByOperator
|
||||
) {
|
||||
Collection<ServerPlayer> updatedPlayers = new HashSet<>();
|
||||
var updatedPlayers = new HashSet<ServerPlayer>();
|
||||
for (var profile : targets) {
|
||||
SkinValue skin = null;
|
||||
if (SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId()))
|
||||
@@ -199,7 +199,7 @@ public final class SkinCommand {
|
||||
}
|
||||
|
||||
private static LiteralArgumentBuilder<CommandSourceStack> buildSetSubcommand(String name, SkinProvider provider) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> action = literal(name);
|
||||
var action = literal(name);
|
||||
|
||||
if (provider.hasVariantSupport()) {
|
||||
for (SkinVariant variant : SkinVariant.values()) {
|
||||
|
||||
@@ -5,6 +5,7 @@ import net.lionarius.skinrestorer.platform.Services;
|
||||
import net.lionarius.skinrestorer.skin.SkinValue;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinShuffleSkinProvider;
|
||||
import net.lionarius.skinrestorer.util.PlayerUtils;
|
||||
import net.lionarius.skinrestorer.util.ServerUtils;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -40,7 +41,7 @@ public class SkinShuffleCompatibility {
|
||||
if (!property.hasSignature())
|
||||
return;
|
||||
|
||||
server.execute(() -> {
|
||||
ServerUtils.scheduleServerTask(server, () -> {
|
||||
SkinRestorer.applySkin(
|
||||
server,
|
||||
Collections.singleton(player.getGameProfile()),
|
||||
|
||||
@@ -18,6 +18,8 @@ public final class Config implements GsonPostProcessable {
|
||||
|
||||
private boolean refreshSkinOnJoin = true;
|
||||
|
||||
private int skinApplyDelayOnJoin = 0;
|
||||
|
||||
private boolean fetchSkinOnFirstJoin = true;
|
||||
|
||||
private FirstJoinSkinProvider firstJoinSkinProvider = FirstJoinSkinProvider.MOJANG;
|
||||
@@ -37,6 +39,10 @@ public final class Config implements GsonPostProcessable {
|
||||
return this.refreshSkinOnJoin;
|
||||
}
|
||||
|
||||
public int skinApplyDelayOnJoin() {
|
||||
return this.skinApplyDelayOnJoin;
|
||||
}
|
||||
|
||||
public boolean fetchSkinOnFirstJoin() {
|
||||
return this.fetchSkinOnFirstJoin;
|
||||
}
|
||||
@@ -82,6 +88,11 @@ public final class Config implements GsonPostProcessable {
|
||||
this.language = "en_us";
|
||||
}
|
||||
|
||||
if (this.skinApplyDelayOnJoin < 0) {
|
||||
SkinRestorer.LOGGER.warn("SkinApplyDelayOnJoin config is less than 0, defaulting to 0");
|
||||
this.skinApplyDelayOnJoin = 0;
|
||||
}
|
||||
|
||||
if (this.firstJoinSkinProvider == null) {
|
||||
SkinRestorer.LOGGER.warn("FirstJoinSkinProvider config is null, defaulting to MOJANG");
|
||||
this.firstJoinSkinProvider = FirstJoinSkinProvider.MOJANG;
|
||||
|
||||
@@ -1,10 +1,42 @@
|
||||
package net.lionarius.skinrestorer.config.provider;
|
||||
|
||||
public interface BuiltInProviderConfig {
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.util.gson.GsonPostProcessable;
|
||||
|
||||
boolean enabled();
|
||||
|
||||
String name();
|
||||
|
||||
CacheConfig cache();
|
||||
public abstract class BuiltInProviderConfig implements GsonPostProcessable {
|
||||
protected boolean enabled;
|
||||
protected String name;
|
||||
protected CacheConfig cache;
|
||||
|
||||
public BuiltInProviderConfig(String name, CacheConfig cache) {
|
||||
this.enabled = true;
|
||||
this.name = name;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CacheConfig cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
protected void validate(String defaultName, CacheConfig defaultCache) {
|
||||
if (this.name == null || this.name.isEmpty()) {
|
||||
SkinRestorer.LOGGER.warn("Provider name is null or empty, defaulting to '{}'", defaultName);
|
||||
this.name = defaultName;
|
||||
}
|
||||
|
||||
if (this.cache == null) {
|
||||
SkinRestorer.LOGGER.warn("Provider cache is null, using default");
|
||||
this.cache = defaultCache;
|
||||
} else {
|
||||
this.cache.validate(defaultCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,46 +1,16 @@
|
||||
package net.lionarius.skinrestorer.config.provider;
|
||||
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.skin.provider.ElyBySkinProvider;
|
||||
import net.lionarius.skinrestorer.util.gson.GsonPostProcessable;
|
||||
|
||||
public class ElyByProviderConfig implements BuiltInProviderConfig, GsonPostProcessable {
|
||||
public final class ElyByProviderConfig extends BuiltInProviderConfig {
|
||||
private static final CacheConfig DEFAULT_CACHE_VALUE = new CacheConfig(true, 60);
|
||||
|
||||
private boolean enabled;
|
||||
private String name;
|
||||
private CacheConfig cache;
|
||||
|
||||
public ElyByProviderConfig() {
|
||||
this.enabled = true;
|
||||
this.name = ElyBySkinProvider.PROVIDER_NAME;
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CacheConfig cache() {
|
||||
return cache;
|
||||
super(ElyBySkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gsonPostProcess() {
|
||||
if (this.name == null || this.name.isEmpty()) {
|
||||
SkinRestorer.LOGGER.warn("Ely.By provider name is null or empty, defaulting to '{}'", ElyBySkinProvider.PROVIDER_NAME);
|
||||
this.name = ElyBySkinProvider.PROVIDER_NAME;
|
||||
}
|
||||
|
||||
if (this.cache == null) {
|
||||
SkinRestorer.LOGGER.warn("Ely.By provider cache is null, using default");
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
} else {
|
||||
this.cache.validate(DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
super.validate(ElyBySkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,52 +2,25 @@ package net.lionarius.skinrestorer.config.provider;
|
||||
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.skin.provider.MineskinSkinProvider;
|
||||
import net.lionarius.skinrestorer.util.gson.GsonPostProcessable;
|
||||
|
||||
public class MineskinProviderConfig implements BuiltInProviderConfig, GsonPostProcessable {
|
||||
public final class MineskinProviderConfig extends BuiltInProviderConfig {
|
||||
private static final CacheConfig DEFAULT_CACHE_VALUE = new CacheConfig(true, 300);
|
||||
|
||||
private boolean enabled;
|
||||
private String name;
|
||||
private CacheConfig cache;
|
||||
private String apiKey;
|
||||
|
||||
public MineskinProviderConfig() {
|
||||
this.enabled = true;
|
||||
this.name = MineskinSkinProvider.PROVIDER_NAME;
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
super(MineskinSkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
|
||||
this.apiKey = "";
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CacheConfig cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public String apiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gsonPostProcess() {
|
||||
if (this.name == null || this.name.isEmpty()) {
|
||||
SkinRestorer.LOGGER.warn("Mineskin provider name is null or empty, defaulting to '{}'", MineskinSkinProvider.PROVIDER_NAME);
|
||||
this.name = MineskinSkinProvider.PROVIDER_NAME;
|
||||
}
|
||||
|
||||
if (this.cache == null) {
|
||||
SkinRestorer.LOGGER.warn("Mineskin cache is null, using default");
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
} else {
|
||||
this.cache.validate(DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
super.validate(MineskinSkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
|
||||
if (this.apiKey == null) {
|
||||
SkinRestorer.LOGGER.warn("Mineskin API key is null, defaulting to an empty string");
|
||||
|
||||
@@ -1,46 +1,16 @@
|
||||
package net.lionarius.skinrestorer.config.provider;
|
||||
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.skin.provider.MojangSkinProvider;
|
||||
import net.lionarius.skinrestorer.util.gson.GsonPostProcessable;
|
||||
|
||||
public class MojangProviderConfig implements BuiltInProviderConfig, GsonPostProcessable {
|
||||
public final class MojangProviderConfig extends BuiltInProviderConfig {
|
||||
private static final CacheConfig DEFAULT_CACHE_VALUE = new CacheConfig(true, 60);
|
||||
|
||||
private boolean enabled;
|
||||
private String name;
|
||||
private CacheConfig cache;
|
||||
|
||||
public MojangProviderConfig() {
|
||||
this.enabled = true;
|
||||
this.name = MojangSkinProvider.PROVIDER_NAME;
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
}
|
||||
|
||||
public boolean enabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public CacheConfig cache() {
|
||||
return cache;
|
||||
super(MojangSkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gsonPostProcess() {
|
||||
if (this.name == null || this.name.isEmpty()) {
|
||||
SkinRestorer.LOGGER.warn("Mojang provider name is null or empty, defaulting to '{}'", MojangSkinProvider.PROVIDER_NAME);
|
||||
this.name = MojangSkinProvider.PROVIDER_NAME;
|
||||
}
|
||||
|
||||
if (this.cache == null) {
|
||||
SkinRestorer.LOGGER.warn("Mojang provider cache is null, using default");
|
||||
this.cache = DEFAULT_CACHE_VALUE;
|
||||
} else {
|
||||
this.cache.validate(DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
super.validate(MojangSkinProvider.PROVIDER_NAME, DEFAULT_CACHE_VALUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package net.lionarius.skinrestorer.mixin;
|
||||
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.util.ServerUtils;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -9,6 +10,7 @@ import net.minecraft.server.players.PlayerList;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
@@ -39,6 +41,22 @@ public abstract class PlayerListMixin {
|
||||
|
||||
@Inject(method = "placeNewPlayer", at = @At("HEAD"))
|
||||
private void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie, CallbackInfo ci) {
|
||||
var delay = SkinRestorer.getConfig().skinApplyDelayOnJoin();
|
||||
var uuid = player.getUUID();
|
||||
|
||||
if (delay <= 0) {
|
||||
skinrestorer$tryApplySkin(server, player);
|
||||
} else {
|
||||
ServerUtils.scheduleServerTask(server, () -> {
|
||||
var actualPlayer = server.getPlayerList().getPlayer(uuid);
|
||||
if (actualPlayer != null)
|
||||
skinrestorer$tryApplySkin(server, actualPlayer);
|
||||
}, delay);
|
||||
}
|
||||
}
|
||||
|
||||
@Unique
|
||||
private static void skinrestorer$tryApplySkin(MinecraftServer server, ServerPlayer player) {
|
||||
if (SkinRestorer.getSkinStorage().hasSavedSkin(player.getUUID()))
|
||||
SkinRestorer.applySkin(server, Collections.singleton(player.getGameProfile()), SkinRestorer.getSkinStorage().getSkin(player.getUUID()));
|
||||
}
|
||||
|
||||
@@ -27,9 +27,7 @@ public class SkinIO {
|
||||
|
||||
public SkinValue loadSkin(UUID uuid) {
|
||||
try {
|
||||
var value = SkinIO.loadSkin(savePath.resolve(SkinIO.uuidToFilename(uuid)));
|
||||
Objects.requireNonNull(value.provider());
|
||||
return value;
|
||||
return SkinIO.loadSkin(savePath.resolve(SkinIO.uuidToFilename(uuid)));
|
||||
} catch (Exception e) {
|
||||
return SkinValue.EMPTY;
|
||||
}
|
||||
|
||||
@@ -3,11 +3,14 @@ package net.lionarius.skinrestorer.skin;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.lionarius.skinrestorer.skin.provider.EmptySkinProvider;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProviderContext;
|
||||
import net.lionarius.skinrestorer.util.gson.GsonPostProcessable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record SkinValue(@NotNull String provider, @Nullable String argument, @Nullable SkinVariant variant,
|
||||
@Nullable Property value, @Nullable Property originalValue) {
|
||||
@Nullable Property value, @Nullable Property originalValue) implements GsonPostProcessable {
|
||||
|
||||
public static final SkinValue EMPTY = new SkinValue(EmptySkinProvider.PROVIDER_NAME, null, null, null);
|
||||
|
||||
@@ -30,4 +33,9 @@ public record SkinValue(@NotNull String provider, @Nullable String argument, @Nu
|
||||
public SkinValue setOriginalValue(Property originalValue) {
|
||||
return new SkinValue(this.provider, this.argument, this.variant, this.value, originalValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gsonPostProcess() {
|
||||
Objects.requireNonNull(this.provider);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import org.mineskin.response.QueueResponse;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -31,19 +30,10 @@ public final class MineskinSkinProvider implements SkinProvider {
|
||||
|
||||
public static final String PROVIDER_NAME = "web";
|
||||
|
||||
private static final URI API_URI;
|
||||
private static MineSkinClient MINESKIN_CLIENT;
|
||||
|
||||
private static LoadingCache<Pair<URI, SkinVariant>, Optional<Property>> SKIN_CACHE;
|
||||
|
||||
static {
|
||||
try {
|
||||
API_URI = new URI("https://api.mineskin.org");
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void reload() {
|
||||
var config = SkinRestorer.getConfig();
|
||||
var configApiKey = config.providersConfig().mineskin().apiKey();
|
||||
|
||||
@@ -67,12 +67,7 @@ public final class PlayerUtils {
|
||||
}
|
||||
|
||||
if (!player.isDeadOrDying()) {
|
||||
player.connection.send(new ClientboundBundlePacket(
|
||||
List.of(
|
||||
new ClientboundRespawnPacket(player.createCommonSpawnInfo(serverLevel), ClientboundRespawnPacket.KEEP_ALL_DATA),
|
||||
new ClientboundGameEventPacket(ClientboundGameEventPacket.LEVEL_CHUNKS_LOAD_START, 0)
|
||||
)
|
||||
));
|
||||
player.connection.send(new ClientboundRespawnPacket(player.createCommonSpawnInfo(serverLevel), ClientboundRespawnPacket.KEEP_ALL_DATA));
|
||||
player.connection.teleport(player.getX(), player.getY(), player.getZ(), player.getYRot(), player.getXRot());
|
||||
player.connection.send(new ClientboundSetEntityMotionPacket(player));
|
||||
var vehicle = player.getVehicle();
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package net.lionarius.skinrestorer.util;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class ServerUtils {
|
||||
|
||||
private ServerUtils() {}
|
||||
|
||||
public static void scheduleServerTask(MinecraftServer server, Consumer<MinecraftServer> task) {
|
||||
ServerUtils.scheduleServerTask(server, task, 0);
|
||||
}
|
||||
|
||||
public static void scheduleServerTask(MinecraftServer server, Runnable task) {
|
||||
ServerUtils.scheduleServerTask(server, task, 0);
|
||||
}
|
||||
|
||||
public static void scheduleServerTask(MinecraftServer server, Consumer<MinecraftServer> task, int tickDelay) {
|
||||
server.execute(new ScheduledTickTask(server, task, server.getTickCount() + tickDelay));
|
||||
}
|
||||
|
||||
public static void scheduleServerTask(MinecraftServer server, Runnable task, int tickDelay) {
|
||||
server.execute(new ScheduledTickTask(server, _server -> task.run(), server.getTickCount() + tickDelay));
|
||||
}
|
||||
|
||||
public record ScheduledTickTask(MinecraftServer server, Consumer<MinecraftServer> task,
|
||||
int tickTarget) implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
if (this.server.getTickCount() >= this.tickTarget)
|
||||
this.task.accept(this.server);
|
||||
else
|
||||
this.server.execute(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
public abstract class MinecraftServerMixin {
|
||||
|
||||
@Inject(method = "runServer",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getNanos()J", ordinal = 0))
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/Util;getMillis()J", ordinal = 0))
|
||||
private void onServerStarted(CallbackInfo ci) {
|
||||
SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this);
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ minecraft {
|
||||
|
||||
runs {
|
||||
client {
|
||||
workingDirectory file('../run/client')
|
||||
workingDirectory rootProject.file('run/client')
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
taskName 'Client'
|
||||
mods {
|
||||
@@ -51,7 +51,7 @@ minecraft {
|
||||
}
|
||||
|
||||
server {
|
||||
workingDirectory file('../run/server')
|
||||
workingDirectory rootProject.file('run/server')
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
taskName 'Server'
|
||||
mods {
|
||||
|
||||
@@ -3,12 +3,12 @@ group=net.lionarius
|
||||
java_version=17
|
||||
|
||||
# Common
|
||||
minecraft_version=1.20.3
|
||||
minecraft_version_list=1.20.3,1.20.4
|
||||
minecraft_version_range=[1.20.3,1.20.4]
|
||||
minecraft_version=1.20.2
|
||||
minecraft_version_list=1.20.2
|
||||
minecraft_version_range=1.20.2
|
||||
mod_id=skinrestorer
|
||||
mod_name=SkinRestorer
|
||||
mod_version=2.2.0
|
||||
mod_version=2.2.1
|
||||
mod_author=Lionarius
|
||||
mod_homepage=https://modrinth.com/mod/skinrestorer
|
||||
mod_sources=https://github.com/Suiranoil/SkinRestorer
|
||||
@@ -17,11 +17,12 @@ license=MIT
|
||||
credits=
|
||||
description=A server-side mod for managing skins.
|
||||
|
||||
# Dependencies
|
||||
mineskin_client_version=3.0.1-SNAPSHOT
|
||||
|
||||
# ParchmentMC mappings, see https://parchmentmc.org/docs/getting-started#choose-a-version for new versions
|
||||
parchment_minecraft=1.20.3
|
||||
parchment_version=2023.12.31
|
||||
parchment_minecraft=1.20.2
|
||||
parchment_version=2023.12.10
|
||||
|
||||
# Fabric, see https://fabricmc.net/develop/ for new versions
|
||||
fabric_loader_version=0.15.0
|
||||
@@ -29,13 +30,13 @@ fabric_api_version=0.91.1+1.20.3
|
||||
fabric_optional_dependencies=fabric-api
|
||||
|
||||
# Forge, see https://files.minecraftforge.net/net/minecraftforge/forge/ for new versions
|
||||
forge_version=49.0.1
|
||||
forge_loader_version_range=[49,)
|
||||
forge_version=48.0.0
|
||||
forge_loader_version_range=[48,)
|
||||
# Forge sometimes skips minor minecraft versions (like 1.20.5)
|
||||
forge_minecraft_version=1.20.3
|
||||
forge_minecraft_version=1.20.2
|
||||
|
||||
# NeoForge, see https://projects.neoforged.net/neoforged/neoforge for new versions
|
||||
neoforge_version=20.3.1-beta
|
||||
neoforge_version=20.2.3-beta
|
||||
neoforge_loader_version_range=[1,)
|
||||
|
||||
# Publishing
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
3
gradlew
vendored
3
gradlew
vendored
@@ -86,8 +86,7 @@ done
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
@@ -3,56 +3,58 @@ plugins {
|
||||
id 'idea'
|
||||
id 'eclipse'
|
||||
|
||||
id 'net.neoforged.gradle.userdev'
|
||||
id 'net.neoforged.moddev'
|
||||
|
||||
id 'multiloader-publish'
|
||||
}
|
||||
|
||||
// Automatically enable neoforge AccessTransformers if the file exists
|
||||
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
if (at.exists()) {
|
||||
minecraft.accessTransformers.file(at)
|
||||
}
|
||||
|
||||
jarJar.enable()
|
||||
|
||||
tasks.named('jar') {
|
||||
archiveClassifier = 'thin'
|
||||
}
|
||||
|
||||
tasks.named('jarJar') {
|
||||
archiveClassifier = ''
|
||||
}
|
||||
|
||||
jar.finalizedBy('jarJar')
|
||||
|
||||
dependencies {
|
||||
implementation "net.neoforged:neoforge:${neoforge_version}"
|
||||
|
||||
implementation(jarJar(group: 'org.mineskin', name: 'java-client', version: mineskin_client_version)) {
|
||||
jarJar.ranged(it, "[${mineskin_client_version},)")
|
||||
neoForge {
|
||||
version = neoforge_version
|
||||
|
||||
// Automatically enable neoforge AccessTransformers if the file exists
|
||||
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg')
|
||||
if (at.exists()) {
|
||||
minecraft.accessTransformers.file(at)
|
||||
}
|
||||
}
|
||||
|
||||
subsystems {
|
||||
parchment {
|
||||
minecraftVersion = parchment_minecraft
|
||||
mappingsVersion = parchment_version
|
||||
}
|
||||
}
|
||||
|
||||
runs {
|
||||
configureEach {
|
||||
modSource project.sourceSets.main
|
||||
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
|
||||
runs {
|
||||
configureEach {
|
||||
ideName = "NeoForge ${it.name.capitalize()} (${project.path})" // Unify the run config names with fabric
|
||||
}
|
||||
|
||||
client {
|
||||
client()
|
||||
gameDirectory = rootProject.file('run/client')
|
||||
}
|
||||
|
||||
server {
|
||||
server()
|
||||
programArgument '--nogui'
|
||||
gameDirectory = rootProject.file('run/server')
|
||||
}
|
||||
}
|
||||
|
||||
client {
|
||||
workingDirectory = file('../run/client')
|
||||
}
|
||||
|
||||
server {
|
||||
programArgument '--nogui'
|
||||
workingDirectory = file('../run/server')
|
||||
mods {
|
||||
"${mod_id}" {
|
||||
sourceSet sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "net.neoforged:neoforge:${neoforge_version}"
|
||||
|
||||
jarJar(implementation('org.mineskin:java-client')) {
|
||||
version {
|
||||
strictly "[${mineskin_client_version},)"
|
||||
prefer mineskin_client_version
|
||||
}
|
||||
}
|
||||
|
||||
additionalRuntimeClasspath "org.mineskin:java-client:${mineskin_client_version}"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user