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

architectury

This commit is contained in:
2024-06-27 17:04:51 +03:00
parent 59ecc5a56a
commit 07e3aaf60c
29 changed files with 280 additions and 67 deletions

10
common/build.gradle Normal file
View File

@@ -0,0 +1,10 @@
architectury {
common rootProject.enabled_platforms.split(',')
}
dependencies {
// We depend on Fabric Loader here to use the Fabric @Environment annotations,
// which get remapped to the correct annotations on each platform.
// Do NOT use other classes from Fabric Loader.
modImplementation "net.fabricmc:fabric-loader:$rootProject.fabric_loader_version"
}

View File

@@ -0,0 +1,31 @@
package net.lionarius.skinrestorer;
import com.google.gson.JsonObject;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.enums.SkinVariant;
import net.lionarius.skinrestorer.util.JsonUtils;
import net.lionarius.skinrestorer.util.WebUtils;
import java.io.IOException;
import java.net.URL;
public class MineskinSkinProvider {
private static final String API = "https://api.mineskin.org/generate/url";
private static final String USER_AGENT = "SkinRestorer";
private static final String TYPE = "application/json";
public static SkinResult getSkin(String url, SkinVariant variant) {
try {
String input = ("{\"variant\":\"%s\",\"name\":\"%s\",\"visibility\":%d,\"url\":\"%s\"}")
.formatted(variant.toString(), "none", 1, url);
JsonObject texture = JsonUtils.parseJson(WebUtils.POSTRequest(new URL(API), USER_AGENT, TYPE, TYPE, input))
.getAsJsonObject("data").getAsJsonObject("texture");
return SkinResult.success(new Property("textures", texture.get("value").getAsString(), texture.get("signature").getAsString()));
} catch (IOException e) {
return SkinResult.error(e);
}
}
}

View File

@@ -0,0 +1,33 @@
package net.lionarius.skinrestorer;
import com.google.gson.JsonObject;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.util.JsonUtils;
import net.lionarius.skinrestorer.util.WebUtils;
import java.io.IOException;
import java.net.URL;
import java.util.UUID;
public class MojangSkinProvider {
private static final String API = "https://api.mojang.com/users/profiles/minecraft/";
private static final String SESSION_SERVER = "https://sessionserver.mojang.com/session/minecraft/profile/";
public static SkinResult getSkin(String name) {
try {
UUID uuid = getUUID(name);
JsonObject texture = JsonUtils.parseJson(WebUtils.GETRequest(new URL(SESSION_SERVER + uuid + "?unsigned=false")))
.getAsJsonArray("properties").get(0).getAsJsonObject();
return SkinResult.success(new Property("textures", texture.get("value").getAsString(), texture.get("signature").getAsString()));
} catch (Exception e) {
return SkinResult.error(e);
}
}
private static UUID getUUID(String name) throws IOException {
return UUID.fromString(JsonUtils.parseJson(WebUtils.GETRequest(new URL(API + name))).get("id").getAsString()
.replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5"));
}
}

View File

@@ -0,0 +1,31 @@
package net.lionarius.skinrestorer;
import com.mojang.authlib.properties.Property;
import net.lionarius.skinrestorer.util.FileUtils;
import net.lionarius.skinrestorer.util.JsonUtils;
import java.nio.file.Path;
import java.util.UUID;
public class SkinIO {
private static final String FILE_EXTENSION = ".json";
private final Path savePath;
public SkinIO(Path savePath) {
this.savePath = savePath;
}
public boolean skinExists(UUID uuid) {
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 void saveSkin(UUID uuid, Property skin) {
FileUtils.writeFile(savePath.toFile(), uuid + FILE_EXTENSION, JsonUtils.toJson(skin));
}
}

View File

@@ -0,0 +1,154 @@
package net.lionarius.skinrestorer;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import it.unimi.dsi.fastutil.Pair;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.network.packet.s2c.play.*;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
public class SkinRestorer {
private static SkinStorage skinStorage;
public static final String MOD_ID = "skinrestorer";
public static final Logger LOGGER = LoggerFactory.getLogger("SkinRestorer");
public static SkinStorage getSkinStorage() {
return skinStorage;
}
public static void onInitialize() {
skinStorage = new SkinStorage(new SkinIO(FabricLoader.getInstance().getConfigDir().resolve("skinrestorer")));
}
public static void refreshPlayer(ServerPlayerEntity player) {
ServerWorld serverWorld = player.getServerWorld();
PlayerManager playerManager = serverWorld.getServer().getPlayerManager();
ServerChunkManager chunkManager = serverWorld.getChunkManager();
playerManager.sendToAll(new BundleS2CPacket(
List.of(
new PlayerRemoveS2CPacket(List.of(player.getUuid())),
PlayerListS2CPacket.entryFromPlayer(Collections.singleton(player))
)
));
if (!player.isDead()) {
chunkManager.unloadEntity(player);
chunkManager.loadEntity(player);
player.networkHandler.sendPacket(new BundleS2CPacket(
List.of(
new PlayerRespawnS2CPacket(player.createCommonPlayerSpawnInfo(serverWorld), PlayerRespawnS2CPacket.KEEP_ALL),
new GameStateChangeS2CPacket(GameStateChangeS2CPacket.INITIAL_CHUNKS_COMING, 0)
)
));
player.networkHandler.requestTeleport(player.getX(), player.getY(), player.getZ(), player.getYaw(), player.getPitch());
player.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(player));
player.sendAbilitiesUpdate();
player.addExperience(0);
playerManager.sendCommandTree(player);
playerManager.sendWorldInfo(player, serverWorld);
playerManager.sendPlayerStatus(player);
playerManager.sendStatusEffects(player);
}
}
public static CompletableFuture<Pair<Collection<ServerPlayerEntity>, Collection<GameProfile>>> setSkinAsync(MinecraftServer server, Collection<GameProfile> targets, Supplier<SkinResult> skinSupplier) {
return CompletableFuture.<Pair<Property, Collection<GameProfile>>>supplyAsync(() -> {
SkinResult result = skinSupplier.get();
if (result.isError()) {
SkinRestorer.LOGGER.error("Could not get skin", result.getError());
return Pair.of(null, Collections.emptySet());
}
Property skin = result.getSkin();
for (GameProfile profile : targets) {
SkinRestorer.getSkinStorage().setSkin(profile.getId(), skin);
}
HashSet<GameProfile> acceptedProfiles = new HashSet<>(targets);
return Pair.of(skin, acceptedProfiles);
}).<Pair<Collection<ServerPlayerEntity>, Collection<GameProfile>>>thenApplyAsync(pair -> {
Property skin = pair.left(); // NullPtrException will be caught by 'exceptionally'
Collection<GameProfile> acceptedProfiles = pair.right();
HashSet<ServerPlayerEntity> acceptedPlayers = new HashSet<>();
for (GameProfile profile : acceptedProfiles) {
ServerPlayerEntity player = server.getPlayerManager().getPlayer(profile.getId());
if (player == null || areSkinPropertiesEquals(skin, getPlayerSkin(player)))
continue;
applyRestoredSkin(player.getGameProfile(), skin);
refreshPlayer(player);
acceptedPlayers.add(player);
}
return Pair.of(acceptedPlayers, acceptedProfiles);
}, server)
.orTimeout(10, TimeUnit.SECONDS)
.exceptionally(e -> Pair.of(Collections.emptySet(), Collections.emptySet()));
}
public static void applyRestoredSkin(GameProfile profile, Property skin) {
profile.getProperties().removeAll("textures");
if (skin != null)
profile.getProperties().put("textures", skin);
}
private static Property getPlayerSkin(ServerPlayerEntity player) {
return player.getGameProfile().getProperties().get("textures").stream().findFirst().orElse(null);
}
private static final Gson gson = new Gson();
private static JsonObject skinPropertyToJson(Property property) {
try {
JsonObject json = gson.fromJson(new String(Base64.getDecoder().decode(property.value()), StandardCharsets.UTF_8), JsonObject.class);
if (json != null)
json.remove("timestamp");
return json;
} catch (Exception ex) {
return null;
}
}
private static boolean areSkinPropertiesEquals(Property x, Property y) {
if (x == y)
return true;
if (x == null || y == null)
return false;
if (x.equals(y))
return true;
JsonObject xJson = skinPropertyToJson(x);
JsonObject yJson = skinPropertyToJson(y);
if (xJson == null || yJson == null)
return false;
return xJson.equals(yJson);
}
}

View File

@@ -0,0 +1,45 @@
package net.lionarius.skinrestorer;
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

@@ -0,0 +1,40 @@
package net.lionarius.skinrestorer;
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 SkinIO skinIO;
public SkinStorage(SkinIO skinIO) {
this.skinIO = skinIO;
}
public boolean hasSavedSkin(UUID uuid) {
return this.skinIO.skinExists(uuid);
}
public Property getSkin(UUID uuid) {
if (!skinMap.containsKey(uuid)) {
Property skin = skinIO.loadSkin(uuid);
setSkin(uuid, skin);
}
return skinMap.get(uuid).orElse(null);
}
public void removeSkin(UUID uuid) {
if (skinMap.containsKey(uuid))
skinIO.saveSkin(uuid, skinMap.get(uuid).orElse(null));
}
public void setSkin(UUID uuid, Property skin) {
skinMap.put(uuid, Optional.ofNullable(skin));
}
}

View File

@@ -0,0 +1,100 @@
package net.lionarius.skinrestorer.command;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import net.lionarius.skinrestorer.MineskinSkinProvider;
import net.lionarius.skinrestorer.MojangSkinProvider;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.SkinResult;
import net.lionarius.skinrestorer.enums.SkinVariant;
import net.lionarius.skinrestorer.util.TranslationUtils;
import net.minecraft.command.argument.GameProfileArgumentType;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import java.util.Collection;
import java.util.Collections;
import java.util.function.Supplier;
import static net.minecraft.server.command.CommandManager.argument;
import static net.minecraft.server.command.CommandManager.literal;
public class SkinCommand {
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(literal("skin")
.then(literal("set")
.then(literal("mojang")
.then(argument("skin_name", StringArgumentType.word())
.executes(context ->
skinAction(context.getSource(),
() -> MojangSkinProvider.getSkin(StringArgumentType.getString(context, "skin_name"))))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(2))
.executes(context ->
skinAction(context.getSource(), GameProfileArgumentType.getProfileArgument(context, "targets"), true,
() -> MojangSkinProvider.getSkin(StringArgumentType.getString(context, "skin_name")))))))
.then(literal("web")
.then(literal("classic")
.then(argument("url", StringArgumentType.string())
.executes(context ->
skinAction(context.getSource(),
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.CLASSIC)))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(2))
.executes(context ->
skinAction(context.getSource(), GameProfileArgumentType.getProfileArgument(context, "targets"), true,
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.CLASSIC))))))
.then(literal("slim")
.then(argument("url", StringArgumentType.string())
.executes(context ->
skinAction(context.getSource(),
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.SLIM)))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(2))
.executes(context ->
skinAction(context.getSource(), GameProfileArgumentType.getProfileArgument(context, "targets"), true,
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.SLIM))))))))
.then(literal("clear")
.executes(context ->
skinAction(context.getSource(),
SkinResult::empty))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(2)).executes(context ->
skinAction(context.getSource(), GameProfileArgumentType.getProfileArgument(context, "targets"), true,
SkinResult::empty))))
);
}
private static int skinAction(ServerCommandSource src, Collection<GameProfile> targets, boolean setByOperator, Supplier<SkinResult> skinSupplier) {
SkinRestorer.setSkinAsync(src.getServer(), targets, skinSupplier).thenAccept(pair -> {
Collection<GameProfile> profiles = pair.right();
Collection<ServerPlayerEntity> players = pair.left();
if (profiles.isEmpty()) {
src.sendError(Text.of(TranslationUtils.translation.skinActionFailed));
return;
}
if (setByOperator) {
src.sendFeedback(() -> Text.of(
String.format(TranslationUtils.translation.skinActionAffectedProfile,
String.join(", ", profiles.stream().map(GameProfile::getName).toList()))), true);
if (!players.isEmpty()) {
src.sendFeedback(() -> Text.of(
String.format(TranslationUtils.translation.skinActionAffectedPlayer,
String.join(", ", players.stream().map(p -> p.getGameProfile().getName()).toList()))), true);
}
} else {
src.sendFeedback(() -> Text.of(TranslationUtils.translation.skinActionOk), true);
}
});
return targets.size();
}
private static int skinAction(ServerCommandSource src, Supplier<SkinResult> skinSupplier) {
if (src.getPlayer() == null)
return 0;
return skinAction(src, Collections.singleton(src.getPlayer().getGameProfile()), false, skinSupplier);
}
}

View File

@@ -0,0 +1,18 @@
package net.lionarius.skinrestorer.enums;
public enum SkinVariant {
CLASSIC("classic"),
SLIM("slim");
private final String name;
SkinVariant(String name) {
this.name = name;
}
@Override
public String toString() {
return name;
}
}

View File

@@ -0,0 +1,26 @@
package net.lionarius.skinrestorer.mixin;
import com.mojang.brigadier.CommandDispatcher;
import net.lionarius.skinrestorer.command.SkinCommand;
import net.minecraft.command.CommandRegistryAccess;
import net.minecraft.server.command.CommandManager;
import net.minecraft.server.command.ServerCommandSource;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(CommandManager.class)
public abstract class CommandManagerMixin {
@Final @Shadow
private CommandDispatcher<ServerCommandSource> dispatcher;
@Inject(method = "<init>", at = @At(value = "INVOKE",
target = "Lnet/minecraft/server/command/AdvancementCommand;register(Lcom/mojang/brigadier/CommandDispatcher;)V"))
private void init(CommandManager.RegistrationEnvironment environment, CommandRegistryAccess commandRegistryAccess, CallbackInfo ci) {
SkinCommand.register(dispatcher);
}
}

View File

@@ -0,0 +1,45 @@
package net.lionarius.skinrestorer.mixin;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.SkinResult;
import net.minecraft.network.ClientConnection;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.PlayerManager;
import net.minecraft.server.network.ConnectedClientData;
import net.minecraft.server.network.ServerPlayerEntity;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Collections;
import java.util.List;
@Mixin(PlayerManager.class)
public abstract class PlayerManagerMixin {
@Shadow
public abstract List<ServerPlayerEntity> getPlayerList();
@Shadow @Final
private MinecraftServer server;
@Inject(method = "remove", at = @At("TAIL"))
private void remove(ServerPlayerEntity player, CallbackInfo ci) {
SkinRestorer.getSkinStorage().removeSkin(player.getUuid());
}
@Inject(method = "disconnectAllPlayers", at = @At("HEAD"))
private void disconnectAllPlayers(CallbackInfo ci) {
for (ServerPlayerEntity player : getPlayerList()) {
SkinRestorer.getSkinStorage().removeSkin(player.getUuid());
}
}
@Inject(method = "onPlayerConnect", at = @At("HEAD"))
private void onPlayerConnected(ClientConnection connection, ServerPlayerEntity player, ConnectedClientData clientData, CallbackInfo ci) {
SkinRestorer.setSkinAsync(server, Collections.singleton(player.getGameProfile()), () -> SkinResult.ofNullable(SkinRestorer.getSkinStorage().getSkin(player.getUuid())));
}
}

View File

@@ -0,0 +1,49 @@
package net.lionarius.skinrestorer.mixin;
import com.mojang.authlib.GameProfile;
import net.lionarius.skinrestorer.MojangSkinProvider;
import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.SkinResult;
import net.minecraft.server.network.ServerLoginNetworkHandler;
import org.jetbrains.annotations.Nullable;
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;
import java.util.concurrent.CompletableFuture;
@Mixin(ServerLoginNetworkHandler.class)
public abstract class ServerLoginNetworkHandlerMixin {
@Shadow @Nullable
private GameProfile profile;
@Unique
private CompletableFuture<SkinResult> skinrestorer_pendingSkin;
@Inject(method = "tickVerify", at = @At(value = "INVOKE",
target = "Lnet/minecraft/server/PlayerManager;checkCanJoin(Ljava/net/SocketAddress;Lcom/mojang/authlib/GameProfile;)Lnet/minecraft/text/Text;"),
cancellable = true)
public void waitForSkin(CallbackInfo ci) {
if (skinrestorer_pendingSkin == null) {
skinrestorer_pendingSkin = CompletableFuture.supplyAsync(() -> {
SkinRestorer.LOGGER.debug("Fetching {}'s skin", profile.getName());
if (!SkinRestorer.getSkinStorage().hasSavedSkin(profile.getId())) { // when player joins for the first time fetch Mojang skin by his username
SkinResult result = MojangSkinProvider.getSkin(profile.getName());
if (!result.isError())
SkinRestorer.getSkinStorage().setSkin(profile.getId(), result.getSkin());
}
return SkinResult.ofNullable(SkinRestorer.getSkinStorage().getSkin(profile.getId()));
});
}
if (!skinrestorer_pendingSkin.isDone())
ci.cancel();
}
}

View File

@@ -0,0 +1,33 @@
package net.lionarius.skinrestorer.util;
import java.io.*;
import java.nio.charset.StandardCharsets;
public class FileUtils {
public static String readFile(File file) {
try (BufferedReader reader = new BufferedReader(new FileReader(file, StandardCharsets.UTF_8))) {
return StringUtils.readString(reader);
} catch (IOException e) {
return null;
}
}
public static boolean writeFile(File path, String fileName, String content) {
try {
if (!path.exists())
path.mkdirs();
File file = new File(path, fileName);
if (!file.exists())
file.createNewFile();
try (FileWriter writer = new FileWriter(file, StandardCharsets.UTF_8)) {
writer.write(content);
}
return true;
} catch (IOException e) {
return false;
}
}
}

View File

@@ -0,0 +1,23 @@
package net.lionarius.skinrestorer.util;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class JsonUtils {
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
public static <T> T fromJson(String json, Class<T> clazz) {
return GSON.fromJson(json, clazz);
}
public static String toJson(Object obj) {
return GSON.toJson(obj);
}
public static JsonObject parseJson(String json) {
return JsonParser.parseString(json).getAsJsonObject();
}
}

View File

@@ -0,0 +1,10 @@
package net.lionarius.skinrestorer.util;
import net.minecraft.server.network.ServerPlayerEntity;
public class PlayerUtils {
public static boolean isFakePlayer(ServerPlayerEntity player) {
return player.getClass() != ServerPlayerEntity.class; // if the player isn't a server player entity, it must be someone's fake player
}
}

View File

@@ -0,0 +1,18 @@
package net.lionarius.skinrestorer.util;
import java.io.BufferedReader;
import java.io.IOException;
public class StringUtils {
public static String readString(BufferedReader reader) throws IOException {
String inputLine;
StringBuilder response = new StringBuilder();
while ((inputLine = reader.readLine()) != null) {
response.append(inputLine);
}
return response.toString();
}
}

View File

@@ -0,0 +1,31 @@
package net.lionarius.skinrestorer.util;
import net.fabricmc.loader.api.FabricLoader;
import net.lionarius.skinrestorer.SkinRestorer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Objects;
public class TranslationUtils {
public static class Translation {
public String skinActionAffectedProfile = "Skin has been saved for %s";
public String skinActionAffectedPlayer = "Apply live skin changes for %s";
public String skinActionFailed = "Failed to set skin";
public String skinActionOk = "Skin changed";
}
public static Translation translation = new Translation();
static {
Path path = FabricLoader.getInstance().getConfigDir().resolve("skinrestorer").resolve("translation.json");
if (Files.exists(path)) {
try {
translation = JsonUtils.fromJson(Objects.requireNonNull(FileUtils.readFile(path.toFile())), Translation.class);
} catch (Exception ex) {
SkinRestorer.LOGGER.error("Failed to load translation", ex);
}
}
}
}

View File

@@ -0,0 +1,43 @@
package net.lionarius.skinrestorer.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
public class WebUtils {
public static String POSTRequest(URL url, String userAgent, String contentType, String responseType, String input)
throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", contentType);
connection.setRequestProperty("Accept", responseType);
connection.setRequestProperty("User-Agent", userAgent);
connection.setDoOutput(true);
connection.setDoInput(true);
try (OutputStream os = connection.getOutputStream()) {
os.write(input.getBytes(StandardCharsets.UTF_8), 0, input.length());
}
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
return StringUtils.readString(br);
}
}
public static String GETRequest(URL url) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setDoOutput(true);
try (BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8))) {
return StringUtils.readString(br);
}
}
}

View File

@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "net.lionarius.skinrestorer.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"CommandManagerMixin",
"PlayerManagerMixin",
"ServerLoginNetworkHandlerMixin"
],
"injectors": {
"defaultRequire": 1
}
}