diff --git a/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/SkullTileEntityMixin.java b/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/SkullTileEntityMixin.java new file mode 100644 index 00000000..65a0cf72 --- /dev/null +++ b/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/SkullTileEntityMixin.java @@ -0,0 +1,96 @@ +package io.izzel.arclight.mixin.core.tileentity; + +import com.google.common.base.Predicate; +import com.google.common.base.Throwables; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.LoadingCache; +import com.google.common.collect.Iterables; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.mojang.authlib.GameProfile; +import com.mysql.jdbc.StringUtils; +import io.izzel.arclight.bridge.server.MinecraftServerBridge; +import io.izzel.arclight.mod.util.ArclightHeadLoader; +import net.minecraft.tileentity.SkullTileEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.CraftServer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Locale; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +@Mixin(SkullTileEntity.class) +public abstract class SkullTileEntityMixin extends TileEntityMixin { + + // @formatter:off + @Shadow public GameProfile playerProfile; + // @formatter:on + + private static ExecutorService executor = Executors.newFixedThreadPool(3, + new ThreadFactoryBuilder() + .setNameFormat("Head Conversion Thread - %1$d") + .build() + ); + + private static LoadingCache skinCache = CacheBuilder.newBuilder().maximumSize(5000L).expireAfterAccess(60L, TimeUnit.MINUTES).build(new ArclightHeadLoader()); + + /** + * @author IzzelAliz + * @reason + */ + @Overwrite + private void updatePlayerProfile() { + GameProfile profile = this.playerProfile; + b(profile, input -> { + playerProfile = input; + markDirty(); + return false; + }, false); + } + + @SuppressWarnings({"UnusedReturnValue", "ConstantConditions"}) + private static Future b(GameProfile gameprofile, Predicate callback, boolean sync) { + if (gameprofile != null && !StringUtils.isNullOrEmpty(gameprofile.getName())) { + if (gameprofile.isComplete() && gameprofile.getProperties().containsKey("textures")) { + callback.apply(gameprofile); + } else if (Bukkit.getServer() == null || ((CraftServer) Bukkit.getServer()).getServer() == null) { + callback.apply(gameprofile); + } else { + GameProfile profile = skinCache.getIfPresent(gameprofile.getName().toLowerCase(Locale.ROOT)); + if (profile != null && Iterables.getFirst((profile.getProperties()).get("textures"), null) != null) { + callback.apply(profile); + return Futures.immediateFuture(profile); + } + Callable callable = () -> { + GameProfile profile1 = skinCache.getUnchecked(gameprofile.getName().toLowerCase(Locale.ROOT)); + ((MinecraftServerBridge) ((CraftServer) Bukkit.getServer()).getServer()).bridge$queuedProcess(() -> { + if (profile1 == null) { + callback.apply(gameprofile); + } else { + callback.apply(profile1); + } + }); + return profile1; + }; + if (sync) { + try { + return Futures.immediateFuture(callable.call()); + } catch (Exception ex) { + Throwables.throwIfUnchecked(ex); + throw new RuntimeException(ex); + } + } + return executor.submit(callable); + } + } else { + callback.apply(gameprofile); + } + return Futures.immediateFuture(gameprofile); + } +} diff --git a/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/TileEntityMixin.java b/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/TileEntityMixin.java index 7ff8fc06..1b5817ad 100644 --- a/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/TileEntityMixin.java +++ b/arclight-coremod/src/main/java/io/izzel/arclight/mixin/core/tileentity/TileEntityMixin.java @@ -29,6 +29,7 @@ public abstract class TileEntityMixin implements TileEntityBridge { @Shadow @Nullable protected World world; @Shadow protected BlockPos pos; @Shadow public abstract BlockState getBlockState(); + @Shadow public abstract void markDirty(); // @formatter:on @Inject(method = "read", at = @At("RETURN")) diff --git a/arclight-coremod/src/main/java/io/izzel/arclight/mod/ArclightMixinPlugin.java b/arclight-coremod/src/main/java/io/izzel/arclight/mod/ArclightMixinPlugin.java index 8029732f..b22d8514 100644 --- a/arclight-coremod/src/main/java/io/izzel/arclight/mod/ArclightMixinPlugin.java +++ b/arclight-coremod/src/main/java/io/izzel/arclight/mod/ArclightMixinPlugin.java @@ -82,6 +82,16 @@ public class ArclightMixinPlugin implements IMixinConfigPlugin { new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "calculateBoundingBox", "(Lnet/minecraft/entity/Entity;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/Direction;II)Lnet/minecraft/util/math/AxisAlignedBB;", null, null) ) )) + .put("net.minecraft.tileentity.SkullTileEntity", + Maps.immutableEntry( + ImmutableList.of( + new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "executor", "Ljava/util/concurrent/ExecutorService;", null, null), + new FieldNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "skinCache", "Lcom/google/common/cache/LoadingCache;", null, null) + ), + ImmutableList.of( + new MethodNode(Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, "b", "(Lcom/mojang/authlib/GameProfile;Lcom/google/common/base/Predicate;Z)Ljava/util/concurrent/Future;", null, null) + ) + )) .build(); private final Set modifyConstructor = ImmutableSet.builder() diff --git a/arclight-coremod/src/main/java/io/izzel/arclight/mod/util/ArclightHeadLoader.java b/arclight-coremod/src/main/java/io/izzel/arclight/mod/util/ArclightHeadLoader.java new file mode 100644 index 00000000..35f92f33 --- /dev/null +++ b/arclight-coremod/src/main/java/io/izzel/arclight/mod/util/ArclightHeadLoader.java @@ -0,0 +1,46 @@ +package io.izzel.arclight.mod.util; + +import com.google.common.cache.CacheLoader; +import com.google.common.collect.Iterables; +import com.mojang.authlib.Agent; +import com.mojang.authlib.GameProfile; +import com.mojang.authlib.ProfileLookupCallback; +import com.mojang.authlib.properties.Property; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.tileentity.SkullTileEntity; +import org.bukkit.Bukkit; +import org.bukkit.craftbukkit.v1_14_R1.CraftServer; + +import java.util.UUID; + +public class ArclightHeadLoader extends CacheLoader { + + @Override + public GameProfile load(String key) { + GameProfile[] profiles = {null}; + ProfileLookupCallback gameProfileLookup = new ProfileLookupCallback() { + @Override + public void onProfileLookupSucceeded(GameProfile gp) { + profiles[0] = gp; + } + + @Override + public void onProfileLookupFailed(GameProfile gp, Exception excptn) { + profiles[0] = gp; + } + }; + ((CraftServer) Bukkit.getServer()).getServer().getGameProfileRepository().findProfilesByNames(new String[]{key}, Agent.MINECRAFT, gameProfileLookup); + GameProfile profile = profiles[0]; + if (profile == null) { + UUID uuid = PlayerEntity.getUUID(new GameProfile(null, key)); + profile = new GameProfile(uuid, key); + gameProfileLookup.onProfileLookupSucceeded(profile); + } else { + Property property = Iterables.getFirst((profile.getProperties()).get("textures"), null); + if (property == null) { + profile = SkullTileEntity.sessionService.fillProfileProperties(profile, true); + } + } + return profile; + } +} diff --git a/arclight-coremod/src/main/resources/META-INF/accesstransformer.cfg b/arclight-coremod/src/main/resources/META-INF/accesstransformer.cfg index 47291269..182f14a5 100644 --- a/arclight-coremod/src/main/resources/META-INF/accesstransformer.cfg +++ b/arclight-coremod/src/main/resources/META-INF/accesstransformer.cfg @@ -16,6 +16,7 @@ public net.minecraft.util.math.shapes.IDoubleListMerger public net.minecraft.util.math.shapes.IndirectMerger (Lit/unimi/dsi/fastutil/doubles/DoubleList;Lit/unimi/dsi/fastutil/doubles/DoubleList;ZZ)V public net.minecraft.util.math.shapes.DoubleCubeMergingList (II)V public net.minecraft.block.ComposterBlock$EmptyInventory +public net.minecraft.tileentity.SkullTileEntity field_184299_k #sessionService # Bukkit public net.minecraft.entity.player.PlayerEntity func_190531_bD()I public net.minecraft.entity.item.ItemFrameEntity func_174859_a(Lnet/minecraft/util/Direction;)V diff --git a/arclight-coremod/src/main/resources/mixins.arclight.core.json b/arclight-coremod/src/main/resources/mixins.arclight.core.json index 2232b2e6..92d6ea21 100644 --- a/arclight-coremod/src/main/resources/mixins.arclight.core.json +++ b/arclight-coremod/src/main/resources/mixins.arclight.core.json @@ -339,6 +339,7 @@ "tileentity.LockableTileEntityMixin", "tileentity.ShulkerBoxTileEntityMixin", "tileentity.SignTileEntityMixin", + "tileentity.SkullTileEntityMixin", "tileentity.TileEntityMixin", "util.BootstrapMixin", "util.DamageSourceMixin",