mirror of
https://github.com/Suiranoil/SkinRestorer.git
synced 2026-01-16 04:42:12 +00:00
use custom type to store skins
This commit is contained in:
@@ -1,11 +1,12 @@
|
||||
package net.lionarius.skinrestorer;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.lionarius.skinrestorer.config.Config;
|
||||
import net.lionarius.skinrestorer.skin.SkinIO;
|
||||
import net.lionarius.skinrestorer.skin.SkinStorage;
|
||||
import net.lionarius.skinrestorer.skin.SkinValue;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProvider;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProviderContext;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProviderRegistry;
|
||||
import net.lionarius.skinrestorer.util.FileUtils;
|
||||
import net.lionarius.skinrestorer.util.PlayerUtils;
|
||||
@@ -23,7 +24,6 @@ import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class SkinRestorer {
|
||||
public static final String MOD_ID = "skinrestorer";
|
||||
@@ -81,31 +81,48 @@ public final class SkinRestorer {
|
||||
return String.format("/assets/%s/%s", SkinRestorer.MOD_ID, name);
|
||||
}
|
||||
|
||||
public static Collection<ServerPlayer> applySkin(MinecraftServer server, Iterable<GameProfile> targets, SkinValue value) {
|
||||
var acceptedPlayers = new HashSet<ServerPlayer>();
|
||||
|
||||
for (var profile : targets) {
|
||||
SkinRestorer.getSkinStorage().setSkin(profile.getId(), value);
|
||||
|
||||
if (PlayerUtils.areSkinPropertiesEquals(value.value(), PlayerUtils.getPlayerSkin(profile)))
|
||||
continue;
|
||||
|
||||
PlayerUtils.applyRestoredSkin(profile, value.value());
|
||||
|
||||
var player = server.getPlayerList().getPlayer(profile.getId());
|
||||
if (player == null)
|
||||
continue;
|
||||
|
||||
PlayerUtils.refreshPlayer(player);
|
||||
acceptedPlayers.add(player);
|
||||
}
|
||||
|
||||
return acceptedPlayers;
|
||||
}
|
||||
|
||||
public static CompletableFuture<Result<Collection<ServerPlayer>, String>> setSkinAsync(
|
||||
MinecraftServer server,
|
||||
Collection<GameProfile> targets,
|
||||
Supplier<Result<Optional<Property>, Exception>> skinSupplier
|
||||
SkinProviderContext context
|
||||
) {
|
||||
return CompletableFuture.supplyAsync(skinSupplier)
|
||||
.thenApplyAsync(skinResult -> {
|
||||
if (skinResult.isError()) {
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> SkinRestorer.getProvider(context.name()).map(provider -> provider.getSkin(context.argument(), context.variant()))
|
||||
)
|
||||
.thenApplyAsync(result -> {
|
||||
if (result.isEmpty())
|
||||
throw new IllegalArgumentException("provider " + context.argument() + " is not registered");
|
||||
|
||||
var skinResult = result.get();
|
||||
if (skinResult.isError())
|
||||
return Result.<Collection<ServerPlayer>, String>error(skinResult.getErrorValue().getMessage());
|
||||
}
|
||||
|
||||
var skin = skinResult.getSuccessValue().orElse(null);
|
||||
HashSet<ServerPlayer> acceptedPlayers = new HashSet<>();
|
||||
var skinValue = SkinValue.fromProviderContextWithValue(context, skinResult.getSuccessValue().orElse(null));
|
||||
|
||||
var acceptedPlayers = SkinRestorer.applySkin(server, targets, skinValue);
|
||||
|
||||
for (GameProfile profile : targets) {
|
||||
SkinRestorer.getSkinStorage().setSkin(profile.getId(), skin);
|
||||
ServerPlayer player = server.getPlayerList().getPlayer(profile.getId());
|
||||
|
||||
if (player == null || PlayerUtils.areSkinPropertiesEquals(skin, PlayerUtils.getPlayerSkin(player)))
|
||||
continue;
|
||||
|
||||
PlayerUtils.applyRestoredSkin(player, skin);
|
||||
PlayerUtils.refreshPlayer(player);
|
||||
acceptedPlayers.add(player);
|
||||
}
|
||||
return Result.<Collection<ServerPlayer>, String>success(acceptedPlayers);
|
||||
}, server)
|
||||
.orTimeout(10, TimeUnit.SECONDS)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.lionarius.skinrestorer.command;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
@@ -11,15 +10,14 @@ import com.mojang.brigadier.context.CommandContext;
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.skin.SkinVariant;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProvider;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProviderContext;
|
||||
import net.lionarius.skinrestorer.util.PlayerUtils;
|
||||
import net.lionarius.skinrestorer.util.Result;
|
||||
import net.lionarius.skinrestorer.util.Translation;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.GameProfileArgument;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -33,7 +31,7 @@ public final class SkinCommand {
|
||||
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
|
||||
LiteralArgumentBuilder<CommandSourceStack> base =
|
||||
literal("skin")
|
||||
.then(buildAction("clear", SkinProvider.EMPTY::getSkin));
|
||||
.then(buildAction("clear", () -> new SkinProviderContext("empty", null, null)));
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> set = literal("set");
|
||||
|
||||
@@ -55,15 +53,20 @@ public final class SkinCommand {
|
||||
literal(variant.toString())
|
||||
.then(buildArgument(
|
||||
argument(provider.getArgumentName(), StringArgumentType.string()),
|
||||
context -> provider.getSkin(StringArgumentType.getString(context, provider.getArgumentName()), variant)
|
||||
context -> {
|
||||
var argument = StringArgumentType.getString(context, provider.getArgumentName());
|
||||
return new SkinProviderContext(name, argument, variant);
|
||||
}
|
||||
))
|
||||
);
|
||||
}
|
||||
} else {
|
||||
action.then(
|
||||
buildArgument(
|
||||
argument(provider.getArgumentName(), StringArgumentType.string()),
|
||||
context -> provider.getSkin(StringArgumentType.getString(context, provider.getArgumentName()), SkinVariant.CLASSIC)
|
||||
argument(provider.getArgumentName(), StringArgumentType.string()), context -> {
|
||||
var argument = StringArgumentType.getString(context, provider.getArgumentName());
|
||||
return new SkinProviderContext(name, argument, null);
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -73,45 +76,45 @@ public final class SkinCommand {
|
||||
|
||||
private static ArgumentBuilder<CommandSourceStack, LiteralArgumentBuilder<CommandSourceStack>> buildAction(
|
||||
String name,
|
||||
Supplier<Result<Optional<Property>, Exception>> supplier
|
||||
Supplier<SkinProviderContext> supplier
|
||||
) {
|
||||
return buildArgument(literal(name), context -> supplier.get());
|
||||
}
|
||||
|
||||
private static <T extends ArgumentBuilder<CommandSourceStack, T>> ArgumentBuilder<CommandSourceStack, T> buildArgument(
|
||||
ArgumentBuilder<CommandSourceStack, T> argument,
|
||||
Function<CommandContext<CommandSourceStack>, Result<Optional<Property>, Exception>> provider
|
||||
Function<CommandContext<CommandSourceStack>, SkinProviderContext> provider
|
||||
) {
|
||||
return argument
|
||||
.executes(context -> skinAction(
|
||||
context.getSource(),
|
||||
() -> provider.apply(context)
|
||||
provider.apply(context)
|
||||
))
|
||||
.then(makeTargetsArgument(provider));
|
||||
}
|
||||
|
||||
private static RequiredArgumentBuilder<CommandSourceStack, GameProfileArgument.Result> makeTargetsArgument(
|
||||
Function<CommandContext<CommandSourceStack>, Result<Optional<Property>, Exception>> provider
|
||||
Function<CommandContext<CommandSourceStack>, SkinProviderContext> provider
|
||||
) {
|
||||
return argument("targets", GameProfileArgument.gameProfile())
|
||||
.requires(source -> source.hasPermission(2))
|
||||
.executes(context -> skinAction(
|
||||
provider.apply(context),
|
||||
context.getSource(),
|
||||
GameProfileArgument.getGameProfiles(context, "targets"),
|
||||
true,
|
||||
() -> provider.apply(context)
|
||||
true
|
||||
));
|
||||
}
|
||||
|
||||
private static int skinAction(
|
||||
SkinProviderContext context,
|
||||
CommandSourceStack src,
|
||||
Collection<GameProfile> targets,
|
||||
boolean setByOperator,
|
||||
Supplier<Result<Optional<Property>, Exception>> skinSupplier
|
||||
boolean setByOperator
|
||||
) {
|
||||
src.sendSystemMessage(Translation.translatableWithFallback(Translation.COMMAND_SKIN_LOADING_KEY));
|
||||
|
||||
SkinRestorer.setSkinAsync(src.getServer(), targets, skinSupplier).thenAccept(result -> {
|
||||
SkinRestorer.setSkinAsync(src.getServer(), targets, context).thenAccept(result -> {
|
||||
if (result.isError()) {
|
||||
src.sendFailure(Translation.translatableWithFallback(
|
||||
Translation.COMMAND_SKIN_FAILED_KEY,
|
||||
@@ -146,12 +149,14 @@ public final class SkinCommand {
|
||||
return targets.size();
|
||||
}
|
||||
|
||||
private static int skinAction(CommandSourceStack
|
||||
src, Supplier<Result<Optional<Property>, Exception>> skinSupplier) {
|
||||
private static int skinAction(
|
||||
CommandSourceStack src,
|
||||
SkinProviderContext context
|
||||
) {
|
||||
if (src.getPlayer() == null)
|
||||
return 0;
|
||||
|
||||
return skinAction(src, Collections.singleton(src.getPlayer().getGameProfile()), false, skinSupplier);
|
||||
return skinAction(context, src, Collections.singleton(src.getPlayer().getGameProfile()), false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package net.lionarius.skinrestorer.mixin;
|
||||
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.util.Result;
|
||||
import net.minecraft.network.Connection;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@@ -40,6 +39,6 @@ public abstract class PlayerListMixin {
|
||||
|
||||
@Inject(method = "placeNewPlayer", at = @At("HEAD"))
|
||||
private void placeNewPlayer(Connection connection, ServerPlayer player, CommonListenerCookie cookie, CallbackInfo ci) {
|
||||
SkinRestorer.setSkinAsync(server, Collections.singleton(player.getGameProfile()), () -> Result.ofNullable(SkinRestorer.getSkinStorage().getSkin(player.getUUID())));
|
||||
SkinRestorer.applySkin(server, Collections.singleton(player.getGameProfile()), SkinRestorer.getSkinStorage().getSkin(player.getUUID()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package net.lionarius.skinrestorer.mixin;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import net.lionarius.skinrestorer.SkinRestorer;
|
||||
import net.lionarius.skinrestorer.skin.SkinVariant;
|
||||
import net.lionarius.skinrestorer.skin.SkinValue;
|
||||
import net.lionarius.skinrestorer.util.Result;
|
||||
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -35,11 +35,15 @@ public abstract class ServerLoginPacketListenerImplMixin {
|
||||
|
||||
if (SkinRestorer.getConfig().fetchSkinOnFirstJoin() && !SkinRestorer.getSkinStorage().hasSavedSkin(authenticatedProfile.getId())) { // when player joins for the first time fetch Mojang skin by his username
|
||||
var result = SkinRestorer.getProvider("mojang").map(
|
||||
provider -> provider.getSkin(authenticatedProfile.getName(), SkinVariant.CLASSIC)
|
||||
provider -> provider.getSkin(authenticatedProfile.getName(), null)
|
||||
).orElse(Result.ofNullable(null));
|
||||
|
||||
if (!result.isError())
|
||||
SkinRestorer.getSkinStorage().setSkin(authenticatedProfile.getId(), result.getSuccessValue().orElse(null));
|
||||
if (!result.isError()) {
|
||||
var value = new SkinValue("mojang", authenticatedProfile.getName(), null, result.getSuccessValue().orElse(null));
|
||||
SkinRestorer.getSkinStorage().setSkin(authenticatedProfile.getId(), value);
|
||||
} else {
|
||||
SkinRestorer.LOGGER.error("failed to fetch skin on first join", result.getErrorValue());
|
||||
}
|
||||
}
|
||||
|
||||
SkinRestorer.getSkinStorage().getSkin(authenticatedProfile.getId());
|
||||
|
||||
@@ -4,7 +4,9 @@ import com.mojang.authlib.properties.Property;
|
||||
import net.lionarius.skinrestorer.util.FileUtils;
|
||||
import net.lionarius.skinrestorer.util.JsonUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
public class SkinIO {
|
||||
@@ -21,11 +23,55 @@ public class SkinIO {
|
||||
return savePath.resolve(uuid + FILE_EXTENSION).toFile().exists();
|
||||
}
|
||||
|
||||
public Property loadSkin(UUID uuid) {
|
||||
return JsonUtils.fromJson(FileUtils.readFile(savePath.resolve(uuid + FILE_EXTENSION).toFile()), Property.class);
|
||||
public SkinValue loadSkin(UUID uuid) {
|
||||
try {
|
||||
var value = SkinIO.loadSkin(savePath.resolve(uuid + FILE_EXTENSION).toFile());
|
||||
Objects.requireNonNull(value.provider());
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
return SkinValue.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSkin(UUID uuid, Property skin) {
|
||||
private static SkinValue loadSkin(File file) {
|
||||
var json = FileUtils.readFile(file);
|
||||
try {
|
||||
return JsonUtils.fromJson(json, SkinValue.class);
|
||||
} catch (Exception e) {
|
||||
var property = JsonUtils.fromJson(json, Property.class);
|
||||
return SkinIO.convertFromOldFormat(property);
|
||||
}
|
||||
}
|
||||
|
||||
public void saveSkin(UUID uuid, SkinValue skin) {
|
||||
FileUtils.writeFile(savePath.toFile(), uuid + FILE_EXTENSION, JsonUtils.toJson(skin));
|
||||
}
|
||||
|
||||
private static SkinValue convertFromOldFormat(Property property) {
|
||||
try {
|
||||
var propertyJson = Objects.requireNonNull(JsonUtils.skinPropertyToJson(property));
|
||||
var textures = propertyJson.getAsJsonObject("textures");
|
||||
|
||||
var capeTexture = textures.getAsJsonObject("CAPE");
|
||||
if (capeTexture != null) {
|
||||
var profileName = propertyJson.get("profileName").getAsString();
|
||||
return new SkinValue("mojang", profileName, null, property);
|
||||
}
|
||||
|
||||
var skinTexture = textures.getAsJsonObject("SKIN");
|
||||
var url = skinTexture.get("url").getAsString();
|
||||
|
||||
var variant = SkinVariant.CLASSIC;
|
||||
var metadata = skinTexture.getAsJsonObject("metadata");
|
||||
if (metadata != null) {
|
||||
var model = metadata.get("model");
|
||||
if (model != null && "slim".equals(model.getAsString()))
|
||||
variant = SkinVariant.SLIM;
|
||||
}
|
||||
|
||||
return new SkinValue("web", url, variant, property);
|
||||
} catch (Exception e) {
|
||||
return SkinValue.EMPTY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package net.lionarius.skinrestorer.skin;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class SkinStorage {
|
||||
|
||||
private final Map<UUID, Optional<Property>> skinMap = new ConcurrentHashMap<>();
|
||||
private final Map<UUID, SkinValue> skinMap = new ConcurrentHashMap<>();
|
||||
private final SkinIO skinIO;
|
||||
|
||||
public SkinStorage(SkinIO skinIO) {
|
||||
@@ -20,21 +17,25 @@ public class SkinStorage {
|
||||
return this.skinIO.skinExists(uuid);
|
||||
}
|
||||
|
||||
public Property getSkin(UUID uuid) {
|
||||
public SkinValue getSkin(UUID uuid) {
|
||||
if (!skinMap.containsKey(uuid)) {
|
||||
Property skin = skinIO.loadSkin(uuid);
|
||||
var skin = skinIO.loadSkin(uuid);
|
||||
setSkin(uuid, skin);
|
||||
}
|
||||
|
||||
return skinMap.get(uuid).orElse(null);
|
||||
return skinMap.get(uuid);
|
||||
}
|
||||
|
||||
public void removeSkin(UUID uuid) {
|
||||
if (skinMap.containsKey(uuid))
|
||||
skinIO.saveSkin(uuid, skinMap.get(uuid).orElse(null));
|
||||
var skin = skinMap.remove(uuid);
|
||||
if (skin != null)
|
||||
skinIO.saveSkin(uuid, skin);
|
||||
}
|
||||
|
||||
public void setSkin(UUID uuid, Property skin) {
|
||||
skinMap.put(uuid, Optional.ofNullable(skin));
|
||||
public void setSkin(UUID uuid, SkinValue skin) {
|
||||
if (skin == null)
|
||||
skin = SkinValue.EMPTY;
|
||||
|
||||
skinMap.put(uuid, skin);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.lionarius.skinrestorer.skin;
|
||||
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import net.lionarius.skinrestorer.skin.provider.SkinProviderContext;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public record SkinValue(@NotNull String provider, @Nullable String argument, @Nullable SkinVariant variant,
|
||||
@Nullable Property value) {
|
||||
|
||||
public static final SkinValue EMPTY = new SkinValue("empty", null, null, null);
|
||||
|
||||
public static SkinValue fromProviderContextWithValue(SkinProviderContext context, Property value) {
|
||||
return new SkinValue(context.name(), context.argument(), context.variant(), value);
|
||||
}
|
||||
|
||||
public SkinProviderContext toProviderContext() {
|
||||
return new SkinProviderContext(this.provider, this.argument, this.variant);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package net.lionarius.skinrestorer.skin.provider;
|
||||
|
||||
import net.lionarius.skinrestorer.skin.SkinVariant;
|
||||
|
||||
public record SkinProviderContext(String name, String argument, SkinVariant variant) {
|
||||
}
|
||||
@@ -84,12 +84,11 @@ public final class PlayerUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static Property getPlayerSkin(ServerPlayer player) {
|
||||
return player.getGameProfile().getProperties().get(TEXTURES_KEY).stream().findFirst().orElse(null);
|
||||
public static Property getPlayerSkin(GameProfile profile) {
|
||||
return profile.getProperties().get(TEXTURES_KEY).stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
public static void applyRestoredSkin(ServerPlayer player, Property skin) {
|
||||
GameProfile profile = player.getGameProfile();
|
||||
public static void applyRestoredSkin(GameProfile profile, Property skin) {
|
||||
profile.getProperties().removeAll(TEXTURES_KEY);
|
||||
|
||||
if (skin != null)
|
||||
|
||||
Reference in New Issue
Block a user