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

Compare commits

...

13 Commits

Author SHA1 Message Date
abcb37fc6b Merge branch '1.20.3-multiloader' into 1.20.2-multiloader
# Conflicts:
#	common/src/main/java/net/lionarius/skinrestorer/mixin/SkullBlockEntityMixin.java
2025-08-17 18:54:00 +03:00
b3b524c53a Merge branch '1.20.5-multiloader' into 1.20.3-multiloader 2025-08-17 18:46:05 +03:00
e6b2dcb4eb Merge branch '1.21-multiloader' into 1.20.5-multiloader 2025-08-17 18:30:22 +03:00
5f8a868e5c backport to 1.21 2025-08-17 18:28:46 +03:00
fce13237f9 Merge branch '1.21.5-multiloader' into 1.21-multiloader 2025-08-17 18:25:45 +03:00
c987d48738 Merge branch '1.21.6-multiloader' into 1.21.5-multiloader
# Conflicts:
#	forge/src/main/java/net/lionarius/skinrestorer/forge/SkinRestorerForge.java
#	gradle.properties
2025-08-17 18:24:03 +03:00
583205febc update CHANGELOG 2025-08-17 17:02:22 +03:00
fb7a4d11e9 bump version 2025-08-17 17:02:22 +03:00
1d8472e35c fix server freeze when loading head 2025-08-17 17:02:00 +03:00
f604ac2c8b add support for additional modloaders when publishing 2025-07-24 03:33:05 +03:00
8d4c2e9f83 add 1.21.8 to supported versions 2025-07-24 03:22:56 +03:00
84a06898a2 do not replace skin for head if on client and not in singleplayer 2025-07-24 03:21:43 +03:00
11ecf88eb2 add OnServerStopped event 2025-07-24 03:20:16 +03:00
19 changed files with 91 additions and 11 deletions

View File

@@ -4,6 +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/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.4.3] - 2025-07-25
### Fixed
- Fixed crash on client when loading player head skin (fixes [#63](https://github.com/Suiranoil/SkinRestorer/issues/63) and [#64](https://github.com/Suiranoil/SkinRestorer/issues/64))
- Fixed server freeze when loading player head skin
## [2.4.2] - 2025-07-13 ## [2.4.2] - 2025-07-13
### Fixed ### Fixed
- Fix crash when head profile name is null (fixes [#60](https://github.com/Suiranoil/SkinRestorer/issues/60) and [#61](https://github.com/Suiranoil/SkinRestorer/issues/61)) - Fix crash when head profile name is null (fixes [#60](https://github.com/Suiranoil/SkinRestorer/issues/60) and [#61](https://github.com/Suiranoil/SkinRestorer/issues/61))

View File

@@ -1,2 +1,3 @@
### Fixed ### Fixed
- Fix crash when head profile name is null (fixes [#60](https://github.com/Suiranoil/SkinRestorer/issues/60) and [#61](https://github.com/Suiranoil/SkinRestorer/issues/61)) - Fixed crash on client when loading player head skin (fixes [#63](https://github.com/Suiranoil/SkinRestorer/issues/63) and [#64](https://github.com/Suiranoil/SkinRestorer/issues/64))
- Fixed server freeze when loading player head skin

View File

@@ -6,6 +6,14 @@ publishMods {
file = project.layout.buildDirectory.file("libs/${project.archivesBaseName}-${project.version}.jar").map { it.asFile }.getOrNull() file = project.layout.buildDirectory.file("libs/${project.archivesBaseName}-${project.version}.jar").map { it.asFile }.getOrNull()
modLoaders.add(project.name) modLoaders.add(project.name)
if (project.hasProperty('additional_modloaders') && !additional_modloaders.isEmpty())
{
def loaders = additional_modloaders.split(',')
for (loader in loaders)
modLoaders.add(loader)
}
type = STABLE type = STABLE
version = project.version version = project.version
displayName = "[${project.name.capitalize()}] ${mod_name} ${mod_version}" displayName = "[${project.name.capitalize()}] ${mod_name} ${mod_version}"

View File

@@ -19,6 +19,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.level.storage.LevelResource; import net.minecraft.world.level.storage.LevelResource;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -37,6 +38,7 @@ public final class SkinRestorer {
private static Path configDir; private static Path configDir;
private static Config config; private static Config config;
private static TickedScheduler tickedScheduler; private static TickedScheduler tickedScheduler;
private static MinecraftServer minecraftServer;
private SkinRestorer() {} private SkinRestorer() {}
@@ -60,6 +62,10 @@ public final class SkinRestorer {
return SkinRestorer.tickedScheduler; return SkinRestorer.tickedScheduler;
} }
public static @Nullable MinecraftServer getMinecraftServer() {
return SkinRestorer.minecraftServer;
}
public static Optional<SkinProvider> getProvider(String name) { public static Optional<SkinProvider> getProvider(String name) {
return Optional.ofNullable(SkinRestorer.providersRegistry.get(name)); return Optional.ofNullable(SkinRestorer.providersRegistry.get(name));
} }
@@ -173,6 +179,14 @@ public final class SkinRestorer {
SkinRestorer.skinStorage = new SkinStorage(new SkinIO(worldSkinDirectory)); SkinRestorer.skinStorage = new SkinStorage(new SkinIO(worldSkinDirectory));
SkinRestorer.tickedScheduler = new TickedScheduler(server); SkinRestorer.tickedScheduler = new TickedScheduler(server);
server.addTickable(SkinRestorer.tickedScheduler); server.addTickable(SkinRestorer.tickedScheduler);
SkinRestorer.minecraftServer = server;
}
public static void onServerStopped(MinecraftServer server) {
SkinRestorer.skinStorage = null;
SkinRestorer.tickedScheduler = null;
SkinRestorer.minecraftServer = null;
} }
public static void onCommandRegister(CommandDispatcher<CommandSourceStack> dispatcher) { public static void onCommandRegister(CommandDispatcher<CommandSourceStack> dispatcher) {

View File

@@ -9,5 +9,5 @@ import org.spongepowered.asm.mixin.gen.Accessor;
public interface ChunkMapAccessor { public interface ChunkMapAccessor {
@Accessor @Accessor
Int2ObjectMap<Object> getEntityMap(); Int2ObjectMap<TrackedEntityAccessorInvoker> getEntityMap();
} }

View File

@@ -0,0 +1,14 @@
package net.lionarius.skinrestorer.mixin;
import net.minecraft.server.players.GameProfileCache;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.Map;
@Mixin(GameProfileCache.class)
public interface GameProfileCacheAccessor {
@Accessor
Map<String, GameProfileCache.GameProfileInfo> getProfilesByName();
}

View File

@@ -3,6 +3,7 @@ package net.lionarius.skinrestorer.mixin;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import net.lionarius.skinrestorer.SkinRestorer; import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.util.PlayerUtils; import net.lionarius.skinrestorer.util.PlayerUtils;
import net.minecraft.Util;
import net.minecraft.server.Services; import net.minecraft.server.Services;
import net.minecraft.server.players.GameProfileCache; import net.minecraft.server.players.GameProfileCache;
import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.block.entity.SkullBlockEntity;
@@ -13,6 +14,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
@@ -33,13 +35,20 @@ public abstract class SkullBlockEntityMixin {
if (name == null) if (name == null)
return; return;
var profileOpt = profileCache.get(name); var profileOpt = Optional.<GameProfile>empty();
var gameProfileInfo = ((GameProfileCacheAccessor) profileCache).getProfilesByName().get(name.toLowerCase(Locale.ROOT));
if (gameProfileInfo != null)
profileOpt = Optional.of(gameProfileInfo.getProfile());
skinrestorer$replaceSkin(profileOpt, cir); skinrestorer$replaceSkin(profileOpt, cir);
} }
@Unique @Unique
private static void skinrestorer$replaceSkin(Optional<GameProfile> profileOpt, CallbackInfoReturnable<CompletableFuture<Optional<GameProfile>>> cir) { private static void skinrestorer$replaceSkin(Optional<GameProfile> profileOpt, CallbackInfoReturnable<CompletableFuture<Optional<GameProfile>>> cir) {
if (SkinRestorer.getMinecraftServer() == null)
return;
if (profileOpt.isEmpty()) if (profileOpt.isEmpty())
return; return;
@@ -51,7 +60,7 @@ public abstract class SkullBlockEntityMixin {
PlayerUtils.applyRestoredSkin(profile, skin.value()); PlayerUtils.applyRestoredSkin(profile, skin.value());
return Optional.of(profile); return Optional.of(profile);
})); }, Util.backgroundExecutor()));
} }
} }
} }

View File

@@ -50,14 +50,14 @@ public final class PlayerUtils {
) )
)); ));
var trackedEntity = (TrackedEntityAccessorInvoker) ((ChunkMapAccessor) chunkMap).getEntityMap().get(player.getId()); var trackedEntity = ((ChunkMapAccessor) chunkMap).getEntityMap().get(player.getId());
if (trackedEntity != null) { if (trackedEntity != null) {
var seenBy = Set.copyOf(trackedEntity.getSeenBy()); var seenBy = Set.copyOf(trackedEntity.getSeenBy());
for (var observerConnection : seenBy) { for (var observerConnection : seenBy) {
var observer = observerConnection.getPlayer(); var observer = observerConnection.getPlayer();
trackedEntity.invokeRemovePlayer(observer); trackedEntity.invokeRemovePlayer(observer);
var trackedObserverEntity = (TrackedEntityAccessorInvoker) ((ChunkMapAccessor) chunkMap).getEntityMap().get(observer.getId()); var trackedObserverEntity = ((ChunkMapAccessor) chunkMap).getEntityMap().get(observer.getId());
if (trackedObserverEntity != null) { if (trackedObserverEntity != null) {
trackedObserverEntity.invokeRemovePlayer(player); trackedObserverEntity.invokeRemovePlayer(player);
trackedObserverEntity.invokeUpdatePlayer(player); trackedObserverEntity.invokeUpdatePlayer(player);

View File

@@ -0,0 +1 @@
public net.minecraft.server.players.GameProfileCache$GameProfileInfo

View File

@@ -0,0 +1,3 @@
accessWidener v2 named
accessible class net/minecraft/server/players/GameProfileCache$GameProfileInfo

View File

@@ -10,7 +10,8 @@
"PlayerListMixin", "PlayerListMixin",
"ServerLoginPacketListenerImplMixin", "ServerLoginPacketListenerImplMixin",
"TrackedEntityAccessorInvoker", "TrackedEntityAccessorInvoker",
"SkullBlockEntityMixin" "SkullBlockEntityMixin",
"GameProfileCacheAccessor"
], ],
"injectors": { "injectors": {
"defaultRequire": 1 "defaultRequire": 1

View File

@@ -3,3 +3,4 @@ fabric_loader_version=0.15.0
fabric_api_version=0.89.0+1.20.2 fabric_api_version=0.89.0+1.20.2
optional_dependencies=fabric-api optional_dependencies=fabric-api
additional_modloaders=quilt

View File

@@ -15,4 +15,10 @@ public abstract class MinecraftServerMixin {
private void onServerStarted(CallbackInfo ci) { private void onServerStarted(CallbackInfo ci) {
SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this); SkinRestorer.Events.onServerStarted((MinecraftServer) (Object) this);
} }
@Inject(method = "runServer",
at = @At(value = "INVOKE", target = "Lnet/minecraft/server/MinecraftServer;onServerExit()V"))
private void onServerStopped(CallbackInfo ci) {
SkinRestorer.Events.onServerStopped((MinecraftServer) (Object) this);
}
} }

View File

@@ -24,6 +24,7 @@
"${mod_id}.mixins.json", "${mod_id}.mixins.json",
"${mod_id}.fabric.mixins.json" "${mod_id}.fabric.mixins.json"
], ],
"accessWidener": "${mod_id}.accesswidener",
"depends": { "depends": {
"fabricloader": ">=${fabric_loader_version}", "fabricloader": ">=${fabric_loader_version}",
"minecraft": ">=${minecraft_version}", "minecraft": ">=${minecraft_version}",

View File

@@ -33,7 +33,7 @@ minecraft {
// Automatically enable forge AccessTransformers if the file exists // Automatically enable forge AccessTransformers if the file exists
// This location is hardcoded in Forge and can not be changed. // This location is hardcoded in Forge and can not be changed.
// https://github.com/MinecraftForge/MinecraftForge/blob/be1698bb1554f9c8fa2f58e32b9ab70bc4385e60/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java#L123 // https://github.com/MinecraftForge/MinecraftForge/blob/be1698bb1554f9c8fa2f58e32b9ab70bc4385e60/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java#L123
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') def at = project(":common").file('src/main/resources/META-INF/accesstransformer.cfg')
if (at.exists()) { if (at.exists()) {
accessTransformer = at accessTransformer = at
} }
@@ -88,3 +88,7 @@ sourceSets.each {
it.output.resourcesDir = dir it.output.resourcesDir = dir
it.java.destinationDirectory = dir it.java.destinationDirectory = dir
} }
tasks.named('processResources') {
duplicatesStrategy = DuplicatesStrategy.INCLUDE
}

View File

@@ -4,6 +4,7 @@ import net.lionarius.skinrestorer.SkinRestorer;
import net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility; import net.lionarius.skinrestorer.compat.skinshuffle.SkinShuffleCompatibility;
import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.server.ServerStartedEvent; import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.event.server.ServerStoppedEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@@ -27,4 +28,9 @@ public final class SkinRestorerForge {
public static void onServerStarted(ServerStartedEvent event) { public static void onServerStarted(ServerStartedEvent event) {
SkinRestorer.Events.onServerStarted(event.getServer()); SkinRestorer.Events.onServerStarted(event.getServer());
} }
@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event) {
SkinRestorer.Events.onServerStopped(event.getServer());
}
} }

View File

@@ -8,7 +8,7 @@ minecraft_version_list=1.20.2
minecraft_version_range=1.20.2 minecraft_version_range=1.20.2
mod_id=skinrestorer mod_id=skinrestorer
mod_name=SkinRestorer mod_name=SkinRestorer
mod_version=2.4.2 mod_version=2.4.3
mod_author=Lionarius mod_author=Lionarius
mod_homepage=https://modrinth.com/mod/skinrestorer mod_homepage=https://modrinth.com/mod/skinrestorer
mod_sources=https://github.com/Suiranoil/SkinRestorer mod_sources=https://github.com/Suiranoil/SkinRestorer

View File

@@ -12,9 +12,9 @@ neoForge {
version = neoforge_version version = neoforge_version
// Automatically enable neoforge AccessTransformers if the file exists // Automatically enable neoforge AccessTransformers if the file exists
def at = project(':common').file('src/main/resources/META-INF/accesstransformer.cfg') def at = project(":common").file('src/main/resources/META-INF/accesstransformer.cfg')
if (at.exists()) { if (at.exists()) {
minecraft.accessTransformers.file(at) accessTransformers.from(at.absolutePath)
} }
parchment { parchment {

View File

@@ -6,6 +6,7 @@ import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.Mod; import net.neoforged.fml.common.Mod;
import net.neoforged.neoforge.event.RegisterCommandsEvent; import net.neoforged.neoforge.event.RegisterCommandsEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent; import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppedEvent;
@Mod(SkinRestorer.MOD_ID) @Mod(SkinRestorer.MOD_ID)
@Mod.EventBusSubscriber(modid = SkinRestorer.MOD_ID) @Mod.EventBusSubscriber(modid = SkinRestorer.MOD_ID)
@@ -27,4 +28,9 @@ public final class SkinRestorerNeoForge {
public static void onServerStarted(ServerStartedEvent event) { public static void onServerStarted(ServerStartedEvent event) {
SkinRestorer.Events.onServerStarted(event.getServer()); SkinRestorer.Events.onServerStarted(event.getServer());
} }
@SubscribeEvent
public static void onServerStopped(ServerStoppedEvent event) {
SkinRestorer.Events.onServerStopped(event.getServer());
}
} }