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

Compare commits

..

22 Commits

Author SHA1 Message Date
0c9b4d44cb bump version 2024-06-25 14:55:25 +03:00
2fca98e0f2 better skin refresh 2024-06-25 14:55:25 +03:00
3cba252b5b allow command blocks to execute commands 2024-06-25 14:55:25 +03:00
592cfb6ca4 fix no permission level for clear command targets 2024-06-25 14:55:25 +03:00
Suiranoil
decad54884 Update README.md 2024-06-25 07:57:58 +03:00
3dd75626a1 downgrade version to 1.2.5 2024-06-25 06:46:12 +03:00
d720fc52ef fix missing constructor 2024-06-25 06:44:35 +03:00
b3a618f50b update minecraft to 1.21 2024-06-25 06:42:50 +03:00
8724d6dd41 fix github actions 2024-06-25 06:00:50 +03:00
baa1228f23 update github actions 2024-06-25 05:55:42 +03:00
16dc4bdc50 bump version 2024-06-25 05:53:56 +03:00
ed3bc666db add keepFading arg to status packet 2024-06-25 05:46:57 +03:00
694aca9825 update minecraft to 1.20.5 2024-06-25 05:42:05 +03:00
7e38f06a84 update loom to 1.7 2024-06-25 05:41:53 +03:00
cd48efb3d2 bump version 2024-06-25 05:15:02 +03:00
1ad0a2bd3a fix loading world screen 2024-06-25 05:02:23 +03:00
5e12356940 update version to 1.20.3 2024-06-25 05:01:39 +03:00
Suiranoil
43e775f53a Update README.md 2023-10-02 11:01:27 +03:00
Suiranoil
25eeeb2c30 update gradle 2023-09-30 09:12:56 +03:00
Suiranoil
e11696fe96 update gradle 2023-09-30 09:10:46 +03:00
Suiranoil
8935ba0028 update github actions 2023-09-30 09:06:37 +03:00
Suiranoil
24eef36890 update buildscript 2023-09-30 08:48:17 +03:00
9 changed files with 129 additions and 122 deletions

View File

@@ -1,20 +1,33 @@
name: Compiling
on:
push:
pull_request:
name: build
on: [pull_request, push]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
java: [
21,
]
os: [ubuntu-22.04]
runs-on: ${{ matrix.os }}
steps:
- name: Compile
uses: Ruochenfu2011/compilation@v1
- name: Archive Artifacts
uses: actions/upload-artifact@v2
with:
name: Artifacts
path: ./build/libs
- name: checkout repository
uses: actions/checkout@v3
- name: validate gradle wrapper
uses: gradle/wrapper-validation-action@v1
- name: setup jdk ${{ matrix.java }}
uses: actions/setup-java@v3
with:
java-version: ${{ matrix.java }}
distribution: 'microsoft'
- name: make gradle wrapper executable
if: ${{ runner.os != 'Windows' }}
run: chmod +x ./gradlew
- name: build
run: ./gradlew build
- name: capture build artifacts
if: ${{ runner.os == 'Linux' && matrix.java == '21' }}
uses: actions/upload-artifact@v3
with:
name: Artifacts
path: build/libs/

View File

@@ -1,6 +1,49 @@
# SkinRestorer
This is a server-side only mod for fabric.
![GitHub License](https://img.shields.io/github/license/Suiranoil/SkinRestorer)
![Build](https://github.com/Suiranoil/SkinRestorer/actions/workflows/build.yml/badge.svg)
![Enviroment](https://img.shields.io/badge/enviroment-server-orangered)
[![CurseForge Downloads](https://cf.way2muchnoise.eu/skinrestorer.svg)](https://www.curseforge.com/minecraft/mc-mods/skinrestorer)
[![Modrinth Downloads](https://img.shields.io/modrinth/dt/skinrestorer?logo=modrinth)](https://modrinth.com/mod/skinrestorer)
It allows you to use and change skins on servers that are in offline/insecure mode.
After installation on server use /skin command to change skin.
SkinRestorer is a **server-side** only mod for Fabric that allows players to use and change skins on servers running in offline/insecure mode.
## Features
- **Set Skins from Mojang Account**: Fetch and apply skins using a valid Minecraft account name.
- **Set Skins from URL**: Apply skins from any image URL, supporting both classic (Steve) and slim (Alex) skin models.
## Command Usage Guide
### Set Mojang Skin
```
/skin set mojang <skin_name> [<targets>]
```
- **Parameters:**
- `<skin_name>`: Minecraft account name to fetch the skin from.
- `[<targets>]`: (Optional, server operators only) Player(s) to apply the skin to.
### Set Web Skin
```
/skin set web (classic|slim) "<url>" [<targets>]
```
- **Parameters:**
- `(classic|slim)`: Type of the skin model (`classic` for Steve model, `slim` for Alex model).
- `"<url>"`: URL pointing to the skin image file (ensure it follows Minecraft's skin size and format requirements).
- `[<targets>]`: (Optional, server operators only) Player(s) to apply the skin to.
### Clear Skin
```
/skin clear [<targets>]
```
- **Parameters:**
- `[<targets>]`: (Optional, server operators only) Player(s) to clear the skin for.
### Notes:
- If `targets` is not specified, the command will apply to the player executing the command.
### Examples:
```
/skin set mojang Notch
/skin set web classic "http://example.com/skin.png"
/skin clear @a
```

View File

@@ -1,32 +1,19 @@
plugins {
id 'fabric-loom' version '1.0-SNAPSHOT'
id 'maven-publish'
id 'fabric-loom' version '1.7-SNAPSHOT'
}
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
archivesBaseName = project.archives_base_name
version = project.mod_version
version = "${project.mod_version}+${project.minecraft_version}"
group = project.maven_group
repositories {
// Add repositories to retrieve artifacts from in here.
// You should only use this when depending on other mods because
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
// See https://docs.gradle.org/current/userguide/declaring_repositories.html
// for more information about repositories.
maven { url = "https://masa.dy.fi/maven" }
}
dependencies {
// To change the versions see the gradle.properties file
minecraft "com.mojang:minecraft:${project.minecraft_version}"
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// PSA: Some older mods, compiled on Loom 0.2.1, might have outdated Maven POMs.
// You may need to force-disable transitiveness on them.
}
processResources {
@@ -38,19 +25,12 @@ processResources {
}
tasks.withType(JavaCompile).configureEach {
// ensure that the encoding is set to UTF-8, no matter what the system default is
// this fixes some edge cases with special characters not displaying correctly
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
// If Javadoc is generated, this must be specified in that task too.
it.options.encoding = "UTF-8"
it.options.release = 17
it.options.release.set(21)
}
java {
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
// if it is present.
// If you remove this line, sources will not be generated.
withSourcesJar()
}
@@ -59,20 +39,3 @@ jar {
rename { "${it}_${project.archivesBaseName}"}
}
}
// configure the maven publication
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
}
}
// See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing.
repositories {
// Add repositories to publish to here.
// Notice: This block does NOT have the same function as the block in the top level.
// The repositories here will be used for publishing your artifact, not for
// retrieving dependencies.
}
}

View File

@@ -1,11 +1,14 @@
# Done to increase the memory available to gradle.
org.gradle.jvmargs=-Xmx1G
org.gradle.parallel=true
# Fabric Properties
# check these on https://fabricmc.net/versions.html
minecraft_version=1.20.2
yarn_mappings=1.20.2+build.1
loader_version=0.14.22
minecraft_version=1.21
yarn_mappings=1.21+build.2
loader_version=0.15.11
# Mod Properties
mod_version=1.2.4
mod_version=1.2.6
maven_group=net.lionarius
archives_base_name=skin-restorer

Binary file not shown.

View File

@@ -1,6 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

19
gradlew vendored
View File

@@ -83,10 +83,8 @@ done
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -133,10 +131,13 @@ location of your Java installation."
fi
else
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
if ! command -v java >/dev/null 2>&1
then
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
fi
# Increase the maximum file descriptors if we can.
@@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,6 +198,10 @@ if "$cygwin" || "$msys" ; then
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in

View File

@@ -1,6 +1,5 @@
package net.lionarius.skinrestorer;
import com.google.common.collect.Lists;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
@@ -8,15 +7,12 @@ import com.mojang.authlib.properties.Property;
import it.unimi.dsi.fastutil.Pair;
import net.fabricmc.api.DedicatedServerModInitializer;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
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.world.biome.source.BiomeAccess;
import net.minecraft.server.world.ServerChunkManager;
import net.minecraft.server.world.ServerWorld;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -43,49 +39,32 @@ public class SkinRestorer implements DedicatedServerModInitializer {
}
public static void refreshPlayer(ServerPlayerEntity player) {
List<com.mojang.datafixers.util.Pair<EquipmentSlot, ItemStack>> equipment = Lists.newArrayList();
for (EquipmentSlot slot : EquipmentSlot.values()) {
ItemStack itemStack = player.getEquippedStack(slot);
if (!itemStack.isEmpty()) {
equipment.add(com.mojang.datafixers.util.Pair.of(slot, itemStack.copy()));
}
}
ServerWorld serverWorld = player.getServerWorld();
PlayerManager playerManager = serverWorld.getServer().getPlayerManager();
ServerChunkManager chunkManager = serverWorld.getChunkManager();
for (ServerPlayerEntity observer : player.server.getPlayerManager().getPlayerList()) {
observer.networkHandler.sendPacket(new PlayerRemoveS2CPacket(List.of(player.getUuid())));
observer.networkHandler.sendPacket(PlayerListS2CPacket.entryFromPlayer(Collections.singleton(player)));
if (observer == player)
continue;
observer.networkHandler.sendPacket(new EntitiesDestroyS2CPacket(player.getId()));
observer.networkHandler.sendPacket(new EntitySpawnS2CPacket(player));
observer.networkHandler.sendPacket(new EntityPositionS2CPacket(player));
observer.networkHandler.sendPacket(new EntityTrackerUpdateS2CPacket(player.getId(), player.getDataTracker().getChangedEntries()));
if (!equipment.isEmpty())
observer.networkHandler.sendPacket(new EntityEquipmentUpdateS2CPacket(player.getId(), equipment));
if (player.hasVehicle())
observer.networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player.getVehicle()));
}
player.networkHandler.sendPacket(new PlayerRespawnS2CPacket(player.createCommonPlayerSpawnInfo(player.getServerWorld()), (byte) 2));
playerManager.sendToAll(new BundleS2CPacket(
List.of(
new PlayerRemoveS2CPacket(List.of(player.getUuid())),
PlayerListS2CPacket.entryFromPlayer(Collections.singleton(player))
)
));
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 UpdateSelectedSlotS2CPacket(player.getInventory().selectedSlot));
player.networkHandler.sendPacket(new EntityTrackerUpdateS2CPacket(player.getId(), player.getDataTracker().getChangedEntries()));
player.networkHandler.sendPacket(new EntityVelocityUpdateS2CPacket(player));
player.sendAbilitiesUpdate();
player.playerScreenHandler.updateToClient();
player.networkHandler.sendPacket(new ExperienceBarUpdateS2CPacket(player.experienceProgress, player.totalExperience, player.experienceLevel));
player.networkHandler.sendPacket(new HealthUpdateS2CPacket(player.getHealth(), player.getHungerManager().getFoodLevel(), player.getHungerManager().getSaturationLevel()));
for (StatusEffectInstance instance : player.getStatusEffects())
player.networkHandler.sendPacket(new EntityStatusEffectS2CPacket(player.getId(), instance));
if (player.hasVehicle())
player.networkHandler.sendPacket(new EntityPassengersSetS2CPacket(player.getVehicle()));
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<Property> skinSupplier) {

View File

@@ -32,7 +32,7 @@ public class SkinCommand {
.executes(context ->
skinAction(context.getSource(),
() -> MojangSkinProvider.getSkin(StringArgumentType.getString(context, "skin_name"))))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(3))
.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")))))))
@@ -42,7 +42,7 @@ public class SkinCommand {
.executes(context ->
skinAction(context.getSource(),
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.CLASSIC)))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(3))
.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))))))
@@ -51,7 +51,7 @@ public class SkinCommand {
.executes(context ->
skinAction(context.getSource(),
() -> MineskinSkinProvider.getSkin(StringArgumentType.getString(context, "url"), SkinVariant.SLIM)))
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(3))
.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))))))))
@@ -59,7 +59,7 @@ public class SkinCommand {
.executes(context ->
skinAction(context.getSource(),
() -> DEFAULT_SKIN))
.then(argument("targets", GameProfileArgumentType.gameProfile()).executes(context ->
.then(argument("targets", GameProfileArgumentType.gameProfile()).requires(source -> source.hasPermissionLevel(2)).executes(context ->
skinAction(context.getSource(), GameProfileArgumentType.getProfileArgument(context, "targets"), true,
() -> DEFAULT_SKIN))))
);