mirror of
https://github.com/HibiscusMC/HMCCosmetics.git
synced 2025-12-19 15:09:19 +00:00
Merge pull request #198 from SolaraGames/optimize-viewer
Optimize Cosmetics
This commit is contained in:
@@ -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.user.manager.UserSearchManager;
|
||||
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 UserSearchManager userSearchManager;
|
||||
|
||||
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.userSearchManager = new UserSearchManager();
|
||||
|
||||
// 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(userSearchManager, this);
|
||||
|
||||
if (HibiscusCommonsPlugin.isOnPaper()) {
|
||||
getServer().getPluginManager().registerEvents(new PaperPlayerGameListener(), this);
|
||||
|
||||
@@ -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;
|
||||
@@ -104,16 +105,21 @@ public abstract class Cosmetic {
|
||||
* Dispatched when an update is requested upon the cosmetic.
|
||||
* @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.
|
||||
* @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
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.hibiscusmc.hmccosmetics.cosmetic.behavior;
|
||||
|
||||
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
|
||||
import org.bukkit.Location;
|
||||
|
||||
public interface CosmeticMovementBehavior {
|
||||
void dispatchMove(
|
||||
final CosmeticUser user,
|
||||
final Location from,
|
||||
final Location to
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.hibiscusmc.hmccosmetics.cosmetic.behavior;
|
||||
|
||||
import com.hibiscusmc.hmccosmetics.user.CosmeticUser;
|
||||
|
||||
public interface CosmeticUpdateBehavior {
|
||||
void dispatchUpdate(final CosmeticUser user);
|
||||
}
|
||||
@@ -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(CosmeticUser user) {
|
||||
if (user.isInWardrobe()) return;
|
||||
Entity entity = Bukkit.getEntity(user.getUniqueId());
|
||||
if (entity == null) return;
|
||||
|
||||
@@ -2,21 +2,19 @@ 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;
|
||||
import com.hibiscusmc.hmccosmetics.util.MessagesUtil;
|
||||
import com.hibiscusmc.hmccosmetics.util.packets.HMCCPacketManager;
|
||||
import lombok.Getter;
|
||||
import me.lojosho.hibiscuscommons.nms.NMSHandlers;
|
||||
import me.lojosho.hibiscuscommons.packets.BundledRidingData;
|
||||
import me.lojosho.hibiscuscommons.util.packets.PacketManager;
|
||||
import me.lojosho.shaded.configurate.ConfigurationNode;
|
||||
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;
|
||||
@@ -26,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;
|
||||
|
||||
@@ -42,30 +39,26 @@ public class CosmeticBackpackType extends Cosmetic {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdate(@NotNull CosmeticUser user) {
|
||||
public void dispatchUpdate(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();
|
||||
if(backpackManager == null) return;
|
||||
|
||||
UserEntity entityManager = backpackManager.getEntityManager();
|
||||
if(entityManager == null) return;
|
||||
|
||||
int firstArmorStandId = backpackManager.getFirstArmorStandId();
|
||||
|
||||
List<Player> newViewers = entityManager.refreshViewers(loc);
|
||||
|
||||
entityManager.teleport(loc);
|
||||
entityManager.setRotation((int) loc.getYaw(), isFirstPersonCompadible());
|
||||
|
||||
if (!newViewers.isEmpty()) {
|
||||
if(!newViewers.isEmpty()) {
|
||||
HMCCPacketManager.spawnInvisibleArmorstand(firstArmorStandId, entityLocation, UUID.randomUUID(), newViewers);
|
||||
PacketManager.equipmentSlotUpdate(firstArmorStandId, EquipmentSlot.HEAD, user.getUserCosmeticItem(this, getItem()), newViewers);
|
||||
|
||||
@@ -101,11 +94,28 @@ public class CosmeticBackpackType extends Cosmetic {
|
||||
backpackManager.showBackpack();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchMove(CosmeticUser user, Location from, 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();
|
||||
|
||||
entityManager.teleport(loc);
|
||||
entityManager.setRotation((int) loc.getYaw(), isFirstPersonCompadible());
|
||||
}
|
||||
|
||||
public boolean isFirstPersonCompadible() {
|
||||
return firstPersonBackpack != null;
|
||||
}
|
||||
|
||||
public ItemStack getFirstPersonBackpack() {
|
||||
return firstPersonBackpack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -14,11 +16,10 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
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 +55,7 @@ public class CosmeticBalloonType extends Cosmetic {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doUpdate(@NotNull CosmeticUser user) {
|
||||
public void dispatchUpdate(CosmeticUser user) {
|
||||
Entity entity = Bukkit.getEntity(user.getUniqueId());
|
||||
UserBalloonManager userBalloonManager = user.getBalloonManager();
|
||||
|
||||
@@ -66,6 +67,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(CosmeticUser user, Location from, 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 +114,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) {
|
||||
|
||||
@@ -40,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)
|
||||
@@ -159,15 +158,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;
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
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 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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -299,10 +302,31 @@ public class CosmeticUser implements CosmeticHolder {
|
||||
|
||||
@Override
|
||||
public void updateCosmetic(@NotNull CosmeticSlot slot) {
|
||||
Cosmetic cosmetic = playerCosmetics.get(slot);
|
||||
if (cosmetic != null) {
|
||||
cosmetic.update(this);
|
||||
final Cosmetic cosmetic = playerCosmetics.get(slot);
|
||||
if(cosmetic == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!(cosmetic instanceof CosmeticUpdateBehavior behavior)) {
|
||||
throw new IllegalArgumentException("attempted to update a cosmetic that does not implement CosmeticUpdateBehavior, " +
|
||||
"please ensure this cosmetic is properly allowed to update.");
|
||||
}
|
||||
|
||||
behavior.dispatchUpdate(this);
|
||||
}
|
||||
|
||||
public void updateMovementCosmetic(CosmeticSlot slot, final Location from, final Location to) {
|
||||
final Cosmetic cosmetic = playerCosmetics.get(slot);
|
||||
if(cosmetic == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!(cosmetic instanceof CosmeticMovementBehavior behavior)) {
|
||||
throw new IllegalArgumentException("attempted to update a cosmetic that does not implement CosmeticUpdateBehavior, " +
|
||||
"please ensure this cosmetic is properly allowed to update.");
|
||||
}
|
||||
|
||||
behavior.dispatchMove(this, from, to);
|
||||
}
|
||||
|
||||
public void updateCosmetic(Cosmetic cosmetic) {
|
||||
@@ -311,10 +335,15 @@ public class CosmeticUser implements CosmeticHolder {
|
||||
|
||||
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,12 +354,19 @@ 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) {
|
||||
|
||||
@@ -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,10 +13,7 @@ import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.*;
|
||||
|
||||
public class UserEntity {
|
||||
|
||||
@@ -41,7 +39,10 @@ public class UserEntity {
|
||||
}
|
||||
|
||||
public List<Player> refreshViewers(Location location) {
|
||||
if (System.currentTimeMillis() - viewerLastUpdate <= 1000) return List.of(); //Prevents mass refreshes
|
||||
//Prevents mass refreshes
|
||||
if(System.currentTimeMillis() - viewerLastUpdate <= 3000) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
Entity ownerPlayer = Bukkit.getEntity(owner);
|
||||
if (ownerPlayer == null) {
|
||||
@@ -49,36 +50,55 @@ public class UserEntity {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
final HashSet<Player> players = new HashSet<>(HMCCPacketManager.getViewers(location));
|
||||
final ArrayList<Player> newPlayers = new ArrayList<>();
|
||||
final ArrayList<Player> removePlayers = new ArrayList<>();
|
||||
final List<Player> players = HMCCosmeticsPlugin.getInstance()
|
||||
.getUserSearchManager()
|
||||
.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);
|
||||
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);
|
||||
newPlayerIds.add(player.getUniqueId());
|
||||
}
|
||||
}
|
||||
// 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)) {
|
||||
removePlayers.add(viewerPlayer);
|
||||
removePlayerIds.add(viewerPlayer.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
// If there are players for removal, send the packets to them
|
||||
if (!removePlayers.isEmpty()) {
|
||||
if (!removePlayerIds.isEmpty()) {
|
||||
final List<Player> removePlayers = removePlayerIds.stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
|
||||
HMCCPacketManager.sendEntityDestroyPacket(ids, removePlayers);
|
||||
viewers.removeAll(removePlayers);
|
||||
}
|
||||
setViewerLastUpdate(System.currentTimeMillis());
|
||||
return newPlayers;
|
||||
|
||||
this.setViewerLastUpdate(System.currentTimeMillis());
|
||||
return newPlayerIds.stream()
|
||||
.map(Bukkit::getPlayer)
|
||||
.filter(Objects::nonNull)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public void teleport(Location location) {
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.hibiscusmc.hmccosmetics.user.manager;
|
||||
|
||||
import com.hibiscusmc.hmccosmetics.util.Octree;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class UserSearchManager 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;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerMove(PlayerMoveEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
if (event.hasChangedBlock()) {
|
||||
updatePlayerPosition(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
worldOctrees.clear();
|
||||
playerPositions.clear();
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerQuit(PlayerQuitEvent event) {
|
||||
removePlayer(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerJoin(PlayerJoinEvent event) {
|
||||
addPlayer(event.getPlayer());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user