mirror of
https://github.com/Xiao-MoMi/Custom-Nameplates.git
synced 2026-01-04 15:31:47 +00:00
3.0.5
This commit is contained in:
@@ -20,6 +20,7 @@ package net.momirealms.customnameplates.api;
|
||||
import io.netty.channel.Channel;
|
||||
import net.momirealms.customnameplates.api.feature.Feature;
|
||||
import net.momirealms.customnameplates.api.feature.TimeStampData;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TeamView;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
import net.momirealms.customnameplates.api.placeholder.Placeholder;
|
||||
import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
|
||||
@@ -48,6 +49,8 @@ public abstract class AbstractCNPlayer implements CNPlayer {
|
||||
private String equippedNameplate;
|
||||
private String equippedBubble;
|
||||
|
||||
private final TeamView teamView = new TeamView();
|
||||
|
||||
private final Map<Integer, TimeStampData<String>> cachedValues = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, WeakHashMap<CNPlayer, TimeStampData<String>>> cachedRelationalValues = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -491,6 +494,11 @@ public abstract class AbstractCNPlayer implements CNPlayer {
|
||||
.build(), plugin.getScheduler().async());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeamView teamView() {
|
||||
return teamView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
|
||||
@@ -20,6 +20,7 @@ package net.momirealms.customnameplates.api;
|
||||
import io.netty.channel.Channel;
|
||||
import net.momirealms.customnameplates.api.feature.Feature;
|
||||
import net.momirealms.customnameplates.api.feature.TimeStampData;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TeamView;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
import net.momirealms.customnameplates.api.placeholder.Placeholder;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
@@ -393,4 +394,11 @@ public interface CNPlayer {
|
||||
* Save the player's current nameplate/bubble to database
|
||||
*/
|
||||
void save();
|
||||
|
||||
/**
|
||||
* Get the player's team view
|
||||
*
|
||||
* @return team view
|
||||
*/
|
||||
TeamView teamView();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) <2024> <XiaoMoMi>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.momirealms.customnameplates.api.feature.tag;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class TeamView {
|
||||
|
||||
private final HashMap<String, Set<String>> teamMembers = new HashMap<>();
|
||||
|
||||
@Nullable
|
||||
public Set<String> getTeamMembers(String team) {
|
||||
return teamMembers.get(team);
|
||||
}
|
||||
|
||||
public void addTeamMembers(String team, Collection<String> members) {
|
||||
teamMembers.computeIfAbsent(team, k -> new HashSet<>(members));
|
||||
}
|
||||
|
||||
public void removeTeamMembers(String team, Collection<String> members) {
|
||||
Set<String> membersSet = teamMembers.get(team);
|
||||
if (membersSet != null) {
|
||||
membersSet.removeAll(members);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTeam(String team) {
|
||||
this.teamMembers.remove(team);
|
||||
}
|
||||
}
|
||||
@@ -17,17 +17,13 @@
|
||||
|
||||
package net.momirealms.customnameplates.bukkit;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.momirealms.customnameplates.api.*;
|
||||
import net.momirealms.customnameplates.api.event.NameplatesReloadEvent;
|
||||
import net.momirealms.customnameplates.api.feature.ChatListener;
|
||||
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
|
||||
import net.momirealms.customnameplates.api.feature.OffsetFont;
|
||||
import net.momirealms.customnameplates.api.feature.PlayerListener;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.helper.VersionHelper;
|
||||
import net.momirealms.customnameplates.api.placeholder.internal.StaticPosition;
|
||||
import net.momirealms.customnameplates.api.util.Vector3;
|
||||
import net.momirealms.customnameplates.backend.feature.actionbar.ActionBarManagerImpl;
|
||||
import net.momirealms.customnameplates.backend.feature.advance.AdvanceManagerImpl;
|
||||
@@ -42,6 +38,7 @@ import net.momirealms.customnameplates.backend.placeholder.PlaceholderManagerImp
|
||||
import net.momirealms.customnameplates.backend.storage.StorageManagerImpl;
|
||||
import net.momirealms.customnameplates.bukkit.command.BukkitCommandManager;
|
||||
import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExpansion;
|
||||
import net.momirealms.customnameplates.bukkit.compatibility.NameplatesExtraExpansion;
|
||||
import net.momirealms.customnameplates.bukkit.compatibility.cosmetic.MagicCosmeticsHook;
|
||||
import net.momirealms.customnameplates.bukkit.requirement.BukkitRequirementManager;
|
||||
import net.momirealms.customnameplates.bukkit.scheduler.BukkitSchedulerAdapter;
|
||||
@@ -57,7 +54,6 @@ import net.momirealms.customnameplates.common.plugin.logging.PluginLogger;
|
||||
import net.momirealms.customnameplates.common.plugin.scheduler.AbstractJavaScheduler;
|
||||
import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerAdapter;
|
||||
import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask;
|
||||
import net.momirealms.customnameplates.common.util.Pair;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
@@ -200,6 +196,7 @@ public class BukkitCustomNameplates extends CustomNameplates implements Listener
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("PlaceholderAPI")) {
|
||||
new NameplatesExpansion(this).register();
|
||||
new NameplatesExtraExpansion(this).register();
|
||||
}
|
||||
if (Bukkit.getPluginManager().isPluginEnabled("MagicCosmetics")) {
|
||||
try {
|
||||
|
||||
@@ -37,6 +37,7 @@ import net.momirealms.customnameplates.bukkit.util.BiomeUtils;
|
||||
import net.momirealms.customnameplates.bukkit.util.EntityData;
|
||||
import net.momirealms.customnameplates.bukkit.util.Reflections;
|
||||
import net.momirealms.customnameplates.common.util.TriConsumer;
|
||||
import net.momirealms.customnameplates.common.util.UUIDUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
@@ -296,27 +297,77 @@ public class BukkitPlatform implements Platform {
|
||||
}
|
||||
}, "ClientboundSetEntityDataPacket", "PacketPlayOutEntityMetadata");
|
||||
|
||||
// not a perfect solution but would work in most cases
|
||||
registerPacketConsumer((player, event, packet) -> {
|
||||
if (!ConfigManager.nametagModule()) return;
|
||||
if (!ConfigManager.hideTeamNames()) return;
|
||||
try {
|
||||
int method = (int) Reflections.field$ClientboundSetPlayerTeamPacket$method.get(packet);
|
||||
if (method == 0 || method == 2) {
|
||||
// How to handle mixed entity team packs
|
||||
// @SuppressWarnings("unchecked")
|
||||
// Collection<String> entities = (Collection<String>) Reflections.field$ClientboundSetPlayerTeamPacket$players.get(packet);
|
||||
// outer: {
|
||||
// for (String entity : entities) {
|
||||
// if (!UUIDUtils.isUUID(entity)) {
|
||||
// break outer;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalParameters = (Optional<Object>) Reflections.field$ClientboundSetPlayerTeamPacket$parameters.get(packet);
|
||||
if (optionalParameters.isPresent()) {
|
||||
Object parameters = optionalParameters.get();
|
||||
Reflections.field$ClientboundSetPlayerTeamPacket$Parameters$nametagVisibility.set(parameters, "never");
|
||||
String teamName = (String) Reflections.field$ClientboundSetPlayerTeamPacket$name.get(packet);
|
||||
switch (method) {
|
||||
// create
|
||||
case 0 -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<String> entities = (Collection<String>) Reflections.field$ClientboundSetPlayerTeamPacket$players.get(packet);
|
||||
player.teamView().addTeamMembers(teamName, entities);
|
||||
// additional check for those teams with only one member
|
||||
if (entities.size() <= 1) {
|
||||
for (String entity : entities) {
|
||||
// is a player
|
||||
if (!UUIDUtils.isUUID(entity)) {
|
||||
Player p = Bukkit.getPlayer(entity);
|
||||
// it's a fake player
|
||||
if (p == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalParameters = (Optional<Object>) Reflections.field$ClientboundSetPlayerTeamPacket$parameters.get(packet);
|
||||
if (optionalParameters.isPresent()) {
|
||||
Object parameters = optionalParameters.get();
|
||||
Reflections.field$ClientboundSetPlayerTeamPacket$Parameters$nametagVisibility.set(parameters, "never");
|
||||
}
|
||||
}
|
||||
// remove
|
||||
case 1 -> {
|
||||
player.teamView().removeTeam(teamName);
|
||||
}
|
||||
// update
|
||||
case 2 -> {
|
||||
Set<String> members = player.teamView().getTeamMembers(teamName);
|
||||
if (members == null) return;
|
||||
if (members.size() <= 1) {
|
||||
for (String entity : members) {
|
||||
// is a player
|
||||
if (!UUIDUtils.isUUID(entity)) {
|
||||
Player p = Bukkit.getPlayer(entity);
|
||||
// it's a fake player
|
||||
if (p == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
Optional<Object> optionalParameters = (Optional<Object>) Reflections.field$ClientboundSetPlayerTeamPacket$parameters.get(packet);
|
||||
if (optionalParameters.isPresent()) {
|
||||
Object parameters = optionalParameters.get();
|
||||
Reflections.field$ClientboundSetPlayerTeamPacket$Parameters$nametagVisibility.set(parameters, "never");
|
||||
}
|
||||
}
|
||||
// add members
|
||||
case 3 -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<String> entities = (Collection<String>) Reflections.field$ClientboundSetPlayerTeamPacket$players.get(packet);
|
||||
player.teamView().addTeamMembers(teamName, entities);
|
||||
}
|
||||
// remove members
|
||||
case 4 -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<String> entities = (Collection<String>) Reflections.field$ClientboundSetPlayerTeamPacket$players.get(packet);
|
||||
player.teamView().removeTeamMembers(teamName, entities);
|
||||
}
|
||||
}
|
||||
} catch (ReflectiveOperationException e) {
|
||||
|
||||
@@ -1006,6 +1006,12 @@ public class Reflections {
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundSetPlayerTeamPacket$name = requireNonNull(
|
||||
ReflectionUtils.getInstanceDeclaredField(
|
||||
clazz$ClientboundSetPlayerTeamPacket, String.class, 0
|
||||
)
|
||||
);
|
||||
|
||||
public static final Field field$ClientboundSetPlayerTeamPacket$parameters = requireNonNull(
|
||||
ReflectionUtils.getInstanceDeclaredField(
|
||||
clazz$ClientboundSetPlayerTeamPacket, Optional.class, 0
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (C) <2024> <XiaoMoMi>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.momirealms.customnameplates.common.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public enum MoonPhase {
|
||||
|
||||
FULL_MOON(0L),
|
||||
WANING_GIBBOUS(1L),
|
||||
LAST_QUARTER(2L),
|
||||
WANING_CRESCENT(3L),
|
||||
NEW_MOON(4L),
|
||||
WAXING_CRESCENT(5L),
|
||||
FIRST_QUARTER(6L),
|
||||
WAXING_GIBBOUS(7L);
|
||||
|
||||
private final long day;
|
||||
|
||||
MoonPhase(long day) {
|
||||
this.day = day;
|
||||
}
|
||||
|
||||
private static final Map<Long, MoonPhase> BY_DAY = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (MoonPhase phase : values()) {
|
||||
BY_DAY.put(phase.day, phase);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static MoonPhase getPhase(long day) {
|
||||
return BY_DAY.get(day % 8L);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (C) <2024> <XiaoMoMi>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.momirealms.customnameplates.bukkit.compatibility;
|
||||
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.clip.placeholderapi.expansion.PlaceholderExpansion;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
|
||||
import net.momirealms.customnameplates.api.feature.OffsetFont;
|
||||
import net.momirealms.customnameplates.api.feature.background.Background;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.common.util.MoonPhase;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class NameplatesExtraExpansion extends PlaceholderExpansion {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
|
||||
public NameplatesExtraExpansion(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getIdentifier() {
|
||||
return "npex";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getAuthor() {
|
||||
return "XiaoMoMi";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getVersion() {
|
||||
return "3.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String onRequest(OfflinePlayer player, @NotNull String params) {
|
||||
String[] split = params.split("_", 2);
|
||||
if (split.length == 0) {
|
||||
return null;
|
||||
}
|
||||
switch (split[0]) {
|
||||
case "offset" -> {
|
||||
if (split.length != 2) {
|
||||
return null;
|
||||
}
|
||||
return OffsetFont.createOffsets(Float.parseFloat(split[1]));
|
||||
}
|
||||
case "background" -> {
|
||||
if (split.length != 2) {
|
||||
return null;
|
||||
}
|
||||
String subParams = split[1];
|
||||
String[] subSplit = subParams.split(":", 4);
|
||||
// 0 1 2 3
|
||||
// config:left:right:advance
|
||||
Optional<Background> optional = CustomNameplatesAPI.getInstance().getBackground(subSplit[0]);
|
||||
float advance;
|
||||
try {
|
||||
advance = Float.parseFloat(subSplit[3]);
|
||||
} catch (NumberFormatException e) {
|
||||
String text;
|
||||
if (subSplit[3].startsWith("{") && subSplit[3].endsWith("}")) {
|
||||
String before = "%" + subSplit[3].substring(1, subSplit[3].length() - 1) + "%";
|
||||
text = PlaceholderAPI.setPlaceholders(player, before);
|
||||
advance = Float.parseFloat(text);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
float finalAdvance = advance;
|
||||
return optional.map(background ->
|
||||
AdventureHelper.surroundWithNameplatesFont(background.createImage(finalAdvance, Float.parseFloat(subSplit[1]), Float.parseFloat(subSplit[2]))))
|
||||
.orElse(null);
|
||||
}
|
||||
case "lunarphase" -> {
|
||||
if (split.length == 1 && player != null && player.isOnline()) {
|
||||
Player online = player.getPlayer();
|
||||
if (online == null) return null;
|
||||
return MoonPhase.getPhase(online.getWorld().getFullTime() / 24_000).name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
if (split.length == 2) {
|
||||
String world = split[1];
|
||||
World bukkitWorld = Bukkit.getWorld(world);
|
||||
if (bukkitWorld == null) return null;
|
||||
return MoonPhase.getPhase(bukkitWorld.getFullTime() / 24_000).name().toLowerCase(Locale.ENGLISH);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
case "weather" -> {
|
||||
World world = null;
|
||||
if (split.length == 1 && player != null && player.isOnline()) {
|
||||
Player online = player.getPlayer();
|
||||
if (online == null) return null;
|
||||
world = online.getWorld();
|
||||
}
|
||||
if (split.length == 2) {
|
||||
World bukkitWorld = Bukkit.getWorld(split[1]);
|
||||
if (bukkitWorld == null) return null;
|
||||
world = bukkitWorld;
|
||||
}
|
||||
if (world == null) return null;
|
||||
String currentWeather;
|
||||
if (world.isClearWeather()) currentWeather = "clear";
|
||||
else if (world.isThundering()) currentWeather = "thunder";
|
||||
else currentWeather = "rain";
|
||||
return currentWeather;
|
||||
}
|
||||
case "time12" -> {
|
||||
long time;
|
||||
if (split.length == 1 && player != null && player.isOnline()) {
|
||||
Player online = player.getPlayer();
|
||||
if (online == null) return null;
|
||||
time = online.getWorld().getTime();
|
||||
} else if (split.length == 2) {
|
||||
String world = split[1];
|
||||
World bukkitWorld = Bukkit.getWorld(world);
|
||||
if (bukkitWorld == null) return null;
|
||||
time = bukkitWorld.getTime();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
String ap = time >= 6000 && time < 18000 ? " PM" : " AM";
|
||||
int hours = (int) (time / 1000) ;
|
||||
int minutes = (int) ((time - hours * 1000 ) * 0.06);
|
||||
hours += 6;
|
||||
while (hours >= 12) hours -= 12;
|
||||
if (minutes < 10) return hours + ":0" + minutes + ap;
|
||||
else return hours + ":" + minutes + ap;
|
||||
}
|
||||
case "time24" -> {
|
||||
long time;
|
||||
if (split.length == 1 && player != null && player.isOnline()) {
|
||||
Player online = player.getPlayer();
|
||||
if (online == null) return null;
|
||||
time = online.getWorld().getTime();
|
||||
} else if (split.length == 2) {
|
||||
String world = split[1];
|
||||
World bukkitWorld = Bukkit.getWorld(world);
|
||||
if (bukkitWorld == null) return null;
|
||||
time = bukkitWorld.getTime();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
int hours = (int) (time / 1000);
|
||||
int minutes = (int) ((time - hours * 1000) * 0.06);
|
||||
hours += 6;
|
||||
if (hours >= 24) hours -= 24;
|
||||
String minuteStr = (minutes < 10) ? "0" + minutes : String.valueOf(minutes);
|
||||
return hours + ":" + minuteStr;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user