refactor: replace fully qualified class names with imports and simplify code readability across rendering modules
This commit is contained in:
@@ -4,17 +4,29 @@ import eu.mhsl.minecraft.pixelpics.assets.AssetReader;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.BlockModelRegistry;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.ResourcePack;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.ResourcePackLoader;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.SkinCache;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.TextureCache;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.font.BitmapFont;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.font.FontLoader;
|
||||
import eu.mhsl.minecraft.pixelpics.commands.PixelPicsCommand;
|
||||
import eu.mhsl.minecraft.pixelpics.listeners.OnMapInitialize;
|
||||
import eu.mhsl.minecraft.pixelpics.render.RenderManager;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.cem.BlockEntityBaker;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.cem.CemBaker;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.cem.CemModelLoader;
|
||||
import eu.mhsl.minecraft.pixelpics.render.render.DefaultScreenRenderer;
|
||||
import eu.mhsl.minecraft.pixelpics.render.tint.BiomeTintProvider;
|
||||
import eu.mhsl.minecraft.pixelpics.survival.CameraListener;
|
||||
import eu.mhsl.minecraft.pixelpics.survival.CraftingListener;
|
||||
import eu.mhsl.minecraft.pixelpics.survival.JoinListener;
|
||||
import eu.mhsl.minecraft.pixelpics.survival.SurvivalRecipes;
|
||||
import eu.mhsl.minecraft.pixelpics.utils.MapColorPalette;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -45,10 +57,10 @@ public final class Main extends JavaPlugin {
|
||||
Bukkit.getPluginManager().registerEvents(new OnMapInitialize(), this);
|
||||
Objects.requireNonNull(Bukkit.getPluginCommand("pixelPic")).setExecutor(new PixelPicsCommand());
|
||||
|
||||
Bukkit.getPluginManager().registerEvents(new eu.mhsl.minecraft.pixelpics.survival.CameraListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new eu.mhsl.minecraft.pixelpics.survival.CraftingListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new eu.mhsl.minecraft.pixelpics.survival.JoinListener(), this);
|
||||
eu.mhsl.minecraft.pixelpics.survival.SurvivalRecipes.register();
|
||||
Bukkit.getPluginManager().registerEvents(new CameraListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new CraftingListener(), this);
|
||||
Bukkit.getPluginManager().registerEvents(new JoinListener(), this);
|
||||
SurvivalRecipes.register();
|
||||
|
||||
initRenderer();
|
||||
}
|
||||
@@ -87,33 +99,33 @@ public final class Main extends JavaPlugin {
|
||||
BlockModelRegistry registry = new BlockModelRegistry(reader, textures);
|
||||
BiomeTintProvider tintProvider = new BiomeTintProvider(textures);
|
||||
|
||||
eu.mhsl.minecraft.pixelpics.render.entity.cem.CemModelLoader cemLoader =
|
||||
new eu.mhsl.minecraft.pixelpics.render.entity.cem.CemModelLoader();
|
||||
try (java.io.InputStream in = getResource("cem/cem_template_models.json")) {
|
||||
CemModelLoader cemLoader =
|
||||
new CemModelLoader();
|
||||
try (InputStream in = getResource("cem/cem_template_models.json")) {
|
||||
int n = in == null ? 0 : cemLoader.load(in, getLogger());
|
||||
getLogger().info("Loaded " + n + " CEM entity models.");
|
||||
} catch (Exception e) {
|
||||
getLogger().severe("Failed to load CEM entity models: " + e.getMessage());
|
||||
}
|
||||
eu.mhsl.minecraft.pixelpics.assets.SkinCache skinCache = new eu.mhsl.minecraft.pixelpics.assets.SkinCache();
|
||||
eu.mhsl.minecraft.pixelpics.assets.font.BitmapFont font =
|
||||
eu.mhsl.minecraft.pixelpics.assets.font.FontLoader.load(resourcePack, textures, getLogger());
|
||||
SkinCache skinCache = new SkinCache();
|
||||
BitmapFont font =
|
||||
FontLoader.load(resourcePack, textures, getLogger());
|
||||
getLogger().info("Loaded sign font (" + (font.isEmpty() ? "no glyphs — text disabled" : "ok") + ").");
|
||||
eu.mhsl.minecraft.pixelpics.render.entity.cem.CemBaker entityBaker =
|
||||
new eu.mhsl.minecraft.pixelpics.render.entity.cem.CemBaker(cemLoader, textures, skinCache);
|
||||
eu.mhsl.minecraft.pixelpics.render.entity.cem.BlockEntityBaker blockEntityBaker =
|
||||
new eu.mhsl.minecraft.pixelpics.render.entity.cem.BlockEntityBaker(cemLoader, textures, skinCache, font);
|
||||
CemBaker entityBaker =
|
||||
new CemBaker(cemLoader, textures, skinCache);
|
||||
BlockEntityBaker blockEntityBaker =
|
||||
new BlockEntityBaker(cemLoader, textures, skinCache, font);
|
||||
|
||||
this.screenRenderer = new DefaultScreenRenderer(registry, tintProvider, textures, entityBaker,
|
||||
blockEntityBaker, getLogger(), renderManager.tracePool());
|
||||
// Warm the map palette on the main thread so off-thread dithering never triggers its first init.
|
||||
eu.mhsl.minecraft.pixelpics.utils.MapColorPalette.size();
|
||||
MapColorPalette.size();
|
||||
getLogger().info("PixelPics renderer initialized with resource pack assets.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
eu.mhsl.minecraft.pixelpics.survival.SurvivalRecipes.unregister();
|
||||
SurvivalRecipes.unregister();
|
||||
if (renderManager != null) {
|
||||
renderManager.shutdown();
|
||||
renderManager = null;
|
||||
|
||||
@@ -8,6 +8,7 @@ import eu.mhsl.minecraft.pixelpics.assets.model.ResolvedModel;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -51,7 +52,7 @@ public final class BlockModelRegistry {
|
||||
|
||||
List<Variant> variants = blockStateResolver.resolve(data);
|
||||
|
||||
List<Element> elements = new java.util.ArrayList<>();
|
||||
List<Element> elements = new ArrayList<>();
|
||||
AverageColor.Accumulator avgColor = new AverageColor.Accumulator();
|
||||
FlatModel lastFlat = null;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package eu.mhsl.minecraft.pixelpics.assets;
|
||||
import eu.mhsl.minecraft.pixelpics.assets.dto.ModelFileDto;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@@ -36,7 +37,7 @@ public final class ModelResolver {
|
||||
}
|
||||
|
||||
Map<String, String> textures = new HashMap<>();
|
||||
java.util.List<ModelFileDto.ElementDto> elements = dto.elements;
|
||||
List<ModelFileDto.ElementDto> elements = dto.elements;
|
||||
|
||||
if (dto.parent != null && depth < MAX_DEPTH && !dto.parent.startsWith("builtin/")) {
|
||||
FlatModel parent = resolve(ResourceLocation.parse(dto.parent), depth + 1);
|
||||
|
||||
@@ -15,6 +15,7 @@ import eu.mhsl.minecraft.pixelpics.render.entity.RenderedEntity;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.TextureOps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -186,7 +187,7 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
|
||||
|
||||
/** Hidden set that leaves only {@code keep} visible out of {@code all}. */
|
||||
private static Set<String> onlyPart(String keep, Set<String> all) {
|
||||
Set<String> hidden = new java.util.HashSet<>(all);
|
||||
Set<String> hidden = new HashSet<>(all);
|
||||
hidden.remove(keep);
|
||||
return hidden;
|
||||
}
|
||||
@@ -200,7 +201,7 @@ public final class BlockEntityBaker implements EntityBaker<BlockEntityState> {
|
||||
if (base == null) return List.of();
|
||||
List<Layer> layers = new ArrayList<>();
|
||||
// Structure (rim/neck/foot) comes from the combined base texture; the four sides are NOT in it.
|
||||
layers.add(new Layer(base, new java.util.HashSet<>(java.util.List.of("front", "back", "left", "right"))));
|
||||
layers.add(new Layer(base, new HashSet<>(List.of("front", "back", "left", "right"))));
|
||||
// Each side: its sherd pattern if set, else the plain brick side. The model's per-face UV maps the
|
||||
// centre of the 16x16 texture onto the face (centred, edges intact).
|
||||
for (int i = 0; i < POT_FACES.length; i++) {
|
||||
|
||||
@@ -15,7 +15,10 @@ import eu.mhsl.minecraft.pixelpics.render.entity.RenderedEntity;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.TextureOps;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Bakes an {@link EntityState} into world-space cubes using a vanilla Java {@link CemModelLoader.CemModel}
|
||||
@@ -27,9 +30,9 @@ import java.util.List;
|
||||
public final class CemBaker implements EntityBaker<EntityState> {
|
||||
|
||||
// Parts representing an alternate state (rolled-up, sleeping, …) that must not render in the idle pose.
|
||||
private static final java.util.Map<String, java.util.Set<String>> HIDDEN_PARTS = java.util.Map.of(
|
||||
"armadillo", java.util.Set.of("cube"), // the rolled-up ball
|
||||
"illager", java.util.Set.of("left_arm", "right_arm")
|
||||
private static final Map<String, Set<String>> HIDDEN_PARTS = Map.of(
|
||||
"armadillo", Set.of("cube"), // the rolled-up ball
|
||||
"illager", Set.of("left_arm", "right_arm")
|
||||
);
|
||||
|
||||
private final CemModelLoader models;
|
||||
@@ -67,7 +70,7 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
// rotations and handedness; only px->block scaling is applied.
|
||||
Affine pre = Affine.scale(sc / 16.0);
|
||||
|
||||
java.util.Set<String> hidden = new java.util.HashSet<>(HIDDEN_PARTS.getOrDefault(cem, java.util.Set.of()));
|
||||
Set<String> hidden = new HashSet<>(HIDDEN_PARTS.getOrDefault(cem, Set.of()));
|
||||
// Donkeys/llamas carry the chest boxes inside the base model; hide them unless a chest is equipped.
|
||||
if (!s.chest()) {
|
||||
if (cem.equals("donkey")) { hidden.add("left_chest"); hidden.add("right_chest"); }
|
||||
@@ -76,7 +79,7 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
// The body model is baked even when invisible — not drawn, but used as the ground-snap reference
|
||||
// so equipment stays at body height (e.g. a lone helmet sits at the head, not on the floor).
|
||||
List<CemGeometry.Baked> body = (model != null && tex != null)
|
||||
? CemGeometry.bakeModel(model, tex, pre, hidden) : java.util.List.of();
|
||||
? CemGeometry.bakeModel(model, tex, pre, hidden) : List.of();
|
||||
|
||||
List<CemGeometry.Baked> baked = new ArrayList<>();
|
||||
if (!invisible) {
|
||||
@@ -200,7 +203,7 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
int[][] saddleTex = textures.get(ResourceLocation.parse("entity/equipment/" + s.typeKey() + "_saddle/saddle")).orElse(null);
|
||||
if (saddleTex == null) return;
|
||||
// Show only the saddle-specific parts: hide every part the base body model also defines.
|
||||
java.util.Set<String> hideBase = new java.util.HashSet<>();
|
||||
Set<String> hideBase = new HashSet<>();
|
||||
for (CemModelLoader.CemPart p : base.parts()) hideBase.add(p.name());
|
||||
baked.addAll(CemGeometry.bakeModel(sm, saddleTex, pre, hideBase));
|
||||
}
|
||||
@@ -211,10 +214,10 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
// shoes=boots;
|
||||
// armor_layer_2 (texture entity/equipment/humanoid_leggings/<mat>): waist+legs=leggings.
|
||||
// Each slot may use a different material, so each is baked separately, showing only its parts.
|
||||
private static final java.util.Set<String> ARMOR1_HEAD = java.util.Set.of("head");
|
||||
private static final java.util.Set<String> ARMOR1_CHEST = java.util.Set.of("body", "left_arm", "right_arm");
|
||||
private static final java.util.Set<String> ARMOR1_FEET = java.util.Set.of("left_shoe", "right_shoe");
|
||||
private static final java.util.Set<String> ARMOR2_LEGS = java.util.Set.of("waist", "left_leg", "right_leg");
|
||||
private static final Set<String> ARMOR1_HEAD = Set.of("head");
|
||||
private static final Set<String> ARMOR1_CHEST = Set.of("body", "left_arm", "right_arm");
|
||||
private static final Set<String> ARMOR1_FEET = Set.of("left_shoe", "right_shoe");
|
||||
private static final Set<String> ARMOR2_LEGS = Set.of("waist", "left_leg", "right_leg");
|
||||
private static final int GLINT_COLOR = 0xFF8040CC; // approximated enchantment-glint purple
|
||||
|
||||
private void addArmorLayers(EntityState s, Affine pre, List<CemGeometry.Baked> baked) {
|
||||
@@ -231,14 +234,14 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
}
|
||||
|
||||
/** Bake one armor slot: its layer model with only {@code show} parts, textured for the material. */
|
||||
private void bakeArmorPiece(EntityState.EquipPiece piece, String modelName, java.util.Set<String> show,
|
||||
private void bakeArmorPiece(EntityState.EquipPiece piece, String modelName, Set<String> show,
|
||||
String layerFolder, Affine pre, List<CemGeometry.Baked> baked) {
|
||||
if (piece == null) return;
|
||||
CemModelLoader.CemModel model = models.get(modelName);
|
||||
if (model == null) return;
|
||||
int[][] tex = buildArmorTexture(piece, layerFolder);
|
||||
if (tex == null) return;
|
||||
java.util.Set<String> hidden = new java.util.HashSet<>();
|
||||
Set<String> hidden = new HashSet<>();
|
||||
for (CemModelLoader.CemPart p : model.parts()) if (!show.contains(p.name())) hidden.add(p.name());
|
||||
baked.addAll(CemGeometry.bakeModel(model, tex, pre, hidden));
|
||||
}
|
||||
@@ -299,7 +302,7 @@ public final class CemBaker implements EntityBaker<EntityState> {
|
||||
if (tex == null) return;
|
||||
int[][] out = TextureOps.deepCopy(tex);
|
||||
if (piece.glint()) applyGlint(out);
|
||||
baked.addAll(CemGeometry.bakeModel(model, out, pre, java.util.Set.of()));
|
||||
baked.addAll(CemGeometry.bakeModel(model, out, pre, Set.of()));
|
||||
}
|
||||
|
||||
private RenderedEntity fallbackBox(EntityState s, int[][] tex) {
|
||||
|
||||
@@ -16,6 +16,9 @@ import org.bukkit.block.Biome;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Traces a single ray against a {@link WorldSnapshot}, sampling block models via the
|
||||
* {@link ElementIntersector} and applying biome tint, directional face shading, transparency and
|
||||
@@ -39,7 +42,7 @@ public final class SnapshotRaytracer {
|
||||
private final double maxDistance;
|
||||
private final int reflectionDepth;
|
||||
private final int maxSteps;
|
||||
private final java.util.Map<Long, BiomeTint> tintCache = new java.util.concurrent.ConcurrentHashMap<>();
|
||||
private final Map<Long, BiomeTint> tintCache = new ConcurrentHashMap<>();
|
||||
|
||||
public SnapshotRaytracer(BlockModelRegistry registry, BiomeTintProvider tintProvider,
|
||||
SkyRenderer skyRenderer, double maxDistance, int reflectionDepth) {
|
||||
|
||||
+20
-12
@@ -15,16 +15,24 @@ import org.bukkit.block.Banner;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.block.DecoratedPot;
|
||||
import org.bukkit.block.Sign;
|
||||
import org.bukkit.block.Skull;
|
||||
import org.bukkit.block.banner.Pattern;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.Directional;
|
||||
import org.bukkit.block.data.Rotatable;
|
||||
import org.bukkit.block.data.type.Bed;
|
||||
import org.bukkit.block.data.type.Bell;
|
||||
import org.bukkit.block.data.type.Chest;
|
||||
import org.bukkit.block.sign.Side;
|
||||
import org.bukkit.block.sign.SignSide;
|
||||
import org.bukkit.profile.PlayerProfile;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -77,7 +85,7 @@ public final class BlockEntitySnapshotBuilder {
|
||||
Kind kind = mat == Material.TRAPPED_CHEST ? Kind.TRAPPED_CHEST
|
||||
: mat == Material.ENDER_CHEST ? Kind.ENDER_CHEST : Kind.CHEST;
|
||||
ChestKind ck = ChestKind.SINGLE;
|
||||
if (data instanceof org.bukkit.block.data.type.Chest cd) {
|
||||
if (data instanceof Chest cd) {
|
||||
ck = switch (cd.getType()) {
|
||||
case LEFT -> ChestKind.LEFT;
|
||||
case RIGHT -> ChestKind.RIGHT;
|
||||
@@ -88,8 +96,8 @@ public final class BlockEntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
// --- beds ---
|
||||
if (data instanceof org.bukkit.block.data.type.Bed bed) {
|
||||
BedPart part = bed.getPart() == org.bukkit.block.data.type.Bed.Part.HEAD ? BedPart.HEAD : BedPart.FOOT;
|
||||
if (data instanceof Bed bed) {
|
||||
BedPart part = bed.getPart() == Bed.Part.HEAD ? BedPart.HEAD : BedPart.FOOT;
|
||||
return base(Kind.BED, bx, by, bz, faceToYaw(bed.getFacing()))
|
||||
.bedPart(part).colorName(stripColor(n, "_BED")).build();
|
||||
}
|
||||
@@ -131,9 +139,9 @@ public final class BlockEntitySnapshotBuilder {
|
||||
kind = Kind.SIGN; yaw = rotationYaw(data);
|
||||
}
|
||||
Builder b = base(kind, bx, by, bz, yaw).wood(wood);
|
||||
if (ts instanceof org.bukkit.block.Sign sign) {
|
||||
b.frontText(signText(sign.getSide(org.bukkit.block.sign.Side.FRONT)));
|
||||
b.backText(signText(sign.getSide(org.bukkit.block.sign.Side.BACK)));
|
||||
if (ts instanceof Sign sign) {
|
||||
b.frontText(signText(sign.getSide(Side.FRONT)));
|
||||
b.backText(signText(sign.getSide(Side.BACK)));
|
||||
}
|
||||
return b.build();
|
||||
}
|
||||
@@ -165,7 +173,7 @@ public final class BlockEntitySnapshotBuilder {
|
||||
// --- bell ---
|
||||
if (mat == Material.BELL) {
|
||||
BellAttach attach = BellAttach.FLOOR;
|
||||
if (data instanceof org.bukkit.block.data.type.Bell bd) {
|
||||
if (data instanceof Bell bd) {
|
||||
attach = switch (bd.getAttachment()) {
|
||||
case FLOOR -> BellAttach.FLOOR;
|
||||
case CEILING -> BellAttach.CEILING;
|
||||
@@ -226,11 +234,11 @@ public final class BlockEntitySnapshotBuilder {
|
||||
// --- data extraction helpers ---
|
||||
|
||||
private static String stripColor(String name, String suffix) {
|
||||
return name.substring(0, name.length() - suffix.length()).toLowerCase(java.util.Locale.ROOT);
|
||||
return name.substring(0, name.length() - suffix.length()).toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
/** One sign side → {@link BlockEntityState.SignText}, or null when all four lines are blank. */
|
||||
private static BlockEntityState.SignText signText(org.bukkit.block.sign.SignSide side) {
|
||||
private static BlockEntityState.SignText signText(SignSide side) {
|
||||
String[] raw = side.getLines();
|
||||
List<String> lines = new ArrayList<>(raw.length);
|
||||
boolean any = false;
|
||||
@@ -251,7 +259,7 @@ public final class BlockEntitySnapshotBuilder {
|
||||
for (String suf : new String[]{"_WALL_HANGING_SIGN", "_HANGING_SIGN", "_WALL_SIGN", "_SIGN"}) {
|
||||
if (s.endsWith(suf)) { s = s.substring(0, s.length() - suf.length()); break; }
|
||||
}
|
||||
return s.toLowerCase(java.util.Locale.ROOT);
|
||||
return s.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static String headType(String name) {
|
||||
@@ -274,14 +282,14 @@ public final class BlockEntitySnapshotBuilder {
|
||||
for (DecoratedPot.Side side : new DecoratedPot.Side[]{
|
||||
DecoratedPot.Side.FRONT, DecoratedPot.Side.LEFT, DecoratedPot.Side.RIGHT, DecoratedPot.Side.BACK}) {
|
||||
Material m = pot.getSherd(side);
|
||||
out.add(m.name().toLowerCase(java.util.Locale.ROOT));
|
||||
out.add(m.name().toLowerCase(Locale.ROOT));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String skinUrl(Skull skull) {
|
||||
try {
|
||||
org.bukkit.profile.PlayerProfile profile = skull.getOwnerProfile();
|
||||
PlayerProfile profile = skull.getOwnerProfile();
|
||||
if (profile != null && profile.getTextures().getSkin() != null) {
|
||||
return profile.getTextures().getSkin().toString();
|
||||
}
|
||||
|
||||
+2
-1
@@ -4,6 +4,7 @@ import eu.mhsl.minecraft.pixelpics.render.entity.DecorationState;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.BlockFace;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.GlowItemFrame;
|
||||
import org.bukkit.entity.ItemFrame;
|
||||
import org.bukkit.entity.Painting;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
@@ -47,7 +48,7 @@ public final class DecorationSnapshotBuilder {
|
||||
}
|
||||
if (e instanceof ItemFrame frame) {
|
||||
BoundingBox bb = e.getBoundingBox();
|
||||
boolean glow = e instanceof org.bukkit.entity.GlowItemFrame
|
||||
boolean glow = e instanceof GlowItemFrame
|
||||
|| e.getType().getKey().getKey().equals("glow_item_frame");
|
||||
String itemId = itemId(frame.getItem());
|
||||
int rot = frame.getRotation().ordinal() * 45;
|
||||
|
||||
+100
-57
@@ -1,18 +1,61 @@
|
||||
package eu.mhsl.minecraft.pixelpics.render.snapshot;
|
||||
|
||||
import com.destroystokyo.paper.profile.ProfileProperty;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import eu.mhsl.minecraft.pixelpics.render.entity.EntityState;
|
||||
import eu.mhsl.minecraft.pixelpics.render.util.ColorUtil;
|
||||
import io.papermc.paper.datacomponent.DataComponentTypes;
|
||||
import org.bukkit.Keyed;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.AbstractHorse;
|
||||
import org.bukkit.entity.AbstractNautilus;
|
||||
import org.bukkit.entity.Ageable;
|
||||
import org.bukkit.entity.ArmorStand;
|
||||
import org.bukkit.entity.Axolotl;
|
||||
import org.bukkit.entity.Cat;
|
||||
import org.bukkit.entity.ChestedHorse;
|
||||
import org.bukkit.entity.Chicken;
|
||||
import org.bukkit.entity.Cow;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Fox;
|
||||
import org.bukkit.entity.Frog;
|
||||
import org.bukkit.entity.Horse;
|
||||
import org.bukkit.entity.LivingEntity;
|
||||
import org.bukkit.entity.Llama;
|
||||
import org.bukkit.entity.MushroomCow;
|
||||
import org.bukkit.entity.Panda;
|
||||
import org.bukkit.entity.Parrot;
|
||||
import org.bukkit.entity.Pig;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Rabbit;
|
||||
import org.bukkit.entity.Sheep;
|
||||
import org.bukkit.entity.Shulker;
|
||||
import org.bukkit.entity.Slime;
|
||||
import org.bukkit.entity.TraderLlama;
|
||||
import org.bukkit.entity.Villager;
|
||||
import org.bukkit.entity.Wolf;
|
||||
import org.bukkit.entity.Zombie;
|
||||
import org.bukkit.entity.ZombieVillager;
|
||||
import org.bukkit.inventory.EntityEquipment;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ArmorMeta;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.inventory.meta.LeatherArmorMeta;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.DoubleSupplier;
|
||||
|
||||
/**
|
||||
* Captures entities near the view frustum into immutable {@link EntityState}s. MUST run on the main
|
||||
@@ -24,7 +67,7 @@ public final class EntitySnapshotBuilder {
|
||||
|
||||
// Technical / non-mob entity types that have no meaningful geometry; rendering them would only
|
||||
// produce stray fallback boxes. Markers, displays, item frames, paintings, projectiles, drops, etc.
|
||||
private static final java.util.Set<String> NON_RENDERABLE = java.util.Set.of(
|
||||
private static final Set<String> NON_RENDERABLE = Set.of(
|
||||
"area_effect_cloud", "marker", "interaction",
|
||||
"item_frame", "glow_item_frame", "painting",
|
||||
"block_display", "item_display", "text_display",
|
||||
@@ -35,7 +78,7 @@ public final class EntitySnapshotBuilder {
|
||||
|
||||
// Entities whose vanilla renderer draws the humanoid armor layers (HumanoidArmorLayer) and held
|
||||
// items. Their CEM bodies share standard humanoid proportions, so the armor_layer_1/2 models align.
|
||||
private static final java.util.Set<String> HUMANOID_ARMOR_WEARERS = java.util.Set.of(
|
||||
private static final Set<String> HUMANOID_ARMOR_WEARERS = Set.of(
|
||||
"player", "mannequin", "armor_stand", "giant",
|
||||
"zombie", "husk", "drowned", "zombie_villager", "zombified_piglin",
|
||||
"skeleton", "stray", "wither_skeleton", "bogged",
|
||||
@@ -67,13 +110,13 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
boolean baby = (e instanceof Ageable a && !a.isAdult())
|
||||
|| (e instanceof org.bukkit.entity.Zombie z && z.isAdult());
|
||||
|| (e instanceof Zombie z && z.isAdult());
|
||||
|
||||
// Invisible entities render only their equipment (like vanilla): the generic invisible flag, an
|
||||
// invisibility potion effect, or an explicitly-hidden armor stand.
|
||||
boolean invisible = e.isInvisible()
|
||||
|| (e instanceof org.bukkit.entity.ArmorStand as && !as.isVisible())
|
||||
|| (e instanceof LivingEntity inv && inv.hasPotionEffect(org.bukkit.potion.PotionEffectType.INVISIBILITY));
|
||||
|| (e instanceof ArmorStand as && !as.isVisible())
|
||||
|| (e instanceof LivingEntity inv && inv.hasPotionEffect(PotionEffectType.INVISIBILITY));
|
||||
|
||||
double width = safeDim(e::getWidth, () -> e.getBoundingBox().getWidthX());
|
||||
double height = safeDim(e::getHeight, () -> e.getBoundingBox().getHeight());
|
||||
@@ -98,65 +141,65 @@ public final class EntitySnapshotBuilder {
|
||||
String bodyEquip = null;
|
||||
try {
|
||||
// Slime & magma cube (MagmaCube extends Slime) scale their model by size (1/2/4).
|
||||
if (e instanceof org.bukkit.entity.Slime sl) sizeScale = sl.getSize();
|
||||
if (e instanceof Slime sl) sizeScale = sl.getSize();
|
||||
// MushroomCow extends Cow, ZombieVillager does not extend Villager — order matters.
|
||||
if (e instanceof org.bukkit.entity.Sheep sh) {
|
||||
if (e instanceof Sheep sh) {
|
||||
tint = ColorUtil.dyeArgb(sh.getColor(), 0);
|
||||
} else if (e instanceof org.bukkit.entity.Cat c) {
|
||||
} else if (e instanceof Cat c) {
|
||||
variant = keyOf(c.getCatType());
|
||||
} else if (e instanceof org.bukkit.entity.Wolf w) {
|
||||
} else if (e instanceof Wolf w) {
|
||||
variant = keyOf(w.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Axolotl a) {
|
||||
} else if (e instanceof Axolotl a) {
|
||||
variant = keyOf(a.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Parrot p) {
|
||||
} else if (e instanceof Parrot p) {
|
||||
variant = keyOf(p.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Rabbit r) {
|
||||
} else if (e instanceof Rabbit r) {
|
||||
variant = keyOf(r.getRabbitType());
|
||||
} else if (e instanceof org.bukkit.entity.Horse h) {
|
||||
} else if (e instanceof Horse h) {
|
||||
variant = keyOf(h.getColor());
|
||||
markings = markingsKey(h.getStyle());
|
||||
saddle = isSaddled(h);
|
||||
bodyEquip = horseArmorKey(h);
|
||||
} else if (e instanceof org.bukkit.entity.Llama l) {
|
||||
} else if (e instanceof Llama l) {
|
||||
variant = keyOf(l.getColor());
|
||||
chest = l.isCarryingChest();
|
||||
// Trader llamas wear a fixed decor; normal llamas carry a dyed carpet in the decor slot.
|
||||
bodyEquip = (e instanceof org.bukkit.entity.TraderLlama) ? "trader_llama" : carpetKey(l);
|
||||
} else if (e instanceof org.bukkit.entity.ChestedHorse ch) {
|
||||
bodyEquip = (e instanceof TraderLlama) ? "trader_llama" : carpetKey(l);
|
||||
} else if (e instanceof ChestedHorse ch) {
|
||||
// Donkey & mule (llama already handled above).
|
||||
chest = ch.isCarryingChest();
|
||||
saddle = isSaddled(ch);
|
||||
} else if (e instanceof org.bukkit.entity.AbstractHorse ah) {
|
||||
} else if (e instanceof AbstractHorse ah) {
|
||||
// Skeleton/zombie horse: only saddle (no colour/markings/armor variants).
|
||||
saddle = isSaddled(ah);
|
||||
} else if (e instanceof org.bukkit.entity.AbstractNautilus nl) {
|
||||
} else if (e instanceof AbstractNautilus nl) {
|
||||
// Nautilus body armor + saddle are same-UV overlays (like horse armor).
|
||||
org.bukkit.inventory.EntityEquipment eq = nl.getEquipment();
|
||||
bodyEquip = equipAsset(eq.getItem(org.bukkit.inventory.EquipmentSlot.BODY));
|
||||
org.bukkit.inventory.ItemStack sd = eq.getItem(org.bukkit.inventory.EquipmentSlot.SADDLE);
|
||||
EntityEquipment eq = nl.getEquipment();
|
||||
bodyEquip = equipAsset(eq.getItem(EquipmentSlot.BODY));
|
||||
ItemStack sd = eq.getItem(EquipmentSlot.SADDLE);
|
||||
saddle = !sd.getType().isAir();
|
||||
} else if (e instanceof org.bukkit.entity.Fox f) {
|
||||
} else if (e instanceof Fox f) {
|
||||
variant = keyOf(f.getFoxType());
|
||||
} else if (e instanceof org.bukkit.entity.MushroomCow mc) {
|
||||
} else if (e instanceof MushroomCow mc) {
|
||||
variant = keyOf(mc.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Panda pa) {
|
||||
} else if (e instanceof Panda pa) {
|
||||
variant = keyOf(pa.getMainGene());
|
||||
} else if (e instanceof org.bukkit.entity.Frog fr) {
|
||||
} else if (e instanceof Frog fr) {
|
||||
variant = keyOf(fr.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Shulker s) {
|
||||
} else if (e instanceof Shulker s) {
|
||||
variant = s.getColor() == null ? null : keyOf(s.getColor());
|
||||
} else if (e instanceof org.bukkit.entity.ZombieVillager zv) {
|
||||
} else if (e instanceof ZombieVillager zv) {
|
||||
variant = keyOf(zv.getVillagerType());
|
||||
// ZombieVillager exposes no level via Bukkit -> no profession-level badge (matches vanilla).
|
||||
} else if (e instanceof org.bukkit.entity.Villager vi) {
|
||||
} else if (e instanceof Villager vi) {
|
||||
variant = keyOf(vi.getVillagerType());
|
||||
profession = keyOf(vi.getProfession());
|
||||
villagerLevel = vi.getVillagerLevel();
|
||||
} else if (e instanceof org.bukkit.entity.Cow co) {
|
||||
} else if (e instanceof Cow co) {
|
||||
variant = keyOf(co.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Pig pg) {
|
||||
} else if (e instanceof Pig pg) {
|
||||
variant = keyOf(pg.getVariant());
|
||||
} else if (e instanceof org.bukkit.entity.Chicken ch) {
|
||||
} else if (e instanceof Chicken ch) {
|
||||
variant = keyOf(ch.getVariant());
|
||||
}
|
||||
} catch (Throwable ignored) {
|
||||
@@ -177,7 +220,7 @@ public final class EntitySnapshotBuilder {
|
||||
/** Worn armor (4 slots) from a humanoid wearer; null when nothing is equipped. */
|
||||
private static EntityState.Equipment captureEquipment(LivingEntity le) {
|
||||
try {
|
||||
org.bukkit.inventory.EntityEquipment eq = le.getEquipment();
|
||||
EntityEquipment eq = le.getEquipment();
|
||||
if (eq == null) return null;
|
||||
EntityState.Equipment equip = new EntityState.Equipment(
|
||||
armorPiece(eq.getHelmet()), armorPiece(eq.getChestplate()),
|
||||
@@ -189,7 +232,7 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
/** One armor slot -> EquipPiece (asset, leather dye, trim, glint); null for empty / non-armor items. */
|
||||
private static EntityState.EquipPiece armorPiece(org.bukkit.inventory.ItemStack it) {
|
||||
private static EntityState.EquipPiece armorPiece(ItemStack it) {
|
||||
if (it == null || it.getType().isAir()) return null;
|
||||
String asset = armorAsset(it.getType());
|
||||
if (asset == null) return null; // not a humanoid-armor item (e.g. a mob head / pumpkin)
|
||||
@@ -197,10 +240,10 @@ public final class EntitySnapshotBuilder {
|
||||
String trimMat = null, trimPat = null;
|
||||
boolean glint = false;
|
||||
if (it.hasItemMeta()) {
|
||||
org.bukkit.inventory.meta.ItemMeta meta = it.getItemMeta();
|
||||
ItemMeta meta = it.getItemMeta();
|
||||
glint = meta.hasEnchants();
|
||||
if (meta instanceof org.bukkit.inventory.meta.LeatherArmorMeta lam) dye = lam.getColor().asARGB();
|
||||
if (meta instanceof org.bukkit.inventory.meta.ArmorMeta am && am.getTrim() != null) {
|
||||
if (meta instanceof LeatherArmorMeta lam) dye = lam.getColor().asARGB();
|
||||
if (meta instanceof ArmorMeta am && am.getTrim() != null) {
|
||||
trimMat = keyOf(am.getTrim().getMaterial());
|
||||
trimPat = keyOf(am.getTrim().getPattern());
|
||||
}
|
||||
@@ -209,7 +252,7 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
/** Item Material -> equipment asset id (= texture name): strips the slot suffix; null if not armor. */
|
||||
private static String armorAsset(org.bukkit.Material m) {
|
||||
private static String armorAsset(Material m) {
|
||||
String key = m.getKey().getKey();
|
||||
if (key.equals("elytra")) return "elytra";
|
||||
if (key.equals("turtle_helmet")) return "turtle_scute";
|
||||
@@ -222,32 +265,32 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
/** Horse coat markings overlay key (vanilla texture suffix); null for the plain NONE style. */
|
||||
private static String markingsKey(org.bukkit.entity.Horse.Style style) {
|
||||
if (style == null || style == org.bukkit.entity.Horse.Style.NONE) return null;
|
||||
return style.name().toLowerCase(java.util.Locale.ROOT).replace("_", ""); // WHITE_DOTS -> whitedots
|
||||
private static String markingsKey(Horse.Style style) {
|
||||
if (style == null || style == Horse.Style.NONE) return null;
|
||||
return style.name().toLowerCase(Locale.ROOT).replace("_", ""); // WHITE_DOTS -> whitedots
|
||||
}
|
||||
|
||||
/** Horse armor material -> equipment/horse_body texture key (golden uses the "gold" file); null if none. */
|
||||
private static String horseArmorKey(org.bukkit.entity.Horse h) {
|
||||
org.bukkit.inventory.ItemStack a = h.getInventory().getArmor();
|
||||
private static String horseArmorKey(Horse h) {
|
||||
ItemStack a = h.getInventory().getArmor();
|
||||
if (a == null || a.getType().isAir()) return null;
|
||||
String k = a.getType().getKey().getKey().replace("_horse_armor", "");
|
||||
return k.equals("golden") ? "gold" : k;
|
||||
}
|
||||
|
||||
/** Llama carpet decor -> equipment/llama_body colour key; null if none. */
|
||||
private static String carpetKey(org.bukkit.entity.Llama l) {
|
||||
org.bukkit.inventory.ItemStack d = l.getInventory().getDecor();
|
||||
private static String carpetKey(Llama l) {
|
||||
ItemStack d = l.getInventory().getDecor();
|
||||
if (d == null || d.getType().isAir()) return null;
|
||||
String k = d.getType().getKey().getKey();
|
||||
return k.endsWith("_carpet") ? k.substring(0, k.length() - "_carpet".length()) : null;
|
||||
}
|
||||
|
||||
/** Equipment asset id (= equipment/<asset> texture name) from an item's EQUIPPABLE component; null if none. */
|
||||
private static String equipAsset(org.bukkit.inventory.ItemStack it) {
|
||||
private static String equipAsset(ItemStack it) {
|
||||
if (it == null || it.getType().isAir()) return null;
|
||||
try {
|
||||
var comp = it.getData(io.papermc.paper.datacomponent.DataComponentTypes.EQUIPPABLE);
|
||||
var comp = it.getData(DataComponentTypes.EQUIPPABLE);
|
||||
if (comp != null && comp.assetId() != null) return comp.assetId().value();
|
||||
} catch (Throwable ignored) {
|
||||
}
|
||||
@@ -255,8 +298,8 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
/** Whether a horse-like mount carries a saddle in its dedicated saddle slot. */
|
||||
private static boolean isSaddled(org.bukkit.entity.AbstractHorse h) {
|
||||
org.bukkit.inventory.ItemStack st = h.getInventory().getSaddle();
|
||||
private static boolean isSaddled(AbstractHorse h) {
|
||||
ItemStack st = h.getInventory().getSaddle();
|
||||
return st != null && !st.getType().isAir();
|
||||
}
|
||||
|
||||
@@ -264,21 +307,21 @@ public final class EntitySnapshotBuilder {
|
||||
private static String keyOf(Object o) {
|
||||
return switch(o) {
|
||||
case null -> null;
|
||||
case org.bukkit.Keyed k -> k.getKey().getKey();
|
||||
case Enum<?> en -> en.name().toLowerCase(java.util.Locale.ROOT);
|
||||
default -> o.toString().toLowerCase(java.util.Locale.ROOT);
|
||||
case Keyed k -> k.getKey().getKey();
|
||||
case Enum<?> en -> en.name().toLowerCase(Locale.ROOT);
|
||||
default -> o.toString().toLowerCase(Locale.ROOT);
|
||||
};
|
||||
}
|
||||
|
||||
/** Returns {skinUrl, model} from the player's profile texture property, or {null, null}. */
|
||||
private static String[] resolveSkin(Player player) {
|
||||
try {
|
||||
for (com.destroystokyo.paper.profile.ProfileProperty prop : player.getPlayerProfile().getProperties()) {
|
||||
for (ProfileProperty prop : player.getPlayerProfile().getProperties()) {
|
||||
if (!prop.getName().equals("textures")) continue;
|
||||
String json = new String(java.util.Base64.getDecoder().decode(prop.getValue()),
|
||||
java.nio.charset.StandardCharsets.UTF_8);
|
||||
com.google.gson.JsonObject root = com.google.gson.JsonParser.parseString(json).getAsJsonObject();
|
||||
com.google.gson.JsonObject skin = root.getAsJsonObject("textures").getAsJsonObject("SKIN");
|
||||
String json = new String(Base64.getDecoder().decode(prop.getValue()),
|
||||
StandardCharsets.UTF_8);
|
||||
JsonObject root = JsonParser.parseString(json).getAsJsonObject();
|
||||
JsonObject skin = root.getAsJsonObject("textures").getAsJsonObject("SKIN");
|
||||
String url = skin.get("url").getAsString();
|
||||
String model = null;
|
||||
if (skin.has("metadata") && skin.getAsJsonObject("metadata").has("model")) {
|
||||
@@ -292,7 +335,7 @@ public final class EntitySnapshotBuilder {
|
||||
}
|
||||
|
||||
/** Reads a dimension via {@code primary}, falling back to {@code fallback} on any version mismatch. */
|
||||
private static double safeDim(java.util.function.DoubleSupplier primary, java.util.function.DoubleSupplier fallback) {
|
||||
private static double safeDim(DoubleSupplier primary, DoubleSupplier fallback) {
|
||||
try {
|
||||
return primary.getAsDouble();
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package eu.mhsl.minecraft.pixelpics.render.util;
|
||||
|
||||
import org.bukkit.Color;
|
||||
import org.bukkit.DyeColor;
|
||||
|
||||
/**
|
||||
* Helpers for packed ARGB integer colors.
|
||||
*/
|
||||
@@ -17,9 +20,9 @@ public final class ColorUtil {
|
||||
}
|
||||
|
||||
/** Opaque ARGB for a Bukkit dye colour, or {@code fallback} when {@code dye} is null. */
|
||||
public static int dyeArgb(org.bukkit.DyeColor dye, int fallback) {
|
||||
public static int dyeArgb(DyeColor dye, int fallback) {
|
||||
if (dye == null) return fallback;
|
||||
org.bukkit.Color c = dye.getColor();
|
||||
Color c = dye.getColor();
|
||||
return argb(0xFF, c.getRed(), c.getGreen(), c.getBlue());
|
||||
}
|
||||
|
||||
@@ -27,8 +30,8 @@ public final class ColorUtil {
|
||||
* The vanilla sign-text colour for a dye (opaque ARGB), from Mojang's {@code DyeColor.getTextColor()}
|
||||
* table (the firework/text colours, NOT the cloth colours). Null = black (the default sign ink).
|
||||
*/
|
||||
public static int signTextColor(org.bukkit.DyeColor dye) {
|
||||
int rgb = switch (dye == null ? org.bukkit.DyeColor.BLACK : dye) {
|
||||
public static int signTextColor(DyeColor dye) {
|
||||
int rgb = switch (dye == null ? DyeColor.BLACK : dye) {
|
||||
case WHITE -> 0xF9FFFE;
|
||||
case ORANGE -> 0xF9801D;
|
||||
case MAGENTA -> 0xC74EBD;
|
||||
@@ -53,7 +56,7 @@ public final class ColorUtil {
|
||||
* The fill colour for sign text: glowing text uses the full dye colour; non-glowing text is the dye
|
||||
* colour darkened to 40% (matching vanilla {@code SignRenderer}, which is why normal ink looks dim).
|
||||
*/
|
||||
public static int signFillArgb(org.bukkit.DyeColor dye, boolean glowing) {
|
||||
public static int signFillArgb(DyeColor dye, boolean glowing) {
|
||||
int base = signTextColor(dye);
|
||||
return glowing ? base : (0xFF000000 | (shade(base, 0.4) & 0xFFFFFF));
|
||||
}
|
||||
@@ -63,8 +66,8 @@ public final class ColorUtil {
|
||||
* the dye colour darkened to 40%, except glowing BLACK ink which gets a light cream outline so it
|
||||
* stays readable.
|
||||
*/
|
||||
public static int signOutlineArgb(org.bukkit.DyeColor dye) {
|
||||
if ((dye == null ? org.bukkit.DyeColor.BLACK : dye) == org.bukkit.DyeColor.BLACK) return 0xFFF0EBCC;
|
||||
public static int signOutlineArgb(DyeColor dye) {
|
||||
if ((dye == null ? DyeColor.BLACK : dye) == DyeColor.BLACK) return 0xFFF0EBCC;
|
||||
return 0xFF000000 | (shade(signTextColor(dye), 0.4) & 0xFFFFFF);
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextDecoration;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.NamespacedKey;
|
||||
import org.bukkit.inventory.EquipmentSlot;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
@@ -112,7 +113,7 @@ public final class CameraItems {
|
||||
|
||||
// --- internals ---
|
||||
|
||||
private static boolean hasMarker(ItemStack item, org.bukkit.NamespacedKey key) {
|
||||
private static boolean hasMarker(ItemStack item, NamespacedKey key) {
|
||||
if (item == null || item.getType() != Material.PLAYER_HEAD || !item.hasItemMeta()) return false;
|
||||
PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer();
|
||||
return pdc.has(key, PersistentDataType.BYTE);
|
||||
|
||||
Reference in New Issue
Block a user