1
0
mirror of https://github.com/Suiranoil/SkinRestorer.git synced 2026-01-16 04:42:12 +00:00

implement Result type

This commit is contained in:
2024-06-29 16:39:22 +03:00
parent 972bd4c8d8
commit 9733490dd0
9 changed files with 137 additions and 77 deletions

View File

@@ -4,13 +4,13 @@ import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import it.unimi.dsi.fastutil.Pair;
import net.lionarius.skinrestorer.skin.SkinIO;
import net.lionarius.skinrestorer.skin.SkinResult;
import net.lionarius.skinrestorer.skin.SkinStorage;
import net.lionarius.skinrestorer.skin.provider.MineskinSkinProvider;
import net.lionarius.skinrestorer.skin.provider.MojangSkinProvider;
import net.lionarius.skinrestorer.skin.provider.SkinProvider;
import net.lionarius.skinrestorer.util.FileUtils;
import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.LevelResource;
@@ -63,15 +63,19 @@ public final class SkinRestorer {
SkinRestorer.skinStorage = new SkinStorage(new SkinIO(worldSkinDirectory));
}
public static CompletableFuture<Pair<Collection<ServerPlayer>, Collection<GameProfile>>> setSkinAsync(MinecraftServer server, Collection<GameProfile> targets, Supplier<SkinResult> skinSupplier) {
public static CompletableFuture<Pair<Collection<ServerPlayer>, Collection<GameProfile>>> setSkinAsync(
MinecraftServer server,
Collection<GameProfile> targets,
Supplier<Result<Optional<Property>, ?>> skinSupplier
) {
return CompletableFuture.<Pair<Property, Collection<GameProfile>>>supplyAsync(() -> {
SkinResult result = skinSupplier.get();
var result = skinSupplier.get();
if (result.isError()) {
SkinRestorer.LOGGER.error("Could not get skin", result.getError());
SkinRestorer.LOGGER.error("Could not get skin: {}", result.getErrorValue());
return Pair.of(null, Collections.emptySet());
}
Property skin = result.getSkin();
Property skin = result.getSuccessValue().orElse(null);
for (GameProfile profile : targets) {
SkinRestorer.getSkinStorage().setSkin(profile.getId(), skin);

View File

@@ -1,6 +1,7 @@
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;
@@ -8,9 +9,9 @@ import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.skin.SkinResult;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.skin.provider.SkinProvider;
import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.TranslationUtils;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.arguments.GameProfileArgument;
@@ -20,6 +21,7 @@ import net.minecraft.server.level.ServerPlayer;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
@@ -33,7 +35,7 @@ public final class SkinCommand {
public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
LiteralArgumentBuilder<CommandSourceStack> base =
literal("skin")
.then(buildAction("clear", SkinResult::empty));
.then(buildAction("clear", () -> Result.ofNullable(null)));
LiteralArgumentBuilder<CommandSourceStack> set = literal("set");
@@ -71,13 +73,16 @@ public final class SkinCommand {
return action;
}
private static ArgumentBuilder<CommandSourceStack, LiteralArgumentBuilder<CommandSourceStack>> buildAction(String name, Supplier<SkinResult> supplier) {
private static ArgumentBuilder<CommandSourceStack, LiteralArgumentBuilder<CommandSourceStack>> buildAction(
String name,
Supplier<Result<Optional<Property>, ?>> 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>, SkinResult> provider
Function<CommandContext<CommandSourceStack>, Result<Optional<Property>, ?>> provider
) {
return argument
.executes(context -> skinAction(
@@ -88,7 +93,7 @@ public final class SkinCommand {
}
private static RequiredArgumentBuilder<CommandSourceStack, GameProfileArgument.Result> makeTargetsArgument(
Function<CommandContext<CommandSourceStack>, SkinResult> provider
Function<CommandContext<CommandSourceStack>, Result<Optional<Property>, ?>> provider
) {
return argument("targets", GameProfileArgument.gameProfile())
.requires(source -> source.hasPermission(2))
@@ -100,7 +105,12 @@ public final class SkinCommand {
));
}
private static int skinAction(CommandSourceStack src, Collection<GameProfile> targets, boolean setByOperator, Supplier<SkinResult> skinSupplier) {
private static int skinAction(
CommandSourceStack src,
Collection<GameProfile> targets,
boolean setByOperator,
Supplier<Result<Optional<Property>, ?>> skinSupplier
) {
SkinRestorer.setSkinAsync(src.getServer(), targets, skinSupplier).thenAccept(pair -> {
Collection<GameProfile> profiles = pair.right();
Collection<ServerPlayer> players = pair.left();
@@ -127,7 +137,7 @@ public final class SkinCommand {
return targets.size();
}
private static int skinAction(CommandSourceStack src, Supplier<SkinResult> skinSupplier) {
private static int skinAction(CommandSourceStack src, Supplier<Result<Optional<Property>, ?>> skinSupplier) {
if (src.getPlayer() == null)
return 0;

View File

@@ -1,7 +1,7 @@
package net.lionarius.skinrestorer.mixin;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.skin.SkinResult;
import net.lionarius.skinrestorer.util.Result;
import net.minecraft.network.Connection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
@@ -40,6 +40,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()), () -> SkinResult.ofNullable(SkinRestorer.getSkinStorage().getSkin(player.getUUID())));
SkinRestorer.setSkinAsync(server, Collections.singleton(player.getGameProfile()), () -> Result.ofNullable(SkinRestorer.getSkinStorage().getSkin(player.getUUID())));
}
}

View File

@@ -2,8 +2,8 @@ package net.lionarius.skinrestorer.mixin;
import com.mojang.authlib.GameProfile;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.skin.SkinResult;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.util.Result;
import net.minecraft.server.network.ServerLoginPacketListenerImpl;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
@@ -22,7 +22,7 @@ public abstract class ServerLoginPacketListenerImplMixin {
private GameProfile authenticatedProfile;
@Unique
private CompletableFuture<SkinResult> skinrestorer_pendingSkin;
private CompletableFuture<Void> skinrestorer_pendingSkin;
@Inject(method = "verifyLoginAndFinishConnectionSetup", at = @At(value = "INVOKE",
target = "Lnet/minecraft/server/players/PlayerList;canPlayerLogin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/network/chat/Component;"),
@@ -34,15 +34,17 @@ public abstract class ServerLoginPacketListenerImplMixin {
SkinRestorer.LOGGER.debug("Fetching {}'s skin", authenticatedProfile.getName());
if (!SkinRestorer.getSkinStorage().hasSavedSkin(authenticatedProfile.getId())) { // when player joins for the first time fetch Mojang skin by his username
SkinResult result = SkinRestorer.getProvider("mojang").map(
var result = SkinRestorer.getProvider("mojang").map(
provider -> provider.getSkin(authenticatedProfile.getName(), SkinVariant.CLASSIC)
).orElse(SkinResult.empty());
).orElse(Result.ofNullable(null));
if (!result.isError())
SkinRestorer.getSkinStorage().setSkin(authenticatedProfile.getId(), result.getSkin());
SkinRestorer.getSkinStorage().setSkin(authenticatedProfile.getId(), result.getSuccessValue().orElse(null));
}
return SkinResult.ofNullable(SkinRestorer.getSkinStorage().getSkin(authenticatedProfile.getId()));
SkinRestorer.getSkinStorage().getSkin(authenticatedProfile.getId()); // loads skin from disk
return null;
});
}

View File

@@ -1,45 +0,0 @@
package net.lionarius.skinrestorer.skin;
import com.mojang.authlib.properties.Property;
import org.jetbrains.annotations.NotNull;
public class SkinResult {
private final Property skin;
private final Exception exception;
private SkinResult(Property skin, Exception exception) {
this.skin = skin;
this.exception = exception;
}
public Property getSkin() {
return this.skin;
}
public Exception getError() {
return this.exception;
}
public boolean isError() {
return this.exception != null;
}
public static SkinResult empty() {
return new SkinResult(null, null);
}
public static SkinResult error(Exception e) {
return new SkinResult(null, e);
}
public static SkinResult success(@NotNull Property skin) {
return new SkinResult(skin, null);
}
public static SkinResult ofNullable(Property skin) {
if (skin == null)
return SkinResult.empty();
return SkinResult.success(skin);
}
}

View File

@@ -2,15 +2,16 @@ package net.lionarius.skinrestorer.skin.provider;
import com.google.gson.JsonObject;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.skin.SkinResult;
import com.mojang.datafixers.util.Either;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.util.JsonUtils;
import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.WebUtils;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
public final class MineskinSkinProvider implements SkinProvider {
@@ -35,7 +36,7 @@ public final class MineskinSkinProvider implements SkinProvider {
}
@Override
public SkinResult getSkin(String url, SkinVariant variant) {
public Result<Optional<Property>, Exception> getSkin(String url, SkinVariant variant) {
try {
String body = ("{\"variant\":\"%s\",\"name\":\"%s\",\"visibility\":%d,\"url\":\"%s\"}")
.formatted(variant.toString(), "none", 1, url);
@@ -43,9 +44,9 @@ public final class MineskinSkinProvider implements SkinProvider {
JsonObject texture = JsonUtils.parseJson(WebUtils.postRequest(API_URL.toURL(), "application/json", body))
.getAsJsonObject("data").getAsJsonObject("texture");
return SkinResult.success(new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString()));
} catch (IOException e) {
return SkinResult.error(e);
return Result.ofNullable(new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString()));
} catch (Exception e) {
return Result.error(e);
}
}
}

View File

@@ -2,15 +2,16 @@ package net.lionarius.skinrestorer.skin.provider;
import com.google.gson.JsonObject;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.skin.SkinResult;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.util.JsonUtils;
import net.lionarius.skinrestorer.util.PlayerUtils;
import net.lionarius.skinrestorer.util.Result;
import net.lionarius.skinrestorer.util.WebUtils;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Optional;
import java.util.UUID;
public final class MojangSkinProvider implements SkinProvider {
@@ -39,15 +40,15 @@ public final class MojangSkinProvider implements SkinProvider {
}
@Override
public SkinResult getSkin(String username, SkinVariant variant) {
public Result<Optional<Property>, Exception> getSkin(String username, SkinVariant variant) {
try {
UUID uuid = getUUID(username);
JsonObject texture = JsonUtils.parseJson(WebUtils.getRequest(SESSION_SERVER_URL.resolve(uuid + "?unsigned=false").toURL()))
.getAsJsonArray("properties").get(0).getAsJsonObject();
return SkinResult.success(new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString()));
return Result.ofNullable(new Property(PlayerUtils.TEXTURES_KEY, texture.get("value").getAsString(), texture.get("signature").getAsString()));
} catch (Exception e) {
return SkinResult.error(e);
return Result.error(e);
}
}

View File

@@ -1,7 +1,10 @@
package net.lionarius.skinrestorer.skin.provider;
import net.lionarius.skinrestorer.skin.SkinResult;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.skin.SkinVariant;
import net.lionarius.skinrestorer.util.Result;
import java.util.Optional;
public interface SkinProvider {
@@ -9,5 +12,5 @@ public interface SkinProvider {
boolean hasVariantSupport();
SkinResult getSkin(String argument, SkinVariant variant);
Result<Optional<Property>, Exception> getSkin(String argument, SkinVariant variant);
}

View File

@@ -0,0 +1,84 @@
package net.lionarius.skinrestorer.util;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
public class Result<S, E> {
private final S successValue;
private final E errorValue;
private Result(S successValue, E errorValue) {
if (successValue == null && errorValue == null)
throw new IllegalArgumentException("Cannot create result of null values");
this.successValue = successValue;
this.errorValue = errorValue;
}
public S getSuccessValue() {
return successValue;
}
public E getErrorValue() {
return errorValue;
}
public boolean isSuccess() {
return successValue != null;
}
public boolean isError() {
return errorValue != null;
}
public <N> Result<N, E> map(Function<S, N> mapper) {
if (successValue == null)
return new Result<>(null, errorValue);
return new Result<>(mapper.apply(successValue), errorValue);
}
public <N> Result<S, N> mapError(Function<E, N> mapper) {
if (errorValue == null)
return new Result<>(successValue, null);
return new Result<>(successValue, mapper.apply(errorValue));
}
public Optional<S> toOptional() {
return Optional.ofNullable(successValue);
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Result<?, ?> result = (Result<?, ?>) o;
return Objects.equals(successValue, result.successValue) && Objects.equals(errorValue, result.errorValue);
}
@Override
public int hashCode() {
return Objects.hash(successValue, errorValue);
}
public static <S, E> Result<S, E> success(@NotNull S successValue) {
return new Result<>(successValue, null);
}
public static <S, E> Result<S, E> error(@NotNull E errorValue) {
return new Result<>(null, errorValue);
}
public static <S, E> Result<Optional<S>, E> ofNullable(S successValue) {
return Result.success(Optional.ofNullable(successValue));
}
}