9
0
mirror of https://github.com/HibiscusMC/HMCCosmetics.git synced 2025-12-19 15:09:19 +00:00

Compare commits

...

58 Commits

Author SHA1 Message Date
Logan
0e2b2edadd chore: fix not importing annotation in CosmeticBackpackType 2025-12-17 13:03:08 -06:00
Logan
7e384fcd79 Merge pull request #204 from HibiscusMC/itemmodel-placeholder
feat: add itemmodel & itemname placeholders, bump api to 1.21.4 with …
2025-12-17 11:09:09 -06:00
Logan
e88b84610e version bump (2.8.3) 2025-12-17 11:06:30 -06:00
lucian929
223ffa46eb chore: change references to glyphs 2025-12-17 11:48:44 -05:00
Logan
f408108682 chore: remove emote distance still variable in Settings 2025-12-17 10:13:26 -06:00
Logan
b9f9448367 chore: move some comments around 2025-12-16 13:08:56 -06:00
Logan
c40aca9946 feat: remove entity-cooldown-teleport-packet option as it is no longer used 2025-12-16 13:07:03 -06:00
Logan
98d2692def fix: dispatch update also updates location for viewers 2025-12-16 13:03:54 -06:00
Logan
ebbc80c465 chore: add what usage api users should use in javadocs in cosmetic deprecated methods 2025-12-16 12:54:48 -06:00
Logan
775699d9ca chore: define nullability of behavior impl 2025-12-16 12:52:41 -06:00
Logan
b0ba5fc011 chore: CosmeticUser move to non-deprecated internal methods to add cosmetics (and indicate nullability of update movement variables) 2025-12-16 12:49:44 -06:00
Logan
af01ff600b chore: move to paper advneture impl 2025-12-16 12:14:02 -06:00
Logan
2e4619da02 fix: import proper nullable in UserBackpackManager 2025-12-16 12:12:06 -06:00
Logan
b4bd08c249 chore: refactor UserSearchManager to PlayerSearchManager 2025-12-16 12:06:21 -06:00
Logan
19981b33d3 fix: better teleport listening 2025-12-16 12:00:24 -06:00
Logan
c7c4c0e583 chore: add multiverse to test server 2025-12-16 11:59:22 -06:00
Logan
935c157421 chore: remove player animator being implemented? 2025-12-14 13:07:21 -06:00
Boy0000
20a3a508c2 refactor: remove AttributeWrapper 2025-11-11 02:40:25 +01:00
Boy0000
92b75c43ab feat: add itemmodel & itemname placeholders, bump api to 1.21.4 with attribute wrapper 2025-11-11 01:39:33 +01:00
Logan
a6ad2c19aa fix: fix client crashes for wardrobe on 1.21.10 2025-11-10 16:49:41 -06:00
Logan
fe8b4c97ff Merge pull request #201 from SolaraGames/fix-api
fix api
2025-10-13 14:29:51 -05:00
Tech
8c119c78db fix api 2025-10-13 14:58:02 -04:00
Logan
ce33a72fc2 chore: update Hibiscus Commons 2025-10-13 11:25:45 -05:00
Tech
2a8f30636d nullability checks (#200) 2025-10-13 18:06:00 +02:00
Logan
4103c2c232 Merge pull request #199 from SolaraGames/fix-memory-leak
fix memory leak
2025-10-12 15:01:13 -05:00
Tech
fb5c9d205c fix memory leak 2025-10-12 16:00:08 -04:00
Logan
126d576303 Merge pull request #198 from SolaraGames/optimize-viewer
Optimize Cosmetics
2025-10-12 14:37:17 -05:00
Tech
c74b76837b fix npe 2025-10-12 01:49:17 -04:00
Tech
d2c37f8273 optimize movement cosmetics 2025-10-12 01:42:32 -04:00
Logan
dbadbda82f chore: disable passenger packet 2025-10-11 22:22:23 -05:00
Logan
d66aa3b79c chore: update luckperms on test server 2025-10-04 22:53:38 -05:00
Logan
1c5cd7b467 chore: minor refactoring of db code 2025-09-25 20:03:05 -05:00
Logan
10f8dda150 fix: dye menu through command not checking if there is a provider available 2025-09-21 15:00:03 -05:00
Logan
e76aa0783e chore: refactor passenger content code 2025-09-12 19:31:19 -05:00
Logan
0676125f07 chore: refactor backpacks files 2025-09-08 14:58:15 -05:00
Logan
7aef681149 feat: refactor UserEntity refreshViewers 2025-09-07 21:44:44 -05:00
Logan
d5ac55f266 chore: update hibiscus commons 2025-09-07 21:38:11 -05:00
Logan
c2b787c97c feat: tomfollery with backpacks, external and regular backpacks appear to work now, but first-person backpacks are still experiencing jank 2025-09-07 21:35:37 -05:00
Logan
732c3de0d8 feat: use new hibiscus commons method for pufferfish spawning 2025-09-07 21:34:38 -05:00
Logan
3843a28804 feat: reenable passenger packet listener 2025-09-07 14:51:36 -05:00
Logan
a63494ba9f feat: make prevent offhand swapping configurable and is now disabled by default, resolves #192 2025-09-07 14:05:29 -05:00
Logan
c7e6a77eb8 chore: remove ai from balloon armorstand 2025-09-07 13:46:11 -05:00
Logan
075d2b3182 fix: move mob key to be a public value and be more specific 2025-09-07 13:36:31 -05:00
Logan
515de7d2c8 feat: add event for cancelling interactions with balloon armorstand 2025-09-07 09:44:53 -05:00
Logan
429287c9f3 feat: only try running spawning invisible armorstands when there is outside viewers to send it to 2025-09-06 23:44:49 -05:00
Logan
fed13b8ee6 feat: wardrobe npc now respects scale 2025-09-06 23:25:56 -05:00
Logan
8011dda200 feat: use new Hibiscus Commons methods for packets 2025-09-06 23:19:31 -05:00
Logan
7abfb0edf1 chore: turn item processing off by default 2025-09-06 23:18:48 -05:00
Logan
1f8c772712 chore: bump to latest Hibiscus Commons 2025-09-06 23:18:24 -05:00
Logan
7d727bb68f Merge pull request #191 from NoltoxGit/remapped
Reformat config.yml
2025-08-28 16:20:56 -05:00
Logan
007f27f109 chore: nexo reload on change 2025-08-28 16:20:47 -05:00
Poy
e0e0d0c997 Add quotes to string values in config.yml
Updated YAML configuration to use quotes for string values.
2025-08-28 21:56:42 +02:00
Poy
75a1458ae1 Fix config (jd.papermc.io) 2025-08-28 21:55:12 +02:00
Logan
548b15d99d chore: bump HibiscusCommons 2025-08-27 10:56:56 -05:00
Logan
50f04373b7 chore: add gsit to the test server 2025-08-24 12:25:06 -05:00
Logan
27a6c1bd54 feat: better handling of invalid cosmetics for dye subcommand 2025-08-18 20:44:16 -05:00
Logan
bb08fb108c feat: better handling of custom dye implementations with HMCColor 2025-08-18 20:41:26 -05:00
Logan
3907bc5839 version bump (2.8.2) 2025-08-18 20:36:53 -05:00
30 changed files with 840 additions and 288 deletions

View File

@@ -8,7 +8,7 @@ plugins {
}
group = "com.hibiscusmc"
version = "2.8.1${getGitCommitHash()}"
version = "2.8.3${getGitCommitHash()}"
allprojects {
apply(plugin = "java")
@@ -75,7 +75,7 @@ allprojects {
compileOnly(fileTree("${project.rootDir}/lib") { include("*.jar") })
compileOnly("com.mojang:authlib:1.5.25")
//compileOnly("org.spigotmc:spigot-api:1.18.2-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.20.6-R0.1-SNAPSHOT")
compileOnly("io.papermc.paper:paper-api:1.21.4-R0.1-SNAPSHOT")
compileOnly("org.jetbrains:annotations:24.1.0")
compileOnly("me.clip:placeholderapi:2.11.6")
compileOnly("com.ticxo.modelengine:ModelEngine:R4.0.6")
@@ -92,12 +92,14 @@ allprojects {
compileOnly("io.github.toxicity188:BetterCommand:1.3") //BetterCommand library
//compileOnly("it.unimi.dsi:fastutil:8.5.14")
compileOnly("org.projectlombok:lombok:1.18.34")
compileOnly("me.lojosho:HibiscusCommons:0.7.1-6c4e262c")
compileOnly("me.lojosho:HibiscusCommons:0.8.1-d9fbae1d")
// Handled by Spigot Library Loader
// Handled by Spigot Library Loader ~ Deprecated as of Dec 16, 2025
/*
compileOnly("net.kyori:adventure-api:4.24.0")
compileOnly("net.kyori:adventure-text-minimessage:4.24.0")
compileOnly("net.kyori:adventure-platform-bukkit:4.4.1")
*/
annotationProcessor("org.projectlombok:lombok:1.18.36")
testCompileOnly("org.projectlombok:lombok:1.18.36")
@@ -107,7 +109,6 @@ allprojects {
exclude("net.kyori") // Already have adventure API
}
implementation("com.owen1212055:particlehelper:1.0.0-SNAPSHOT")
implementation("com.ticxo.playeranimator:PlayerAnimator:R1.2.8")
}
tasks {
@@ -144,8 +145,10 @@ tasks {
downloadPlugins {
hangar("PlaceholderAPI", "2.11.6")
url("https://download.luckperms.net/1593/bukkit/loader/LuckPerms-Bukkit-5.5.8.jar")
hangar("Multiverse-Core", "5.3.4")
url("https://download.luckperms.net/1604/bukkit/loader/LuckPerms-Bukkit-5.5.15.jar")
github("Test-Account666", "PlugManX", "2.4.1", "PlugManX-2.4.1.jar")
//github("gecolay", "GSit", "2.4.3", "GSit-2.4.3.jar")
}
}

View File

@@ -18,15 +18,14 @@ import com.hibiscusmc.hmccosmetics.hooks.misc.HookBetterHud;
import com.hibiscusmc.hmccosmetics.hooks.placeholders.HMCPlaceholderExpansion;
import com.hibiscusmc.hmccosmetics.hooks.worldguard.WGHook;
import com.hibiscusmc.hmccosmetics.hooks.worldguard.WGListener;
import com.hibiscusmc.hmccosmetics.listener.PaperPlayerGameListener;
import com.hibiscusmc.hmccosmetics.listener.PlayerConnectionListener;
import com.hibiscusmc.hmccosmetics.listener.PlayerGameListener;
import com.hibiscusmc.hmccosmetics.listener.ServerListener;
import com.hibiscusmc.hmccosmetics.listener.*;
import com.hibiscusmc.hmccosmetics.packets.CosmeticPacketInterface;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
import com.hibiscusmc.hmccosmetics.util.PlayerSearchManager;
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
import com.hibiscusmc.hmccosmetics.util.TranslationUtil;
import lombok.Getter;
import me.lojosho.hibiscuscommons.HibiscusCommonsPlugin;
import me.lojosho.hibiscuscommons.HibiscusPlugin;
import me.lojosho.hibiscuscommons.config.serializer.ItemSerializer;
@@ -51,6 +50,9 @@ public final class HMCCosmeticsPlugin extends HibiscusPlugin {
private static HMCCosmeticsPlugin instance;
private static YamlConfigurationLoader configLoader;
@Getter
private PlayerSearchManager playerSearchManager;
public HMCCosmeticsPlugin() {
super(13873, 1879);
new HookHMCCosmetics();
@@ -62,6 +64,9 @@ public final class HMCCosmeticsPlugin extends HibiscusPlugin {
// Plugin startup logic
instance = this;
// Search Service
this.playerSearchManager = new PlayerSearchManager(this);
// File setup
saveDefaultConfig();
if (!Path.of(getDataFolder().getPath(), "messages.yml").toFile().exists()) saveResource("messages.yml", false);
@@ -102,6 +107,8 @@ public final class HMCCosmeticsPlugin extends HibiscusPlugin {
getServer().getPluginManager().registerEvents(new PlayerConnectionListener(), this);
getServer().getPluginManager().registerEvents(new PlayerGameListener(), this);
getServer().getPluginManager().registerEvents(new ServerListener(), this);
getServer().getPluginManager().registerEvents(new PlayerMovementListener(), this);
getServer().getPluginManager().registerEvents(playerSearchManager, this);
if (HibiscusCommonsPlugin.isOnPaper()) {
getServer().getPluginManager().registerEvents(new PaperPlayerGameListener(), this);
@@ -116,7 +123,11 @@ public final class HMCCosmeticsPlugin extends HibiscusPlugin {
// HMCColor
if (Hooks.isActiveHook("HMCColor")) {
DyeMenuProvider.setDyeMenuProvider(new HMCColorDyeMenu());
try {
DyeMenuProvider.setDyeMenuProvider(new HMCColorDyeMenu());
} catch (IllegalStateException e) {
getLogger().warning("Unable to set HMCColor as the dye menu. There is likely another plugin registering another dye menu.");
}
}
}

View File

@@ -310,13 +310,17 @@ public class CosmeticCommand implements CommandExecutor {
return true;
}
String rawSlot = args[1];
final String rawSlot = args[1];
if (!CosmeticSlot.contains(rawSlot)) {
if (!silent) MessagesUtil.sendMessage(player, "invalid-slot");
return true;
}
CosmeticSlot slot = CosmeticSlot.valueOf(rawSlot);
Cosmetic cosmetic = user.getCosmetic(slot);
final CosmeticSlot slot = CosmeticSlot.valueOf(rawSlot); // This is checked above. While IDEs may say the slot might be null, it will not be.
final Cosmetic cosmetic = user.getCosmetic(slot);
if (cosmetic == null) {
if (!silent) MessagesUtil.sendMessage(player, "invalid-slot");
return true;
}
if (args.length >= 3) {
if (args[2].isEmpty()) {
@@ -330,7 +334,11 @@ public class CosmeticCommand implements CommandExecutor {
}
user.addCosmetic(cosmetic, color); // #FFFFFF
} else {
DyeMenuProvider.openMenu(player, user, cosmetic);
if (DyeMenuProvider.hasMenuProvider()) {
DyeMenuProvider.openMenu(player, user, cosmetic);
} else {
if (!silent) MessagesUtil.sendMessage(player, "invalid-color");
}
}
}
case ("setwardrobesetting") -> {

View File

@@ -43,10 +43,10 @@ public class Settings {
private static final String HOOK_WG_MOVE_CHECK_PATH = "player-move-check";
private static final String HOOK_WG_MOVE_CHECK_PATH_LEGACY = "player_move_check";
private static final String COSMETIC_DISABLED_WORLDS_PATH = "disabled-worlds";
private static final String COSMETIC_PACKET_ENTITY_TELEPORT_COOLDOWN_PATH = "entity-cooldown-teleport-packet";
private static final String COSMETIC_BACKPACK_FORCE_RIDING_PACKET_PATH = "backpack-force-riding-packet";
private static final String COSMETIC_DESTROY_LOOSE_COSMETIC_PATH = "destroy-loose-cosmetics";
private static final String COSMETIC_BALLOON_HEAD_FORWARD_PATH = "balloon-head-forward";
private static final String COSMETIC_OFFHAND_PREVENT_SWAPPING = "offhand-prevent-swapping";
private static final String MENU_SETTINGS_PATH = "menu-settings";
private static final String MENU_CLICK_COOLDOWN_PATH = "click-cooldown";
private static final String MENU_CLICK_COOLDOWN_TIME_PATH = "time";
@@ -101,6 +101,8 @@ public class Settings {
@Getter
private static boolean destroyLooseCosmetics;
@Getter
private static boolean preventOffhandSwapping;
@Getter
private static boolean backpackForceRidingEnabled;
@Getter
private static boolean disabledGamemodesEnabled;
@@ -117,14 +119,10 @@ public class Settings {
@Getter
private static int tickPeriod;
@Getter
private static int packetEntityTeleportCooldown;
@Getter
private static Long defaultMenuCooldown;
@Getter
private static boolean menuClickCooldown;
@Getter
private static double emoteDistance;
@Getter
private static Vector balloonOffset;
@Getter
private static String cosmeticEquipClickType;
@@ -192,6 +190,7 @@ public class Settings {
forceShowOnJoin = cosmeticSettings.node(FORCE_SHOW_COSMETICS_PATH).getBoolean(false);
destroyLooseCosmetics = cosmeticSettings.node(COSMETIC_DESTROY_LOOSE_COSMETIC_PATH).getBoolean(false);
backpackForceRidingEnabled = cosmeticSettings.node(COSMETIC_BACKPACK_FORCE_RIDING_PACKET_PATH).getBoolean(false);
preventOffhandSwapping = cosmeticSettings.node(COSMETIC_OFFHAND_PREVENT_SWAPPING).getBoolean(false);
cosmeticSettings.node(SLOT_OPTIONS_PATH).childrenMap().forEach((key, value) -> {
EquipmentSlot slot = convertConfigToEquipment(key.toString().toLowerCase());
@@ -206,7 +205,6 @@ public class Settings {
tickPeriod = cosmeticSettings.node(TICK_PERIOD_PATH).getInt(-1);
viewDistance = cosmeticSettings.node(VIEW_DISTANCE_PATH).getInt(-3);
packetEntityTeleportCooldown = cosmeticSettings.node(COSMETIC_PACKET_ENTITY_TELEPORT_COOLDOWN_PATH).getInt(-1);
balloonHeadForward = cosmeticSettings.node(COSMETIC_BALLOON_HEAD_FORWARD_PATH).getBoolean(false);
backpackPreventDarkness = cosmeticSettings.node(BACKPACK_PREVENT_DARKNESS_PATH).getBoolean(true);

View File

@@ -1,5 +1,6 @@
package com.hibiscusmc.hmccosmetics.cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticUpdateBehavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
import lombok.AccessLevel;
@@ -101,19 +102,24 @@ public abstract class Cosmetic {
}
/**
* Dispatched when an update is requested upon the cosmetic.
* Dispatched when an update is requested upon the cosmetic. Instead, you should use {@link CosmeticUser#updateCosmetic(CosmeticSlot)})}
* @param user the user to preform the update against
*/
public final void update(CosmeticUser user) {
this.doUpdate(user);
@Deprecated(since = "2.8.2")
public void update(CosmeticUser user) {
if(this instanceof CosmeticUpdateBehavior behavior) {
behavior.dispatchUpdate(user);
}
}
/**
* Action preformed on the update.
* Action preformed on the update. Instead, you should use {@link CosmeticUser#updateCosmetic(CosmeticSlot)})}
* @param user the user to preform the update against
*/
@Deprecated(since = "2.8.2")
protected void doUpdate(final CosmeticUser user) {
// NO-OP.
// #update should be the preferred way of interacting with this api now.
this.update(user);
}
@Nullable

View File

@@ -3,6 +3,7 @@ package com.hibiscusmc.hmccosmetics.cosmetic;
import com.google.common.collect.ImmutableCollection;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -54,7 +55,9 @@ public interface CosmeticHolder {
boolean canEquipCosmetic(@NotNull Cosmetic cosmetic, boolean ignoreWardrobe);
void updateCosmetic(@NotNull CosmeticSlot slot);
boolean updateCosmetic(@NotNull CosmeticSlot slot);
boolean updateMovementCosmetic(@NotNull CosmeticSlot slot, final Location from, final Location to);
/**
* Just for backwards compatibility, ensures that the given viewer and the given cosmetic holder

View File

@@ -0,0 +1,16 @@
package com.hibiscusmc.hmccosmetics.cosmetic.behavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import org.bukkit.Location;
import org.jetbrains.annotations.NotNull;
/**
* Updates cosmetics whenever a player moves.
*/
public interface CosmeticMovementBehavior {
void dispatchMove(
@NotNull final CosmeticUser user,
@NotNull final Location from,
@NotNull final Location to
);
}

View File

@@ -0,0 +1,13 @@
package com.hibiscusmc.hmccosmetics.cosmetic.behavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import org.jetbrains.annotations.NotNull;
/**
* Generic updates that happen every tick or when manually requested to be dispatched.
*/
public interface CosmeticUpdateBehavior {
void dispatchUpdate(
@NotNull final CosmeticUser user
);
}

View File

@@ -2,6 +2,7 @@ package com.hibiscusmc.hmccosmetics.cosmetic.types;
import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticUpdateBehavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.util.HMCCInventoryUtils;
import com.hibiscusmc.hmccosmetics.util.packets.HMCCPacketManager;
@@ -15,8 +16,7 @@ import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class CosmeticArmorType extends Cosmetic {
public class CosmeticArmorType extends Cosmetic implements CosmeticUpdateBehavior {
private final EquipmentSlot equipSlot;
public CosmeticArmorType(String id, ConfigurationNode config) {
@@ -31,7 +31,7 @@ public class CosmeticArmorType extends Cosmetic {
}
@Override
protected void doUpdate(@NotNull CosmeticUser user) {
public void dispatchUpdate(@NotNull CosmeticUser user) {
if (user.isInWardrobe()) return;
Entity entity = Bukkit.getEntity(user.getUniqueId());
if (entity == null) return;

View File

@@ -2,6 +2,8 @@ package com.hibiscusmc.hmccosmetics.cosmetic.types;
import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticMovementBehavior;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticUpdateBehavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.manager.UserBackpackManager;
import com.hibiscusmc.hmccosmetics.user.manager.UserEntity;
@@ -13,7 +15,6 @@ import org.bukkit.Location;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
@@ -23,9 +24,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class CosmeticBackpackType extends Cosmetic {
@Getter
@Getter
public class CosmeticBackpackType extends Cosmetic implements CosmeticUpdateBehavior, CosmeticMovementBehavior {
private int height = -1;
private ItemStack firstPersonBackpack;
@@ -39,45 +39,45 @@ public class CosmeticBackpackType extends Cosmetic {
}
@Override
protected void doUpdate(@NotNull CosmeticUser user) {
public void dispatchUpdate(@NotNull CosmeticUser user) {
Entity entity = user.getEntity();
if (entity == null) return;
if(entity == null) {
return;
}
Location entityLocation = entity.getLocation();
Location loc = entityLocation.clone().add(0, 2, 0);
if (user.isInWardrobe() || !user.isBackpackSpawned()) return;
if (user.isHidden()) {
// Sometimes the backpack is not despawned when the player is hidden (weird ass logic happening somewhere)
user.despawnBackpack();
return;
}
UserBackpackManager backpackManager = user.getUserBackpackManager();
UserEntity entityManager = backpackManager.getEntityManager();
int firstArmorStandId = backpackManager.getFirstArmorStandId();
if(backpackManager == null) return;
List<Player> outsideViewers = entityManager.refreshViewers(loc);
UserEntity entityManager = backpackManager.getEntityManager();
if(entityManager == null) return;
entityManager.teleport(loc);
entityManager.setRotation((int) loc.getYaw(), isFirstPersonCompadible());
HMCCPacketManager.sendEntitySpawnPacket(entityLocation, firstArmorStandId, EntityType.ARMOR_STAND, UUID.randomUUID(), outsideViewers);
HMCCPacketManager.sendArmorstandMetadata(firstArmorStandId, outsideViewers);
int firstArmorStandId = backpackManager.getFirstArmorStandId();
if (user.getPlayer() != null) {
AttributeInstance scaleAttribute = user.getPlayer().getAttribute(Attribute.GENERIC_SCALE);
if (scaleAttribute != null) {
HMCCPacketManager.sendEntityScalePacket(user.getUserBackpackManager().getFirstArmorStandId(), scaleAttribute.getValue(), outsideViewers);
List<Player> newViewers = entityManager.refreshViewers(loc);
if(!newViewers.isEmpty()) {
HMCCPacketManager.spawnInvisibleArmorstand(firstArmorStandId, entityLocation, UUID.randomUUID(), newViewers);
PacketManager.equipmentSlotUpdate(firstArmorStandId, EquipmentSlot.HEAD, user.getUserCosmeticItem(this, getItem()), newViewers);
if (user.getPlayer() != null) {
AttributeInstance scaleAttribute = user.getPlayer().getAttribute(Attribute.SCALE);
if (scaleAttribute != null) {
HMCCPacketManager.sendEntityScalePacket(user.getUserBackpackManager().getFirstArmorStandId(), scaleAttribute.getValue(), newViewers);
}
}
}
PacketManager.equipmentSlotUpdate(firstArmorStandId, EquipmentSlot.HEAD, user.getUserCosmeticItem(this, getItem()), outsideViewers);
// If true, it will send the riding packet to all players. If false, it will send the riding packet only to new players
if (Settings.isBackpackForceRidingEnabled()) HMCCPacketManager.sendRidingPacket(entity.getEntityId(), firstArmorStandId, entityManager.getViewers());
else HMCCPacketManager.sendRidingPacket(entity.getEntityId(), firstArmorStandId, outsideViewers);
else HMCCPacketManager.sendRidingPacket(entity.getEntityId(), firstArmorStandId, newViewers);
if (!user.isInWardrobe() && isFirstPersonCompadible() && user.getPlayer() != null) {
if (isFirstPersonCompadible() && !user.isInWardrobe() && user.getPlayer() != null) {
List<Player> owner = List.of(user.getPlayer());
ArrayList<Integer> particleCloud = backpackManager.getAreaEffectEntityId();
@@ -90,21 +90,36 @@ public class CosmeticBackpackType extends Cosmetic {
}
HMCCPacketManager.sendRidingPacket(particleCloud.get(particleCloud.size() - 1), firstArmorStandId, owner);
if (!user.isHidden()) {
//if (loc.getPitch() < -70) NMSHandlers.getHandler().equipmentSlotUpdate(user.getUserBackpackManager().getFirstArmorStandId(), EquipmentSlot.HEAD, new ItemStack(Material.AIR), owner);
//else NMSHandlers.getHandler().equipmentSlotUpdate(user.getUserBackpackManager().getFirstArmorStandId(), EquipmentSlot.HEAD, firstPersonBackpack, owner);
PacketManager.equipmentSlotUpdate(firstArmorStandId, EquipmentSlot.HEAD, user.getUserCosmeticItem(this, firstPersonBackpack), owner);
}
//MessagesUtil.sendDebugMessages("First Person Backpack Update[owner=" + user.getUniqueId() + ",player_location=" + loc + "]!", Level.INFO);
}
backpackManager.showBackpack();
}
@Override
public void dispatchMove(@NotNull CosmeticUser user, @NotNull Location from, @NotNull Location to) {
@SuppressWarnings("DuplicatedCode") // thanks.
Entity entity = user.getEntity();
if(entity == null) {
return;
}
Location entityLocation = entity.getLocation();
Location loc = entityLocation.clone().add(0, 2, 0);
UserBackpackManager backpackManager = user.getUserBackpackManager();
if(backpackManager == null) return;
UserEntity entityManager = backpackManager.getEntityManager();
if(entityManager == null) return;
entityManager.teleport(loc);
entityManager.setRotation((int) loc.getYaw(), isFirstPersonCompadible());
}
public boolean isFirstPersonCompadible() {
return firstPersonBackpack != null;
}
public ItemStack getFirstPersonBackpack() {
return firstPersonBackpack;
}
}

View File

@@ -2,6 +2,8 @@ package com.hibiscusmc.hmccosmetics.cosmetic.types;
import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticMovementBehavior;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticUpdateBehavior;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.manager.UserBalloonManager;
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
@@ -18,7 +20,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
public class CosmeticBalloonType extends Cosmetic {
public class CosmeticBalloonType extends Cosmetic implements CosmeticUpdateBehavior, CosmeticMovementBehavior {
@Getter
private final String modelName;
@@ -54,7 +56,7 @@ public class CosmeticBalloonType extends Cosmetic {
}
@Override
protected void doUpdate(@NotNull CosmeticUser user) {
public void dispatchUpdate(@NotNull CosmeticUser user) {
Entity entity = Bukkit.getEntity(user.getUniqueId());
UserBalloonManager userBalloonManager = user.getBalloonManager();
@@ -66,6 +68,29 @@ public class CosmeticBalloonType extends Cosmetic {
return;
}
Location newLocation = entity.getLocation();
newLocation = newLocation.clone().add(getBalloonOffset());
if (Settings.isBalloonHeadForward()) newLocation.setPitch(0);
if (!user.isHidden() && showLead) {
List<Player> sendTo = userBalloonManager.getPufferfish().refreshViewers(newLocation);
if (sendTo.isEmpty()) return;
user.getBalloonManager().getPufferfish().spawnPufferfish(newLocation, sendTo);
}
}
@Override
public void dispatchMove(@NotNull CosmeticUser user, @NotNull Location from, @NotNull Location to) {
Entity entity = Bukkit.getEntity(user.getUniqueId());
UserBalloonManager userBalloonManager = user.getBalloonManager();
if (entity == null || userBalloonManager == null) return;
if (user.isInWardrobe()) return;
if (!userBalloonManager.getModelEntity().isValid()) {
return;
}
Location newLocation = entity.getLocation();
Location currentLocation = user.getBalloonManager().getLocation();
newLocation = newLocation.clone().add(getBalloonOffset());
@@ -90,15 +115,6 @@ public class CosmeticBalloonType extends Cosmetic {
HMCCPacketManager.sendTeleportPacket(userBalloonManager.getPufferfishBalloonId(), newLocation, false, viewer);
HMCCPacketManager.sendLeashPacket(userBalloonManager.getPufferfishBalloonId(), entity.getEntityId(), viewer);
if (user.isHidden()) {
userBalloonManager.getPufferfish().hidePufferfish();
return;
}
if (!user.isHidden() && showLead) {
List<Player> sendTo = userBalloonManager.getPufferfish().refreshViewers(newLocation);
if (sendTo.isEmpty()) return;
user.getBalloonManager().getPufferfish().spawnPufferfish(newLocation, sendTo);
}
}
public boolean isDyeablePart(String name) {

View File

@@ -38,14 +38,14 @@ public class MySQLData extends SQLData {
HMCCosmeticsPlugin plugin = HMCCosmeticsPlugin.getInstance();
try {
openConnection();
if (connection == null) throw new NullPointerException("Connection is null");
if (connection == null) throw new IllegalStateException("Connection is null");
try (PreparedStatement preparedStatement = connection.prepareStatement("CREATE TABLE IF NOT EXISTS `COSMETICDATABASE` " +
"(UUID varchar(36) PRIMARY KEY, " +
"COSMETICS MEDIUMTEXT " +
");")) {
preparedStatement.execute();
}
} catch (SQLException | NullPointerException e) {
} catch (SQLException | IllegalStateException e) {
plugin.getLogger().severe("");
plugin.getLogger().severe("");
plugin.getLogger().severe("MySQL DATABASE CAN NOT BE REACHED.");
@@ -72,16 +72,10 @@ public class MySQLData extends SQLData {
}
private void openConnection() throws SQLException {
// Bukkit.getScheduler().runTaskAsynchronously(HMCCosmeticsPlugin.getInstance(), () -> {
// ...
// });
// connection = DriverManager.getConnection("jdbc:mysql://" + DatabaseSettings.getHost() + ":" + DatabaseSettings.getPort() + "/" + DatabaseSettings.getDatabase(), setupProperties());
// Connection isn't null AND Connection isn't closed :: return
try {
if (isConnectionOpen()) {
return;
} else if (connection != null) close(); // Close connection if still active
if (isConnectionOpen()) return;
if (connection != null) close(); // Close connection if still active
} catch (RuntimeException e) {
e.printStackTrace(); // If isConnectionOpen() throws error
}
@@ -90,17 +84,15 @@ public class MySQLData extends SQLData {
try {
Class.forName("com.mysql.jdbc.Driver");
connection = DriverManager.getConnection("jdbc:mysql://" + host + ":" + port + "/" + database, setupProperties());
} catch (SQLException e) {
} catch (SQLException | ClassNotFoundException e) {
System.out.println(e.getMessage());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
public void close() {
Bukkit.getScheduler().runTaskAsynchronously(HMCCosmeticsPlugin.getInstance(), () -> {
try {
if (connection == null) throw new NullPointerException("Connection is null");
if (connection == null) throw new IllegalStateException("Connection is null");
connection.close();
} catch (SQLException | NullPointerException e) {
System.out.println(e.getMessage());
@@ -111,13 +103,12 @@ public class MySQLData extends SQLData {
@NotNull
private Properties setupProperties() {
Properties props = new Properties();
props.put("user", user);
props.put("password", password);
props.put("autoReconnect", "true");
props.setProperty("user", user);
props.setProperty("password", password);
return props;
}
private boolean isConnectionOpen() throws RuntimeException {
private boolean isConnectionOpen() {
try {
return connection != null && !connection.isClosed();
} catch (SQLException e) {
@@ -139,12 +130,12 @@ public class MySQLData extends SQLData {
}
try {
if (connection == null) throw new NullPointerException("Connection is null");
if (connection == null) throw new IllegalStateException("Connection is null");
ps = connection.prepareStatement(query);
} catch (SQLException | NullPointerException e) {
} catch (SQLException | IllegalStateException e) {
e.printStackTrace();
}
return ps;
}
}
}

View File

@@ -60,11 +60,6 @@ public class SQLiteData extends SQLData {
}
private void openConnection() throws SQLException {
// Bukkit.getScheduler().runTaskAsynchronously(HMCCosmeticsPlugin.getInstance(), () -> {
// ...
// });
// connection = DriverManager.getConnection("jdbc:mysql://" + DatabaseSettings.getHost() + ":" + DatabaseSettings.getPort() + "/" + DatabaseSettings.getDatabase(), setupProperties());
if (connection != null && !connection.isClosed()) return;
// Close Connection if still active
@@ -84,9 +79,12 @@ public class SQLiteData extends SQLData {
@Override
public PreparedStatement preparedStatement(String query) {
PreparedStatement ps = null;
if (!isConnectionOpen()) MessagesUtil.sendDebugMessages("Connection is not open");
try {
if (!isConnectionOpen()) {
MessagesUtil.sendDebugMessages("Connection is not open");
openConnection();
}
ps = connection.prepareStatement(query);
} catch (SQLException e) {
e.printStackTrace();

View File

@@ -27,8 +27,12 @@ public class DyeMenuProvider {
* @param viewer The viewer of the menu
* @param cosmeticHolder The cosmetic holder that the player viewer wishing to modify (could be themselves or another CosmeticHolder)
* @param cosmetic The cosmetic the user wishes to dye
* @throws IllegalStateException IllegalStateException will be thrown if the dye menu instance is null (Check {@link DyeMenuProvider#hasMenuProvider()} before calling)
*/
public static void openMenu(@NotNull Player viewer, @NotNull CosmeticHolder cosmeticHolder, @NotNull Cosmetic cosmetic) {
public static void openMenu(@NotNull Player viewer, @NotNull CosmeticHolder cosmeticHolder, @NotNull Cosmetic cosmetic) throws IllegalStateException {
if (instance == null) {
throw new IllegalStateException("Unable to open a dye menu without instance of it.");
}
instance.openMenu(viewer, cosmeticHolder, cosmetic);
}

View File

@@ -73,29 +73,20 @@ public class HMCPlaceholderExpansion extends PlaceholderExpansion {
if (placeholderArgs.size() >= 2) {
CosmeticSlot slot = CosmeticSlot.valueOf(placeholderArgs.get(1).toUpperCase());
if (slot == null) return null;
if (user.getCosmetic(slot) == null) return TranslationUtil.getTranslation("current-cosmetic", "no-cosmetic");
Cosmetic cosmetic = user.getCosmetic(slot);
if (cosmetic == null) return TranslationUtil.getTranslation("current-cosmetic", "no-cosmetic");
if (placeholderArgs.size() == 2) return user.getCosmetic(slot).getId();
String output;
switch (placeholderArgs.get(2).toLowerCase()) {
case "material" -> {
output = getMaterialName(user.getCosmetic(slot));
}
case "custommodeldata" -> {
output = getModelData(user.getCosmetic(slot));
}
case "name" -> {
output = getItemName(user.getCosmetic(slot));
}
case "lore" -> {
output = getItemLore(user.getCosmetic(slot));
}
case "permission" -> {
output = user.getCosmetic(slot).getPermission();
}
default -> {
output = user.getCosmetic(slot).getId();
}
case "material" -> output = getMaterialName(cosmetic);
case "custommodeldata" -> output = getModelData(cosmetic);
case "itemmodel" -> output = getItemModel(cosmetic);
case "itemname" -> output = getItemName(cosmetic);
case "name" -> output = getDisplayName(cosmetic);
case "lore" -> output = getItemLore(cosmetic);
case "permission" -> output = user.getCosmetic(slot).getPermission();
default -> output = user.getCosmetic(slot).getId();
}
if (output == null) output = "none";
return TranslationUtil.getTranslation("current-cosmetic", output);
@@ -217,13 +208,55 @@ public class HMCPlaceholderExpansion extends PlaceholderExpansion {
return String.valueOf(itemMeta.getCustomModelData());
}
/**
* Gets the cosmetic items item model
* @param cosmetic The cosmetic to get its item model
* @return The cosmetic items item model
*/
@Nullable
public String getItemModel(@NotNull Cosmetic cosmetic) {
try {
ItemStack item = cosmetic.getItem();
if (item == null) return null;
if (!item.hasItemMeta()) return null;
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta == null && itemMeta.hasItemModel() ) return null;
return itemMeta.getItemModel().asString();
} catch (Exception e) {
return null;
}
}
/**
* Gets the cosmetic items item name
* @param cosmetic The cosmetic to get its items item name
* @return The cosmetic items item name
*/
@Nullable
public String getItemName(@NotNull Cosmetic cosmetic) {
try {
ItemStack item = cosmetic.getItem();
if (item == null) return null;
if (!item.hasItemMeta()) return null;
ItemMeta itemMeta = item.getItemMeta();
if (itemMeta == null || !itemMeta.hasItemName()) return null;
return itemMeta.getDisplayName();
} catch (Exception e) {
return null;
}
}
/**
* Gets the cosmetic items display name
* @param cosmetic The cosmetic to get its items display name
* @return The cosmetic items display name
*/
@Nullable
public String getItemName(@NotNull Cosmetic cosmetic) {
public String getDisplayName(@NotNull Cosmetic cosmetic) {
ItemStack item = cosmetic.getItem();
if (item == null) return null;
if (!item.hasItemMeta()) return null;

View File

@@ -4,22 +4,16 @@ import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin;
import com.hibiscusmc.hmccosmetics.api.events.PlayerCosmeticPostEquipEvent;
import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.config.WardrobeSettings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticArmorType;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBackpackType;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBalloonType;
import com.hibiscusmc.hmccosmetics.gui.Menu;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
import com.hibiscusmc.hmccosmetics.user.manager.UserWardrobeManager;
import com.hibiscusmc.hmccosmetics.util.HMCCInventoryUtils;
import com.hibiscusmc.hmccosmetics.util.HMCCServerUtils;
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
import com.hibiscusmc.hmccosmetics.util.packets.HMCCPacketManager;
import me.lojosho.hibiscuscommons.api.events.*;
import me.lojosho.hibiscuscommons.nms.MinecraftVersion;
import me.lojosho.hibiscuscommons.nms.NMSHandlers;
import me.lojosho.hibiscuscommons.util.packets.PacketManager;
import org.bukkit.Bukkit;
import org.bukkit.Location;
@@ -46,7 +40,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
public class PlayerGameListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerClick(@NotNull InventoryClickEvent event) {
// || !event.getClickedInventory().getType().equals(InventoryType.PLAYER)
@@ -101,19 +94,17 @@ public class PlayerGameListener implements Listener {
Bukkit.getScheduler().runTaskLater(HMCCosmeticsPlugin.getInstance(), () -> {
if (user.getEntity() == null || user.isInWardrobe()) return; // fixes disconnecting when in wardrobe (the entity stuff)
if (Settings.getDisabledWorlds().contains(user.getEntity().getLocation().getWorld().getName())) {
user.hideCosmetics(CosmeticUser.HiddenReason.WORLD);
} else {
user.showCosmetics(CosmeticUser.HiddenReason.WORLD);
}
if (user.hasCosmeticInSlot(CosmeticSlot.BACKPACK) && user.getUserBackpackManager() != null) {
user.respawnBackpack();
}
if (user.hasCosmeticInSlot(CosmeticSlot.BALLOON)) {
user.respawnBalloon();
}
user.respawnBackpack();
user.respawnBalloon();
user.updateCosmetic();
}, 2);
}, 4);
if (event.getCause().equals(PlayerTeleportEvent.TeleportCause.NETHER_PORTAL) || event.getCause().equals(PlayerTeleportEvent.TeleportCause.END_PORTAL)) return;
}
@@ -147,8 +138,7 @@ public class PlayerGameListener implements Listener {
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerHit(EntityDamageByEntityEvent event) {
Entity entity = event.getEntity();
if (!entity.getPersistentDataContainer().has(new NamespacedKey(HMCCosmeticsPlugin.getInstance(), "cosmeticMob"), PersistentDataType.SHORT))
return;
if (!entity.getPersistentDataContainer().has(HMCCServerUtils.getCosmemeticMobKey(), PersistentDataType.BOOLEAN)) return;
event.setCancelled(true);
}
@@ -166,15 +156,6 @@ public class PlayerGameListener implements Listener {
}
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
Player player = event.getPlayer();
CosmeticUser user = CosmeticUsers.getUser(player);
if (user == null) return;
user.updateCosmetic(CosmeticSlot.BACKPACK);
user.updateCosmetic(CosmeticSlot.BALLOON);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlayerPoseChange(EntityPoseChangeEvent event) {
if (!(event.getEntity() instanceof Player player)) return;
@@ -364,6 +345,15 @@ public class PlayerGameListener implements Listener {
}
}
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
public void onPlayerInteract(PlayerInteractEntityEvent event) {
final Player player = event.getPlayer();
final Entity entity = event.getRightClicked();
// Balloons are technically actual entities, so we need to cancel any interactions with them
if (!entity.getPersistentDataContainer().has(HMCCServerUtils.getCosmemeticMobKey(), PersistentDataType.BOOLEAN)) return;
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerVanish(HibiscusPlayerVanishEvent event) {
CosmeticUser user = CosmeticUsers.getUser(event.getPlayer());

View File

@@ -0,0 +1,101 @@
package com.hibiscusmc.hmccosmetics.listener;
import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
import lombok.extern.slf4j.Slf4j;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerChangedWorldEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Slf4j
public class PlayerMovementListener implements Listener {
private static final List<CosmeticSlot> MOVEMENT_COSMETICS = List.of(
CosmeticSlot.BACKPACK,
CosmeticSlot.BALLOON
);
// Player Id -> Small Location
private final Map<UUID, SmallLocation> locations = new HashMap<>();
@EventHandler(priority = EventPriority.LOW, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent ev) {
final Player player = ev.getPlayer();
final CosmeticUser user = CosmeticUsers.getUser(player);
if(user == null) {
return;
}
if(!updateDirtyLocation(ev.getPlayer(), ev.getTo())) {
return;
}
for(final CosmeticSlot slot : MOVEMENT_COSMETICS) {
user.updateMovementCosmetic(slot, ev.getFrom(), ev.getTo());
}
}
private boolean updateDirtyLocation(final Player player, final Location nextLoc) {
final SmallLocation previous = locations.computeIfAbsent(
player.getUniqueId(),
$ -> SmallLocation.fromLocation(nextLoc)
);
final SmallLocation next = SmallLocation.fromLocation(nextLoc);
if(next.distanceTo(previous) > 0.25) {
this.locations.put(player.getUniqueId(), next);
return true;
}
if(next.yawDistanceTo(previous) > 5) {
this.locations.put(player.getUniqueId(), next);
return true;
}
return false;
}
@EventHandler(priority = EventPriority.NORMAL)
public void onWorldChange(final PlayerChangedWorldEvent ev) {
this.locations.remove(ev.getPlayer().getUniqueId());
}
@EventHandler
public void onPlayerQuit(final PlayerQuitEvent ev) {
locations.remove(ev.getPlayer().getUniqueId());
}
record SmallLocation(
double x,
double y,
double z,
float yaw
) {
public double distanceTo(SmallLocation other) {
double dx = this.x - other.x;
double dy = this.y - other.y;
double dz = this.z - other.z;
return Math.sqrt(dx * dx + dy * dy + dz * dz);
}
public float yawDistanceTo(SmallLocation other) {
float diff = Math.abs(this.yaw - other.yaw) % 360;
return diff > 180 ? 360 - diff : diff;
}
public static SmallLocation fromLocation(final Location location) {
return new SmallLocation(location.getX(), location.getY(), location.getZ(), location.getYaw());
}
}
}

View File

@@ -5,6 +5,7 @@ import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticArmorType;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBackpackType;
import com.hibiscusmc.hmccosmetics.gui.Menu;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
@@ -124,26 +125,28 @@ public class CosmeticPacketInterface implements PacketInterface {
@Override
public @NotNull PacketAction writePassengerContent(@NotNull Player player, @NotNull PassengerWrapper wrapper) {
return PacketAction.NOTHING;
// TODO: Figure out what to do with this, because with it in, it ruins backpacks (they keep getting thrown to random locations).
// When you have this all disabled, it works better
return PacketAction.NOTHING;
/*
CosmeticUser viewerUser = CosmeticUsers.getUser(player);
if (viewerUser == null || viewerUser.isInWardrobe()) return PacketAction.NOTHING;
int ownerId = wrapper.getOwner();
List<Integer> originalPassengers = wrapper.getPassengers();
MessagesUtil.sendDebugMessages("Mount Packet Read - EntityID: " + ownerId);
Optional<CosmeticUser> optionalCosmeticUser = CosmeticUsers.values().stream().filter(user -> user.getPlayer() != null).filter(user -> ownerId == user.getPlayer().getEntityId()).findFirst();
if (optionalCosmeticUser.isEmpty()) return PacketAction.NOTHING;
CosmeticUser user = optionalCosmeticUser.get();
MessagesUtil.sendDebugMessages("Mount Packet Sent - " + user.getUniqueId());
if (!user.hasCosmeticInSlot(CosmeticSlot.BACKPACK)) return PacketAction.NOTHING;
Cosmetic backpackCosmetic = user.getCosmetic(CosmeticSlot.BACKPACK);
if (backpackCosmetic == null) return PacketAction.NOTHING;
if (!(backpackCosmetic instanceof CosmeticBackpackType cosmeticBackpackType)) return PacketAction.NOTHING;
if (user.getUniqueId().equals(viewerUser.getUniqueId())) {
if (cosmeticBackpackType.isFirstPersonCompadible()) return PacketAction.NOTHING;
}
if (user.getUserBackpackManager() == null) return PacketAction.NOTHING;
List<Integer> originalPassengers = wrapper.getPassengers();
List<Integer> passengers = new ArrayList<>(user.getUserBackpackManager().getEntityManager().getIds());
passengers.addAll(originalPassengers);
wrapper.setPassengers(passengers);
@@ -191,6 +194,7 @@ public class CosmeticPacketInterface implements PacketInterface {
@Override
public @NotNull PacketAction readPlayerAction(@NotNull Player player, @NotNull PlayerActionWrapper wrapper) {
if (!Settings.isPreventOffhandSwapping()) return PacketAction.NOTHING;
String actionType = wrapper.getActionType();
MessagesUtil.sendDebugMessages("EntityStatus Initial " + player.getEntityId() + " - " + actionType);
// If it's not SWAP_ITEM_WITH_OFFHAND, ignore

View File

@@ -10,6 +10,8 @@ import com.hibiscusmc.hmccosmetics.config.WardrobeSettings;
import com.hibiscusmc.hmccosmetics.cosmetic.Cosmetic;
import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticHolder;
import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticMovementBehavior;
import com.hibiscusmc.hmccosmetics.cosmetic.behavior.CosmeticUpdateBehavior;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticArmorType;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBackpackType;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBalloonType;
@@ -28,6 +30,7 @@ import me.lojosho.hibiscuscommons.util.InventoryUtils;
import me.lojosho.hibiscuscommons.util.packets.PacketManager;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.HumanEntity;
@@ -51,7 +54,7 @@ public class CosmeticUser implements CosmeticHolder {
private final HashMap<CosmeticSlot, Cosmetic> playerCosmetics = new HashMap<>();
private UserWardrobeManager userWardrobeManager;
private UserBalloonManager userBalloonManager;
@Getter
@Getter @Nullable
private UserBackpackManager userBackpackManager;
// Cosmetic Settings/Toggles
@@ -96,7 +99,7 @@ public class CosmeticUser implements CosmeticHolder {
Color color = null;
if (colorRGBInt != -1) color = Color.fromRGB(colorRGBInt); // -1 is defined as no color; anything else is a color
this.addPlayerCosmetic(cosmetic, color);
this.addCosmetic(cosmetic, color);
}
this.applyHiddenState(userData.getHiddenReasons());
}
@@ -109,7 +112,7 @@ public class CosmeticUser implements CosmeticHolder {
* This is used to help hooking plugins apply custom logic to the user.
*/
protected boolean applyCosmetic(@NotNull Cosmetic cosmetic, @Nullable Color color) {
this.addPlayerCosmetic(cosmetic, color);
this.addCosmetic(cosmetic, color);
return true;
}
@@ -298,23 +301,52 @@ public class CosmeticUser implements CosmeticHolder {
}
@Override
public void updateCosmetic(@NotNull CosmeticSlot slot) {
Cosmetic cosmetic = playerCosmetics.get(slot);
if (cosmetic != null) {
cosmetic.update(this);
public boolean updateCosmetic(@NotNull CosmeticSlot slot) {
final Cosmetic cosmetic = playerCosmetics.get(slot);
if(cosmetic == null) {
return false;
}
if(!(cosmetic instanceof CosmeticUpdateBehavior behavior)) {
MessagesUtil.sendDebugMessages("Attempted to update cosmetic that does not implement CosmeticUpdateBehavior");
return false;
}
behavior.dispatchUpdate(this);
return true;
}
public void updateCosmetic(Cosmetic cosmetic) {
updateCosmetic(cosmetic.getSlot());
@Override
public boolean updateMovementCosmetic(@NotNull CosmeticSlot slot, @NotNull final Location from, @NotNull final Location to) {
final Cosmetic cosmetic = playerCosmetics.get(slot);
if(cosmetic == null) {
return false;
}
if(!(cosmetic instanceof CosmeticMovementBehavior behavior)) {
MessagesUtil.sendDebugMessages("Attempted to update cosmetic that does not implement CosmeticMovementBehavior");
return false;
}
behavior.dispatchMove(this, from, to);
return true;
}
public boolean updateCosmetic(@NotNull final Cosmetic cosmetic) {
return updateCosmetic(cosmetic.getSlot());
}
public void updateCosmetic() {
MessagesUtil.sendDebugMessages("updateCosmetic (All) - start");
HashMap<EquipmentSlot, ItemStack> items = new HashMap<>();
final HashMap<EquipmentSlot, ItemStack> items = new HashMap<>();
for (Cosmetic cosmetic : playerCosmetics.values()) {
if (cosmetic instanceof CosmeticArmorType armorType) {
for(final Cosmetic cosmetic : playerCosmetics.values()) {
if(!(cosmetic instanceof CosmeticUpdateBehavior behavior)) {
continue;
}
// defers item updates to end of operation
if(cosmetic instanceof CosmeticArmorType armorType) {
if (isInWardrobe()) return;
if (!(getEntity() instanceof HumanEntity humanEntity)) return;
@@ -325,15 +357,22 @@ public class CosmeticUser implements CosmeticHolder {
items.put(HMCCInventoryUtils.getEquipmentSlot(armorType.getSlot()), armorType.getItem(this));
} else {
cosmetic.update(this);
behavior.dispatchUpdate(this);
}
}
if (items.isEmpty() || getEntity() == null) return;
PacketManager.equipmentSlotUpdate(getEntity().getEntityId(), items, HMCCPacketManager.getViewers(getEntity().getLocation()));
MessagesUtil.sendDebugMessages("updateCosmetic (All) - end - " + items.size());
final Entity entity = this.getEntity();
if(!items.isEmpty() && entity != null) {
PacketManager.equipmentSlotUpdate(
entity.getEntityId(),
items,
HMCCPacketManager.getViewers(entity.getLocation())
);
MessagesUtil.sendDebugMessages("updateCosmetic (All) - end - " + items.size());
}
}
public ItemStack getUserCosmeticItem(CosmeticSlot slot) {
public ItemStack getUserCosmeticItem(@NotNull CosmeticSlot slot) {
Cosmetic cosmetic = getCosmetic(slot);
if (cosmetic == null) return new ItemStack(Material.AIR);
return getUserCosmeticItem(cosmetic);

View File

@@ -12,10 +12,10 @@ import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
@@ -30,7 +30,7 @@ public class UserBackpackManager {
private ArrayList<Integer> particleCloud = new ArrayList<>();
@Getter
private final CosmeticUser user;
@Getter
@Getter @Nullable
private UserEntity entityManager;
public UserBackpackManager(CosmeticUser user) {
@@ -55,11 +55,11 @@ public class UserBackpackManager {
getEntityManager().setIds(List.of(invisibleArmorStand));
getEntityManager().teleport(user.getEntity().getLocation());
List<Player> outsideViewers = getEntityManager().getViewers();
HMCCPacketManager.sendEntitySpawnPacket(user.getEntity().getLocation(), getFirstArmorStandId(), EntityType.ARMOR_STAND, UUID.randomUUID(), getEntityManager().getViewers());
HMCCPacketManager.sendArmorstandMetadata(getFirstArmorStandId(), outsideViewers);
HMCCPacketManager.spawnInvisibleArmorstand(getFirstArmorStandId(), user.getEntity().getLocation(), UUID.randomUUID(), outsideViewers);
if (user.getPlayer() != null) {
AttributeInstance scaleAttribute = user.getPlayer().getAttribute(Attribute.GENERIC_SCALE);
AttributeInstance scaleAttribute = user.getPlayer().getAttribute(Attribute.SCALE);
if (scaleAttribute != null) {
HMCCPacketManager.sendEntityScalePacket(getFirstArmorStandId(), scaleAttribute.getValue(), outsideViewers);
}
@@ -81,8 +81,7 @@ public class UserBackpackManager {
if (cosmeticBackpackType.isFirstPersonCompadible()) {
for (int i = particleCloud.size(); i < cosmeticBackpackType.getHeight(); i++) {
int entityId = ServerUtils.getNextEntityId();
HMCCPacketManager.sendEntitySpawnPacket(user.getEntity().getLocation(), entityId, EntityType.AREA_EFFECT_CLOUD, UUID.randomUUID());
HMCCPacketManager.sendCloudEffect(entityId, HMCCPacketManager.getViewers(user.getEntity().getLocation()));
HMCCPacketManager.spawnCloudAndHandleEffect(entityId, user.getEntity().getLocation(), UUID.randomUUID(), HMCCPacketManager.getViewers(user.getEntity().getLocation()));
this.particleCloud.add(entityId);
}
// Copied code from updating the backpack

View File

@@ -5,6 +5,7 @@ import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.cosmetic.types.CosmeticBalloonType;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
import com.hibiscusmc.hmccosmetics.util.HMCCServerUtils;
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
import com.hibiscusmc.hmccosmetics.util.packets.HMCCPacketManager;
import com.ticxo.modelengine.api.ModelEngineAPI;
@@ -49,7 +50,8 @@ public class UserBalloonManager {
e.setSmall(true);
e.setMarker(true);
e.setPersistent(false);
e.getPersistentDataContainer().set(new NamespacedKey(HMCCosmeticsPlugin.getInstance(), "cosmeticMob"), PersistentDataType.SHORT, Short.valueOf("1"));
e.setAI(false);
e.getPersistentDataContainer().set(HMCCServerUtils.getCosmemeticMobKey(), PersistentDataType.BOOLEAN, true);
});
}

View File

@@ -1,8 +1,8 @@
package com.hibiscusmc.hmccosmetics.user.manager;
import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin;
import com.hibiscusmc.hmccosmetics.util.packets.HMCCPacketManager;
import lombok.Getter;
import me.lojosho.hibiscuscommons.nms.NMSHandlers;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
@@ -21,6 +21,7 @@ public class UserBalloonPufferfish extends UserEntity {
public UserBalloonPufferfish(UUID owner, int pufferFishEntityId, UUID uuid) {
super(owner);
this.pufferFishEntityId = pufferFishEntityId;
setIds(List.of(pufferFishEntityId));
this.uuid = uuid;
}
@@ -30,8 +31,11 @@ public class UserBalloonPufferfish extends UserEntity {
}
public void spawnPufferfish(Location location, List<Player> sendTo) {
NMSHandlers.getHandler().getPacketHandler().sendInvisibleEntity(pufferFishEntityId, EntityType.PUFFERFISH, location, uuid, sendTo);
/*
HMCCPacketManager.sendEntitySpawnPacket(location, pufferFishEntityId, EntityType.PUFFERFISH, uuid, sendTo);
HMCCPacketManager.sendInvisibilityPacket(pufferFishEntityId, sendTo);
*/
}
public void destroyPufferfish() {

View File

@@ -1,5 +1,6 @@
package com.hibiscusmc.hmccosmetics.user.manager;
import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin;
import com.hibiscusmc.hmccosmetics.config.Settings;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
@@ -12,9 +13,7 @@ import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;
public class UserEntity {
@@ -40,39 +39,66 @@ public class UserEntity {
}
public List<Player> refreshViewers(Location location) {
if (System.currentTimeMillis() - viewerLastUpdate <= 1000) return List.of(); //Prevents mass refreshes
ArrayList<Player> newPlayers = new ArrayList<>();
ArrayList<Player> removePlayers = new ArrayList<>();
List<Player> players = HMCCPacketManager.getViewers(location);
//Prevents mass refreshes
//if(System.currentTimeMillis() - viewerLastUpdate <= 3000) {
// return List.of();
//}
Entity ownerPlayer = Bukkit.getEntity(owner);
if (ownerPlayer == null) {
MessagesUtil.sendDebugMessages("Owner is null (refreshViewers), returning empty list");
return List.of();
}
final List<Player> players = HMCCosmeticsPlugin.getInstance()
.getPlayerSearchManager()
.getPlayersInRange(location, Settings.getViewDistance());
final ArrayList<UUID> newPlayerIds = new ArrayList<>();
final ArrayList<UUID> removePlayerIds = new ArrayList<>();
// Go through all nearby players, check if they are new to the viewers list.
for (Player player : players) {
CosmeticUser user = CosmeticUsers.getUser(player);
if (user != null && owner != user.getUniqueId() && user.isInWardrobe() && !player.canSee(ownerPlayer)) { // Fixes issue where players in wardrobe would see other players cosmetics if they were not in wardrobe
removePlayers.add(player);
HMCCPacketManager.sendEntityDestroyPacket(ids, List.of(player));
if(
user != null
&& owner != user.getUniqueId()
&& user.isInWardrobe()
// Fixes issue where players in wardrobe would see other players cosmetics if they were not in wardrobe
&& !player.canSee(ownerPlayer)
) {
removePlayerIds.add(player.getUniqueId());
continue;
}
if (!viewers.contains(player)) {
viewers.add(player);
newPlayers.add(player);
continue;
}
// bad loopdy loops
for (Player viewerPlayer : viewers) {
if (!players.contains(viewerPlayer)) {
removePlayers.add(viewerPlayer);
HMCCPacketManager.sendEntityDestroyPacket(ids, List.of(viewerPlayer));
}
newPlayerIds.add(player.getUniqueId());
}
}
viewers.removeAll(removePlayers);
setViewerLastUpdate(System.currentTimeMillis());
return newPlayers;
// Basically, if they are not nearby, they are still in the viewers and we need to kick em to the curb
for (Player viewerPlayer : viewers) {
if (!players.contains(viewerPlayer)) {
removePlayerIds.add(viewerPlayer.getUniqueId());
}
}
// If there are players for removal, send the packets to them
if (!removePlayerIds.isEmpty()) {
final List<Player> removePlayers = removePlayerIds.stream()
.map(Bukkit::getPlayer)
.filter(Objects::nonNull)
.toList();
HMCCPacketManager.sendEntityDestroyPacket(ids, removePlayers);
viewers.removeAll(removePlayers);
}
this.setViewerLastUpdate(System.currentTimeMillis());
return newPlayerIds.stream()
.map(Bukkit::getPlayer)
.filter(Objects::nonNull)
.toList();
}
public void teleport(Location location) {

View File

@@ -20,12 +20,13 @@ import me.lojosho.hibiscuscommons.nms.NMSHandlers;
import me.lojosho.hibiscuscommons.util.packets.PacketManager;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.bossbar.BossBar;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerTeleportEvent;
@@ -141,6 +142,10 @@ public class UserWardrobeManager {
HMCCPacketManager.sendPlayerOverlayPacket(NPC_ID, viewer);
MessagesUtil.sendDebugMessages("Spawned Fake Player on " + npcLocation);
NMSHandlers.getHandler().getPacketHandler().sendScoreboardHideNamePacket(player, npcName);
AttributeInstance scaleAttribute = user.getPlayer().getAttribute(Attribute.SCALE);
if (scaleAttribute != null) {
HMCCPacketManager.sendEntityScalePacket(NPC_ID, scaleAttribute.getValue(), viewer);
}
}, 4);
// Location
@@ -178,9 +183,9 @@ public class UserWardrobeManager {
Component message = MessagesUtil.processStringNoKey(player, WardrobeSettings.getBossbarMessage());
bossBar = BossBar.bossBar(message, progress, WardrobeSettings.getBossbarColor(), WardrobeSettings.getBossbarOverlay());
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
//Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.showBossBar(bossBar);
player.showBossBar(bossBar);
}
if (WardrobeSettings.isEnterOpenMenu()) {
@@ -282,9 +287,8 @@ public class UserWardrobeManager {
HMCCPacketManager.equipmentSlotUpdate(player.getEntityId(), items, viewer);
if (WardrobeSettings.isEnabledBossbar()) {
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.hideBossBar(bossBar);
//Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
player.hideBossBar(bossBar);
}
user.updateCosmetic();

View File

@@ -1,5 +1,6 @@
package com.hibiscusmc.hmccosmetics.util;
import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin;
import com.owen1212055.particlehelper.api.particle.MultiParticle;
import com.owen1212055.particlehelper.api.particle.Particle;
import com.owen1212055.particlehelper.api.particle.types.*;
@@ -9,6 +10,8 @@ import me.lojosho.hibiscuscommons.nms.NMSHandlers;
import org.bukkit.Color;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.Nullable;
import java.math.BigInteger;
@@ -157,4 +160,8 @@ public class HMCCServerUtils {
}
return nextYaw;
}
public static NamespacedKey getCosmemeticMobKey() {
return new NamespacedKey(HMCCosmeticsPlugin.getInstance(), "cosmeticmob");
}
}

View File

@@ -7,7 +7,6 @@ import me.lojosho.hibiscuscommons.hooks.Hooks;
import me.lojosho.hibiscuscommons.util.AdventureUtils;
import me.lojosho.shaded.configurate.ConfigurationNode;
import net.kyori.adventure.audience.Audience;
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.kyori.adventure.title.Title;
@@ -43,41 +42,35 @@ public class MessagesUtil {
public static void sendMessage(Player player, String key) {
Component finalMessage = processString(player, key);
if (finalMessage == null) return;
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.sendMessage(finalMessage);
player.sendMessage(finalMessage);
}
public static void sendMessage(CommandSender sender, String key) {
Component finalMessage = processString(null, key);
if (finalMessage == null) return;
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).sender(sender);
target.sendMessage(finalMessage);
sender.sendMessage(finalMessage);
}
public static void sendMessage(Player player, String key, TagResolver placeholder) {
Component finalMessage = processString(player, key, placeholder);
if (finalMessage == null) return;
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.sendMessage(finalMessage);
player.sendMessage(finalMessage);
}
public static void sendMessageNoKey(Player player, String message) {
Component finalMessage = processStringNoKey(player, message);
if (finalMessage == null) return;
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.sendMessage(finalMessage);
player.sendMessage(finalMessage);
}
public static void sendActionBar(Player player, String key) {
Component finalMessage = processString(player, key);
if (finalMessage == null) return;
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
target.sendActionBar(finalMessage);
player.sendActionBar(finalMessage);
}
public static void sendTitle(Player player, String message) {
@@ -85,12 +78,10 @@ public class MessagesUtil {
}
public static void sendTitle(Player player, String message, int fadein, int stay, int fadeout) {
Audience target = BukkitAudiences.create(HMCCosmeticsPlugin.getInstance()).player(player);
Title.Times times = Title.Times.times(Duration.ofMillis(fadein), Duration.ofMillis(stay), Duration.ofMillis(fadeout));
Title title = Title.title(processStringNoKey(player, message), Component.empty(), times);
target.showTitle(title);
player.showTitle(title);
}
public static Component processString(Player player, String key) {

View File

@@ -0,0 +1,148 @@
package com.hibiscusmc.hmccosmetics.util;
import java.util.ArrayList;
import java.util.List;
public class Octree<T> {
private static final int MAX_CAPACITY = 8;
private BoundingBox boundary;
private List<Entry> points;
private Octree<T>[] children;
private boolean isDivided;
public Octree(BoundingBox boundary) {
this.boundary = boundary;
this.points = new ArrayList<>();
this.isDivided = false;
}
public boolean insert(Point3D position, T data) {
if (!boundary.contains(position)) {
return false;
}
if (points.size() < MAX_CAPACITY && !isDivided) {
points.add(new Entry(position, data));
return true;
}
if (!isDivided) {
subdivide();
}
for (Octree<T> child : children) {
if (child.insert(position, data)) {
return true;
}
}
return false;
}
public boolean remove(Point3D point, T player) {
if (!boundary.contains(point)) {
return false;
}
if (points.size() < MAX_CAPACITY && !isDivided) {
points.removeIf(entry -> entry.position.equals(point) && entry.data.equals(player));
return true;
}
if (!isDivided) {
return false;
}
for (Octree<T> child : children) {
if (child.remove(point, player)) {
return true;
}
}
return false;
}
public List<T> queryRange(BoundingBox range) {
List<T> found = new ArrayList<>();
if (!boundary.intersects(range)) {
return found;
}
for (Entry entry : points) {
if (range.contains(entry.position)) {
found.add(entry.data);
}
}
if (isDivided) {
for (Octree<T> child : children) {
found.addAll(child.queryRange(range));
}
}
return found;
}
private void subdivide() {
double x = boundary.center.x;
double y = boundary.center.y;
double z = boundary.center.z;
double newHalfSize = boundary.halfSize / 2.0;
children = new Octree[8];
children[0] = new Octree<>(new BoundingBox(new Point3D(x - newHalfSize, y - newHalfSize, z - newHalfSize), newHalfSize));
children[1] = new Octree<>(new BoundingBox(new Point3D(x + newHalfSize, y - newHalfSize, z - newHalfSize), newHalfSize));
children[2] = new Octree<>(new BoundingBox(new Point3D(x - newHalfSize, y + newHalfSize, z - newHalfSize), newHalfSize));
children[3] = new Octree<>(new BoundingBox(new Point3D(x + newHalfSize, y + newHalfSize, z - newHalfSize), newHalfSize));
children[4] = new Octree<>(new BoundingBox(new Point3D(x - newHalfSize, y - newHalfSize, z + newHalfSize), newHalfSize));
children[5] = new Octree<>(new BoundingBox(new Point3D(x + newHalfSize, y - newHalfSize, z + newHalfSize), newHalfSize));
children[6] = new Octree<>(new BoundingBox(new Point3D(x - newHalfSize, y + newHalfSize, z + newHalfSize), newHalfSize));
children[7] = new Octree<>(new BoundingBox(new Point3D(x + newHalfSize, y + newHalfSize, z + newHalfSize), newHalfSize));
isDivided = true;
}
public static class Point3D {
double x, y, z;
public Point3D(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
}
public static class BoundingBox {
Point3D center;
double halfSize;
public BoundingBox(Point3D center, double halfSize) {
this.center = center;
this.halfSize = halfSize;
}
public boolean contains(Point3D point) {
return Math.abs(point.x - center.x) <= halfSize &&
Math.abs(point.y - center.y) <= halfSize &&
Math.abs(point.z - center.z) <= halfSize;
}
public boolean intersects(BoundingBox other) {
return Math.abs(center.x - other.center.x) <= halfSize + other.halfSize &&
Math.abs(center.y - other.center.y) <= halfSize + other.halfSize &&
Math.abs(center.z - other.center.z) <= halfSize + other.halfSize;
}
}
private class Entry {
Point3D position;
T data;
Entry(Point3D position, T data) {
this.position = position;
this.data = data;
}
}
}

View File

@@ -0,0 +1,105 @@
package com.hibiscusmc.hmccosmetics.util;
import com.hibiscusmc.hmccosmetics.HMCCosmeticsPlugin;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.jetbrains.annotations.NotNull;
import java.util.*;
public class PlayerSearchManager implements Listener {
private final Map<UUID, Octree<Player>> worldOctrees = new HashMap<>();
private final Map<UUID, Octree.Point3D> playerPositions = new HashMap<>();
//private static final double WORLD_HALF_SIZE = 30_000_000; // Previous built in value
private final double WORLD_HALF_SIZE;
public PlayerSearchManager(@NotNull HMCCosmeticsPlugin plugin) {
WORLD_HALF_SIZE = (double) plugin.getServer().getMaxWorldSize() / 2;
}
private Octree<Player> getOrCreateOctree(World world) {
return worldOctrees.computeIfAbsent(world.getUID(), $ -> {
Octree.BoundingBox worldBoundary = new Octree.BoundingBox(
new Octree.Point3D(0, 160, 0), WORLD_HALF_SIZE
);
return new Octree<>(worldBoundary);
});
}
private Octree.Point3D toPoint3D(Location location) {
return new Octree.Point3D(location.getX(), location.getY(), location.getZ());
}
public boolean addPlayer(Player player) {
Octree<Player> octree = getOrCreateOctree(player.getWorld());
Octree.Point3D point = toPoint3D(player.getLocation());
if(octree.insert(point, player)) {
playerPositions.put(player.getUniqueId(), point);
return true;
}
return false;
}
public boolean removePlayer(Player player) {
Octree<Player> octree = worldOctrees.get(player.getWorld().getUID());
if (octree == null) return false;
Octree.Point3D point = playerPositions.remove(player.getUniqueId());
if (point != null) return octree.remove(point, player);
return false;
}
public void updatePlayerPosition(Player player) {
removePlayer(player);
addPlayer(player);
}
public List<Player> getPlayersInRange(Location location, double range) {
Octree<Player> octree = worldOctrees.get(location.getWorld().getUID());
if (octree == null) return Collections.emptyList();
Octree.Point3D point = toPoint3D(location);
Octree.BoundingBox searchArea = new Octree.BoundingBox(point, range);
return octree.queryRange(searchArea)
.stream()
.filter(Objects::nonNull)
.toList();
}
public void clear() {
worldOctrees.clear();
playerPositions.clear();
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerMove(PlayerMoveEvent event) {
if (event.hasChangedBlock()) updatePlayerPosition(event.getPlayer());
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void onPlayerTeleport(PlayerTeleportEvent event) {
updatePlayerPosition(event.getPlayer());
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerQuit(PlayerQuitEvent event) {
removePlayer(event.getPlayer());
}
@EventHandler(priority = EventPriority.NORMAL)
public void onPlayerJoin(PlayerJoinEvent event) {
addPlayer(event.getPlayer());
}
}

View File

@@ -5,6 +5,7 @@ import com.hibiscusmc.hmccosmetics.cosmetic.CosmeticSlot;
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
import com.hibiscusmc.hmccosmetics.user.CosmeticUsers;
import com.hibiscusmc.hmccosmetics.util.HMCCInventoryUtils;
import me.lojosho.hibiscuscommons.nms.MinecraftVersion;
import me.lojosho.hibiscuscommons.nms.NMSHandlers;
import me.lojosho.hibiscuscommons.util.packets.PacketManager;
import org.bukkit.Location;
@@ -19,10 +20,14 @@ import org.jetbrains.annotations.NotNull;
import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class HMCCPacketManager extends PacketManager {
// The cloud effect map, in case it gets lost: Map<Integer, Number> dataValues = Map.of(0, (byte) 0x20, 8, 0f);
private static final List<CosmeticSlot> EQUIPMENT_SLOTS = List.of(CosmeticSlot.HELMET, CosmeticSlot.CHESTPLATE, CosmeticSlot.LEGGINGS, CosmeticSlot.BOOTS, CosmeticSlot.MAINHAND, CosmeticSlot.OFFHAND);
public static void sendEntitySpawnPacket(
@@ -83,15 +88,6 @@ public class HMCCPacketManager extends PacketManager {
equipmentSlotUpdate(entityId, HMCCInventoryUtils.getEquipmentSlot(cosmeticSlot), user.getUserCosmeticItem(cosmeticSlot), sendTo);
}
public static void sendArmorstandMetadata(
int entityId,
List<Player> sendTo
) {
byte mask = (byte) (Settings.isBackpackPreventDarkness() ? 0x21 : 0x20);
Map<Integer, Number> dataValues = Map.of(0, mask, 15, (byte) 0x10);
NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(entityId, dataValues, sendTo);
}
public static void sendInvisibilityPacket(
int entityId,
List<Player> sendTo
@@ -99,14 +95,50 @@ public class HMCCPacketManager extends PacketManager {
NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(entityId, Map.of(0, (byte) 0x20), sendTo);
}
public static void sendCloudEffect(
public static void spawnCloudAndHandleEffect(
int entityId,
Location location,
UUID uuid,
List<Player> sendTo
) {
NMSHandlers.getHandler().getPacketHandler().sendInvisibleParticleCloud(entityId, location, uuid, sendTo);
}
/**
* This handles both spawn + metadata in a bundle packet
* @param entityId
* @param location
* @param uuid
* @param sendTo
*/
public static void spawnInvisibleArmorstand(
int entityId,
Location location,
UUID uuid,
List<Player> sendTo
) {
byte mask = getMask();
NMSHandlers.getHandler().getPacketHandler().sendInvisibleArmorstand(entityId, location, uuid, mask, sendTo);
}
/**
* This is just a normal meta data packet (non-bundled)
* @param entityId
* @param sendTo
*/
public static void sendArmorstandMetadata(
int entityId,
List<Player> sendTo
) {
Map<Integer, Number> dataValues = Map.of(0, (byte) 0x20, 8, 0f);
byte mask = getMask();
Map<Integer, Number> dataValues = Map.of(0, mask, 15, (byte) 0x10);
NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(entityId, dataValues, sendTo);
}
private static byte getMask() {
return (byte) (Settings.isBackpackPreventDarkness() ? 0x21 : 0x20);
}
public static void sendRotationPacket(
int entityId,
Location location,
@@ -174,22 +206,6 @@ public class HMCCPacketManager extends PacketManager {
sendRidingPacket(mountId, new int[] {passengerId}, sendTo);
}
/**
*
* @param location Location of the fake player.
* @param uuid UUID of the fake player. Should be random.
* @param entityId The entityID that the entity will take on.
* @param sendTo Who should it send the packet to?
*/
public static void sendFakePlayerSpawnPacket(
final @NotNull Location location,
final UUID uuid,
final int entityId,
final @NotNull List<Player> sendTo
) {
sendEntitySpawnPacket(location, entityId, EntityType.PLAYER, uuid, sendTo);
}
/**
* Creates a fake player entity.
* @param skinnedPlayer The original player it bases itself off of.
@@ -215,19 +231,10 @@ public class HMCCPacketManager extends PacketManager {
final int playerId,
final @NotNull List<Player> sendTo
) {
/*
0x01 = Is on fire
0x02 = Is courching
0x04 = Unusued
0x08 = Sprinting
0x10 = Is swimming
0x20 = Invisibile
0x40 = Is Glowing
0x80 = Is flying with an elytra
https://wiki.vg/Entity_metadata#Entity
*/
// https://minecraft.wiki/w/Java_Edition_protocol/Entity_metadata#Avatar
final byte mask = 0x01 | 0x02 | 0x04 | 0x08 | 0x010 | 0x020 | 0x40;
NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(playerId, Map.of(17, mask), sendTo);
if (NMSHandlers.getVersion().isLowerOrEqual(MinecraftVersion.v1_21_8)) NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(playerId, Map.of(17, mask), sendTo);
else NMSHandlers.getHandler().getPacketHandler().sendSharedEntityData(playerId, Map.of(16, mask), sendTo);
}
/**

View File

@@ -6,29 +6,39 @@ config-version: 1
default-menu: defaultmenu_hats
debug-mode: false
database-settings:
type: sqlite # SQLite (Default), MYSQL, NONE
# SQLite (Default), MYSQL, NONE
type: sqlite
mysql:
database: database
password: cherryBomb
database: "database"
password: "cherryBomb"
port: 3306
host: localhost
user: username
host: "localhost"
user: "username"
delay:
enabled: false # This is for if other plugins need to handle certain aspects of a player first.
delay: 20 # In ticks
# This is for if other plugins need to handle certain aspects of a player first.
enabled: false
# Delay in ticks from when a player joins
delay: 20
cosmetic-settings:
tick-period: 20 # In ticks, determines how many times a player cosmetics should be updated. -1 to disable.
# In ticks, determines how many times a player cosmetics should be updated. -1 to disable.
# Ticking periods is something that ensures new players near a player get the packets to actually apply to the user.
tick-period: 20
unapply-on-death: false # If when a player dies, their cosmetics should be unapplied. If this is true, use hmccosmetics.unapplydeath.bypass to bypass
force-permission-join: true # Checks a player permission if they can have a cosmetic when they join the server.
force-show-join: false # If the plugin should force show a player's cosmetics when they join the server.
# If when a player dies, their cosmetics should be unapplied. If this is true, use hmccosmetics.unapplydeath.bypass to bypass
unapply-on-death: false
# Checks a player permission if they can have a cosmetic when they join the server.
force-permission-join: true
# If the plugin should attempt to force show a player's cosmetics when they join the server by removing all hidden flags.
force-show-join: false
# This determines what to process what areas to process placeholders when displaying the item to the user.
# This can be heavy on servers with a lot of players, so if experiencing lag issues relating to setting display/lores of items, disable this.
item-processing:
display-name: true
lore: true
display-name: false
lore: false
# This prevents the offhand from being swapped with the mainhand. Only affects if a user has a cosmetic in their offhand
offhand-prevent-swapping: false
# This is a list of worlds that cosmetics are hidden in. When a player enters one of these worlds, their cosmetics will be hidden.
disabled-worlds:
@@ -44,10 +54,6 @@ cosmetic-settings:
backpack-force-riding-packet: true
# This prevents the backpack from being dark when the player is below a block at the expense of making the backpack bright
backpack-prevent-darkness: true
# This helps reduce the amount of packets sent for packet entities, but reduces accuracy of the entity. This is in milliseconds. -1 to disable.
# This is useful for servers with a lot of backpacks, as they are passengers, which means the client will update their position automatically within an area where the entity is located.
entity-cooldown-teleport-packet: 500
# This forces the offhand to always show the cosmetic. False means it will only show when the slot is empty.
slot-options:
helmet:
@@ -85,6 +91,7 @@ cosmetic-settings:
x: 0.5
y: 3
z: 0.5
menu-settings:
click-cooldown:
enabled: true
@@ -99,20 +106,20 @@ menu-settings:
first-row-shift: "<shift:-153>" # Nexo: "<shift:-153>"
sequent-row-shift: " <shift:-153>" # Nexo: " <shift:-153>" (Space is important here!)
individual-column-shift: "<shift:-3>" # Nexo: "<shift:-3>"
background: "<glyph:shade_row_<row>:colorable> " # Nexo: "<glyph:shade_row_<row>:colorable>
clear-background: "<glyph:clear_row_<row>:colorable> " # Nexo: "<glyph:clear_row_<row>:colorable> "
background: "<glyph:hmcc_shade_row_<row>:colorable> " # Nexo: "<glyph:shade_row_<row>:colorable>
clear-background: "<glyph:hmcc_clear_row_<row>:colorable> " # Nexo: "<glyph:clear_row_<row>:colorable> "
#These use MiniMessage for color! https://docs.advntr.dev/minimessage/format.html#color
equipped-cosmetic-color: "<yellow>"
equipable-cosmetic-color: "<green>"
locked-cosmetic-color: "<red>"
cosmetic-type:
# This allows you to specify if it should require a special click type for the interaction to work.
equip-click: "ANY" # ANY or ClickType, https://jd.papermc.io/paper/1.20/org/bukkit/event/inventory/ClickType.html
unequip-click: "ANY" # ANY or ClickType, https://jd.papermc.io/paper/1.20/org/bukkit/event/inventory/ClickType.html
equip-click: "ANY" # ANY or ClickType, https://jd.papermc.io/paper/org/bukkit/event/inventory/ClickType.html
unequip-click: "ANY" # ANY or ClickType, https://jd.papermc.io/paper/org/bukkit/event/inventory/ClickType.html
dye-menu:
# If you use ItemsAdder, set this to "<white>:img_offset_-8::img_dye_menu:"
# If you use Oraxen, set this to "<white><s:-8><g:dye_menu>"
# If you use Nexo, set this to "<white><shift:-8><glyph:dye_menu>"
# If you use ItemsAdder, set this to "<white>:img_offset_-8::img_hmcc_dye_menu:"
# If you use Oraxen, set this to "<white><s:-8><g:hmcc_dye_menu>"
# If you use Nexo, set this to "<white><shift:-8><glyph:hmcc_dye_menu>"
title: "<white>"
input-slot: 10
output-slot: 16
@@ -122,7 +129,8 @@ hook-settings:
# could cause console spam as HMCCosmetics has to reload itself as well.
reload-on-change: false
nexo:
# This causes the plugin to reload itself after any Nexo change. This keeps the plugin fully up to date with Nexo
# This causes the plugin to reload itself after any Nexo change. This keeps the plugin fully up to date with Nexo, but
# could cause console spam as HMCCosmetics has to reload itself as well.
reload-on-change: true
worldguard:
# Checks worldguard regions for HMCC flags. If set to false, flags will not work properly.
@@ -170,8 +178,10 @@ wardrobe:
title-fade-out: 1000 # milliseconds
wardrobes:
default:
distance: -1 # Distance in blocks that a player can interact with the wardrobe. -1 to ignore.
permission: "hmccosmetics.wardrobe.default" # Permission required to use the wardrobe.
# Distance in blocks that a player can interact with the wardrobe. -1 to ignore.
distance: -1
# Permission required to use the wardrobe.
permission: "hmccosmetics.wardrobe.default"
default-menu: defaultmenu
npc-location:
world: "world"