mirror of
https://github.com/Xiao-MoMi/Custom-Nameplates.git
synced 2025-12-29 19:59:09 +00:00
API
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.backend.feature.actionbar;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
|
||||
import net.momirealms.customnameplates.api.feature.actionbar.ActionBarConfig;
|
||||
import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManager;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ActionBarManagerImpl implements ActionBarManager, JoinQuitListener {
|
||||
|
||||
protected final CustomNameplates plugin;
|
||||
private final LinkedHashMap<String, ActionBarConfig> configs = new LinkedHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, ActionBarSender> senders = new ConcurrentHashMap<>();
|
||||
private ActionBarConfig[] configArray = new ActionBarConfig[0];
|
||||
|
||||
public ActionBarManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// ignore disabled modules
|
||||
if (!ConfigManager.actionbarModule()) return;
|
||||
this.loadConfig();
|
||||
this.resetArray();
|
||||
for (CNPlayer online : plugin.getOnlinePlayers()) {
|
||||
onPlayerJoin(online);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (ActionBarSender sender : senders.values()) {
|
||||
sender.destroy();
|
||||
}
|
||||
this.senders.clear();
|
||||
this.configs.clear();
|
||||
this.resetArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshConditions() {
|
||||
for (ActionBarSender sender : senders.values()) {
|
||||
sender.onConditionTimerCheck();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkHeartBeats() {
|
||||
for (ActionBarSender sender : senders.values()) {
|
||||
sender.onHeartBeatTimer();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetArray() {
|
||||
configArray = configs.values().toArray(new ActionBarConfig[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionBarConfig configById(String name) {
|
||||
return configs.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionBarConfig[] actionBarConfigs() {
|
||||
return configArray;
|
||||
}
|
||||
|
||||
public void handleActionBarPacket(CNPlayer player, String miniMessage) {
|
||||
ActionBarSender sender = senders.get(player.uuid());
|
||||
if (sender != null) {
|
||||
sender.externalActionBar(miniMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getExternalActionBar(CNPlayer player) {
|
||||
ActionBarSender sender = senders.get(player.uuid());
|
||||
if (sender != null) {
|
||||
return sender.externalActionBar();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerJoin(CNPlayer player) {
|
||||
if (!ConfigManager.actionbarModule()) return;
|
||||
plugin.getScheduler().asyncLater(() -> {
|
||||
if (!player.isOnline()) return;
|
||||
ActionBarSender sender = new ActionBarSender(this, player);
|
||||
ActionBarSender previous = senders.put(player.uuid(), sender);
|
||||
if (previous != null) {
|
||||
previous.destroy();
|
||||
}
|
||||
}, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerQuit(CNPlayer player) {
|
||||
ActionBarSender sender = senders.remove(player.uuid());
|
||||
if (sender != null) {
|
||||
sender.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "actionbar.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "actionbar.yml"));
|
||||
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
|
||||
if (!(entry.getValue() instanceof Section section))
|
||||
return;
|
||||
this.configs.put(entry.getKey(),
|
||||
ActionBarConfig.builder()
|
||||
.id(entry.getKey())
|
||||
.requirement(plugin.getRequirementManager().parseRequirements(section.getSection("conditions")))
|
||||
.carouselText(
|
||||
section.contains("text") ?
|
||||
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
|
||||
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
* 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.backend.feature.actionbar;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.DynamicText;
|
||||
import net.momirealms.customnameplates.api.feature.Feature;
|
||||
import net.momirealms.customnameplates.api.feature.actionbar.ActionBarConfig;
|
||||
import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManager;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.placeholder.Placeholder;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
public class ActionBarSender implements Feature {
|
||||
|
||||
private final ActionBarManager manager;
|
||||
private final CNPlayer owner;
|
||||
private long lastUpdateTime;
|
||||
|
||||
private ActionBarConfig currentConfig;
|
||||
private int order;
|
||||
private int timeLeft;
|
||||
private DynamicText currentActionBar;
|
||||
|
||||
private String externalActionBar;
|
||||
private long externalExpireTime;
|
||||
|
||||
private String latestContent;
|
||||
|
||||
private boolean textChangeFlag = false;
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "ActionBarSender";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ActionBarSender sender = (ActionBarSender) o;
|
||||
return owner == sender.owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return owner.name().hashCode();
|
||||
}
|
||||
|
||||
public ActionBarSender(ActionBarManager manager, CNPlayer owner) {
|
||||
this.owner = owner;
|
||||
this.manager = manager;
|
||||
this.owner.addFeature(this);
|
||||
this.onConditionTimerCheck();
|
||||
}
|
||||
|
||||
public void onConditionTimerCheck() {
|
||||
ActionBarConfig[] configs = manager.actionBarConfigs();
|
||||
outer: {
|
||||
for (ActionBarConfig config : configs) {
|
||||
if (owner.isMet(config.requirements())) {
|
||||
if (config != currentConfig) {
|
||||
currentConfig = config;
|
||||
order = config.carouselTexts().length - 1;
|
||||
timeLeft = 0;
|
||||
}
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
||||
// no actionbar available
|
||||
currentConfig = null;
|
||||
currentActionBar = null;
|
||||
latestContent = null;
|
||||
}
|
||||
|
||||
if (currentConfig != null) {
|
||||
|
||||
if (timeLeft > 0)
|
||||
timeLeft--;
|
||||
|
||||
if (timeLeft == 0) {
|
||||
int triedTimes = 0;
|
||||
|
||||
do {
|
||||
if (triedTimes == currentConfig.carouselTexts().length) {
|
||||
timeLeft = 20;
|
||||
latestContent = null;
|
||||
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your actionbar's conditions.");
|
||||
return;
|
||||
}
|
||||
order++;
|
||||
if (order >= currentConfig.carouselTexts().length) {
|
||||
order = 0;
|
||||
}
|
||||
triedTimes++;
|
||||
} while (
|
||||
!owner.isMet(currentConfig.carouselTexts()[order].requirements())
|
||||
);
|
||||
|
||||
CarouselText carouselText = currentConfig.carouselTexts()[order];
|
||||
timeLeft = carouselText.duration();
|
||||
currentActionBar = carouselText.preParsedDynamicText().fastCreate(owner);
|
||||
|
||||
if (carouselText.updateOnDisplay()) {
|
||||
owner.forceUpdate(currentActionBar.placeholders(), Collections.emptySet());
|
||||
}
|
||||
textChangeFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onHeartBeatTimer() {
|
||||
if (textChangeFlag) {
|
||||
refresh();
|
||||
sendLatestActionBar();
|
||||
return;
|
||||
}
|
||||
if (shouldSendBeatPacket()) {
|
||||
sendLatestActionBar();
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
latestContent = this.currentActionBar.render(owner);
|
||||
textChangeFlag = false;
|
||||
}
|
||||
|
||||
public void sendLatestActionBar() {
|
||||
if (latestContent != null) {
|
||||
updateLastUpdateTime();
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().setActionBarTextPacket(AdventureHelper.miniMessageToMinecraftComponent(latestContent, "np", "ab"));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
this.owner.removeFeature(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> activePlaceholders() {
|
||||
if (currentActionBar == null) return Collections.emptySet();
|
||||
return currentActionBar.placeholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> allPlaceholders() {
|
||||
HashSet<Placeholder> placeholders = new HashSet<>();
|
||||
for (ActionBarConfig config : manager.actionBarConfigs()) {
|
||||
for (CarouselText text : config.carouselTexts()) {
|
||||
placeholders.addAll(text.preParsedDynamicText().placeholders());
|
||||
}
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPlaceholderUpdates(CNPlayer player, boolean force) {
|
||||
refresh();
|
||||
sendLatestActionBar();
|
||||
}
|
||||
|
||||
public void updateLastUpdateTime() {
|
||||
lastUpdateTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public boolean shouldSendBeatPacket() {
|
||||
return System.currentTimeMillis() - lastUpdateTime > 1500;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String externalActionBar() {
|
||||
if (System.currentTimeMillis() > externalExpireTime) {
|
||||
externalActionBar = null;
|
||||
}
|
||||
return externalActionBar;
|
||||
}
|
||||
|
||||
public void externalActionBar(@NotNull String externalActionBar) {
|
||||
requireNonNull(externalActionBar);
|
||||
this.externalActionBar = externalActionBar;
|
||||
this.externalExpireTime = System.currentTimeMillis() + 3000;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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.backend.feature.advance;
|
||||
|
||||
public record SizeOverrides(int startCodePoint, int endCodePoint, int left, int right) {
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.backend.feature.background;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
|
||||
import net.momirealms.customnameplates.api.feature.background.Background;
|
||||
import net.momirealms.customnameplates.api.feature.background.BackgroundManager;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class BackgroundManagerImpl implements BackgroundManager {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final Map<String, Background> backgrounds = new HashMap<>();
|
||||
|
||||
public BackgroundManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.backgrounds.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.loadConfigs();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Background backgroundById(String id) {
|
||||
return this.backgrounds.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Background> getBackgrounds() {
|
||||
return new HashSet<>(backgrounds.values());
|
||||
}
|
||||
|
||||
private void loadConfigs() {
|
||||
File backgroundFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "backgrounds");
|
||||
if (!backgroundFolder.exists() && backgroundFolder.mkdirs()) {
|
||||
saveDefaultBackgrounds();
|
||||
}
|
||||
List<File> configFiles = ConfigUtils.getConfigsDeeply(backgroundFolder);
|
||||
for (File configFile : configFiles) {
|
||||
YamlDocument config = plugin.getConfigManager().loadData(configFile);
|
||||
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
|
||||
int height = config.getInt("middle.height", 14);
|
||||
int ascent = config.getInt("middle.ascent", 8);
|
||||
Background background = Background.builder()
|
||||
.id(id)
|
||||
.left(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
|
||||
config.getInt("left.ascent", 8),
|
||||
config.getInt("left.height", 14)
|
||||
))
|
||||
.right(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
|
||||
config.getInt("right.ascent", 8),
|
||||
config.getInt("right.height", 14)
|
||||
))
|
||||
.width_1(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.1") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_2(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.2") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_4(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.4") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_8(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.8") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_16(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.16") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_32(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.32") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_64(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.64") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.width_128(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.128") + ".png"),
|
||||
ascent, height
|
||||
))
|
||||
.build();
|
||||
this.backgrounds.put(id, background);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDefaultBackgrounds() {
|
||||
String[] bg_list = new String[]{"b0", "b1", "b2", "b4", "b8", "b16","b32","b64","b128"};
|
||||
for (String bg : bg_list) {
|
||||
plugin.getConfigManager().saveResource("contents" + File.separator + "backgrounds" + File.separator + bg + ".png");
|
||||
}
|
||||
String[] config_list = new String[]{"bedrock_1", "bedrock_2"};
|
||||
for (String config : config_list) {
|
||||
plugin.getConfigManager().saveResource("contents" + File.separator + "backgrounds" + File.separator + config + ".yml");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.backend.feature.bossbar;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBarConfig;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBarManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BossBarDisplayController {
|
||||
|
||||
private final CNPlayer owner;
|
||||
private final BossBarManager manager;
|
||||
private final BossBarSender[] senders;
|
||||
|
||||
public BossBarDisplayController(BossBarManager manager, CNPlayer owner) {
|
||||
this.owner = owner;
|
||||
this.manager = manager;
|
||||
List<BossBarSender> senderList = new ArrayList<>();
|
||||
for (BossBarConfig config : manager.bossBarConfigs()) {
|
||||
BossBarSender sender = new BossBarSender(owner, config);
|
||||
senderList.add(sender);
|
||||
this.owner.addFeature(sender);
|
||||
}
|
||||
this.senders = senderList.toArray(new BossBarSender[0]);
|
||||
}
|
||||
|
||||
public void onTick() {
|
||||
int size = senders.length;
|
||||
int[] states = new int[size];
|
||||
int index = size;
|
||||
for (int i = 0; i < size; i++) {
|
||||
BossBarSender sender = senders[i];
|
||||
boolean canShow = sender.checkConditions();
|
||||
if (canShow) {
|
||||
if (!sender.isShown()) {
|
||||
states[i] = 1;
|
||||
sender.init();
|
||||
sender.tick();
|
||||
if (index == size) {
|
||||
index = i;
|
||||
}
|
||||
} else {
|
||||
states[i] = 2;
|
||||
if (i > index) {
|
||||
sender.hide();
|
||||
}
|
||||
sender.tick();
|
||||
}
|
||||
} else {
|
||||
if (sender.isShown()) {
|
||||
sender.hide();
|
||||
}
|
||||
states[i] = 0;
|
||||
}
|
||||
}
|
||||
if (index != size) {
|
||||
for (int i = index; i < size; i++) {
|
||||
if (states[i] != 0) {
|
||||
senders[i].show();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
for (BossBarSender sender : this.senders) {
|
||||
sender.hide();
|
||||
this.owner.removeFeature(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.backend.feature.bossbar;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBar;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBarConfig;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBarManager;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BossBarManagerImpl implements BossBarManager, JoinQuitListener {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final LinkedHashMap<String, BossBarConfig> configs = new LinkedHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, BossBarDisplayController> senders = new ConcurrentHashMap<>();
|
||||
private BossBarConfig[] configArray = new BossBarConfig[0];
|
||||
|
||||
public BossBarManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
// ignore disabled modules
|
||||
if (!ConfigManager.bossbarModule()) return;
|
||||
this.loadConfig();
|
||||
this.resetArray();
|
||||
for (CNPlayer online : plugin.getOnlinePlayers()) {
|
||||
onPlayerJoin(online);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (BossBarDisplayController sender : senders.values()) {
|
||||
sender.destroy();
|
||||
}
|
||||
this.senders.clear();
|
||||
this.configs.clear();
|
||||
this.resetArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
for (BossBarDisplayController sender : senders.values()) {
|
||||
sender.onTick();
|
||||
}
|
||||
}
|
||||
|
||||
private void resetArray() {
|
||||
configArray = configs.values().toArray(new BossBarConfig[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerJoin(CNPlayer player) {
|
||||
if (!ConfigManager.bossbarModule()) return;
|
||||
plugin.getScheduler().asyncLater(() -> {
|
||||
if (!player.isOnline()) return;
|
||||
BossBarDisplayController sender = new BossBarDisplayController(this, player);
|
||||
BossBarDisplayController previous = senders.put(player.uuid(), sender);
|
||||
if (previous != null) {
|
||||
previous.destroy();
|
||||
}
|
||||
}, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerQuit(CNPlayer player) {
|
||||
BossBarDisplayController sender = senders.remove(player.uuid());
|
||||
if (sender != null) {
|
||||
sender.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBarConfig configById(String name) {
|
||||
return configs.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BossBarConfig[] bossBarConfigs() {
|
||||
return configArray;
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "bossbar.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "bossbar.yml"));
|
||||
for (Map.Entry<String, Object> entry : document.getStringRouteMappedValues(false).entrySet()) {
|
||||
if (!(entry.getValue() instanceof Section section))
|
||||
return;
|
||||
this.configs.put(entry.getKey(),
|
||||
BossBarConfig.builder()
|
||||
.id(entry.getKey())
|
||||
.overlay(BossBar.Overlay.valueOf(section.getString("overlay", "PROGRESS").toUpperCase(Locale.ENGLISH)))
|
||||
.color(BossBar.Color.valueOf(section.getString("color", "YELLOW").toUpperCase(Locale.ENGLISH)))
|
||||
.requirement(plugin.getRequirementManager().parseRequirements(section.getSection("conditions")))
|
||||
.carouselText(
|
||||
section.contains("text") ?
|
||||
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
|
||||
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
|
||||
)
|
||||
.progress(section.getFloat("progress", 0f))
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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.backend.feature.bossbar;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.DynamicText;
|
||||
import net.momirealms.customnameplates.api.feature.Feature;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBar;
|
||||
import net.momirealms.customnameplates.api.feature.bossbar.BossBarConfig;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.placeholder.Placeholder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class BossBarSender implements Feature, BossBar {
|
||||
|
||||
private final UUID uuid = UUID.randomUUID();
|
||||
private final CNPlayer owner;
|
||||
|
||||
private boolean isShown = false;
|
||||
private final BossBarConfig config;
|
||||
|
||||
private int order;
|
||||
private int timeLeft;
|
||||
private DynamicText currentBossBar;
|
||||
|
||||
private String latestContent;
|
||||
|
||||
public BossBarSender(CNPlayer owner, BossBarConfig config) {
|
||||
this.owner = owner;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
BossBarSender that = (BossBarSender) o;
|
||||
return owner == that.owner && uuid == that.uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UUID uuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "BossBarSender";
|
||||
}
|
||||
|
||||
@Override
|
||||
public float progress() {
|
||||
return config.progress();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color color() {
|
||||
return config.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Overlay overlay() {
|
||||
return config.overlay();
|
||||
}
|
||||
|
||||
public boolean checkConditions() {
|
||||
return owner.isMet(config.requirements());
|
||||
}
|
||||
|
||||
public void init() {
|
||||
order = config.carouselTexts().length - 1;
|
||||
timeLeft = 0;
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
if (timeLeft > 0)
|
||||
timeLeft--;
|
||||
|
||||
if (timeLeft == 0) {
|
||||
int triedTimes = 0;
|
||||
|
||||
do {
|
||||
if (triedTimes == config.carouselTexts().length) {
|
||||
timeLeft = 20;
|
||||
currentBossBar = null;
|
||||
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your bossbar's conditions.");
|
||||
return;
|
||||
}
|
||||
order++;
|
||||
if (order >= config.carouselTexts().length) {
|
||||
order = 0;
|
||||
}
|
||||
triedTimes++;
|
||||
} while (
|
||||
!owner.isMet(config.carouselTexts()[order].requirements())
|
||||
);
|
||||
|
||||
CarouselText carouselText = config.carouselTexts()[order];
|
||||
timeLeft = carouselText.duration();
|
||||
currentBossBar = carouselText.preParsedDynamicText().fastCreate(owner);
|
||||
|
||||
if (carouselText.updateOnDisplay()) {
|
||||
owner.forceUpdate(currentBossBar.placeholders(), Collections.emptySet());
|
||||
}
|
||||
|
||||
refresh();
|
||||
if (isShown()) {
|
||||
sendLatestBossBarName();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
isShown = false;
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().removeBossBarPacket(uuid);
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
|
||||
}
|
||||
|
||||
public void show() {
|
||||
isShown = true;
|
||||
if (latestContent == null) {
|
||||
refresh();
|
||||
}
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().createBossBarPacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent), progress(), overlay(), color());
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
|
||||
}
|
||||
|
||||
public boolean isShown() {
|
||||
return isShown;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> activePlaceholders() {
|
||||
if (currentBossBar == null || !isShown()) return Collections.emptySet();
|
||||
return currentBossBar.placeholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> allPlaceholders() {
|
||||
HashSet<Placeholder> placeholders = new HashSet<>();
|
||||
for (CarouselText text : config.carouselTexts()) {
|
||||
placeholders.addAll(text.preParsedDynamicText().placeholders());
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPlaceholderUpdates(CNPlayer p1, boolean force) {
|
||||
refresh();
|
||||
if (isShown()) {
|
||||
sendLatestBossBarName();
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
latestContent = this.currentBossBar.render(owner);
|
||||
}
|
||||
|
||||
public void sendLatestBossBarName() {
|
||||
if (latestContent != null) {
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().updateBossBarNamePacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* 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.backend.feature.bubble;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ChatListener;
|
||||
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.BubbleManager;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.ChannelMode;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class BubbleManagerImpl implements BubbleManager, ChatListener {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final Map<String, Bubble> bubbles = new HashMap<>();
|
||||
private Requirement[] sendBubbleRequirements;
|
||||
private Requirement[] viewBubbleRequirements;
|
||||
private String defaultBubbleId;
|
||||
private double yOffset;
|
||||
private int stayDuration;
|
||||
private int appearDuration;
|
||||
private int disappearDuration;
|
||||
private float viewRange;
|
||||
private Set<String> blacklistChannels;
|
||||
private ChannelMode channelMode;
|
||||
private final HashMap<String, BubbleConfig> bubbleConfigs = new HashMap<>();
|
||||
|
||||
public BubbleManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.bubbles.clear();
|
||||
this.bubbleConfigs.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (!ConfigManager.bubbleModule()) return;
|
||||
this.loadConfigs();
|
||||
this.loadConfig();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Bubble bubbleById(String id) {
|
||||
return this.bubbles.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BubbleConfig bubbleConfigById(String id) {
|
||||
return this.bubbleConfigs.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Bubble> bubbles() {
|
||||
return new HashSet<>(bubbles.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BubbleConfig> bubbleConfigs() {
|
||||
return new HashSet<>(bubbleConfigs.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasBubble(CNPlayer player, String id) {
|
||||
if (!this.bubbleConfigs.containsKey(id)) {
|
||||
return false;
|
||||
}
|
||||
return player.hasPermission("bubbles.equip." + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<BubbleConfig> availableBubbles(CNPlayer player) {
|
||||
ArrayList<BubbleConfig> available = new ArrayList<>();
|
||||
for (BubbleConfig bubble : bubbleConfigs.values()) {
|
||||
if (player.hasPermission("bubbles.equip." + bubble.id())) {
|
||||
available.add(bubble);
|
||||
}
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> blacklistChannels() {
|
||||
return blacklistChannels;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultBubbleId() {
|
||||
return defaultBubbleId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Requirement[] sendBubbleRequirements() {
|
||||
return sendBubbleRequirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Requirement[] viewBubbleRequirements() {
|
||||
return viewBubbleRequirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double verticalOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int stayDuration() {
|
||||
return stayDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int appearDuration() {
|
||||
return appearDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int disappearDuration() {
|
||||
return disappearDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float viewRange() {
|
||||
return viewRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChannelMode channelMode() {
|
||||
return channelMode;
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "bubble.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "bubble.yml"));
|
||||
sendBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("sender-requirements"));
|
||||
viewBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("viewer-requirements"));
|
||||
defaultBubbleId = document.getString("default-bubble", "chat");
|
||||
yOffset = document.getDouble("y-offset", 0.2);
|
||||
stayDuration = document.getInt("stay-duration", 160);
|
||||
appearDuration = document.getInt("appear-duration", 20);
|
||||
disappearDuration = document.getInt("disappear-duration", 10);
|
||||
viewRange = document.getFloat("view-range", 0.5f);
|
||||
blacklistChannels = new HashSet<>(document.getStringList("blacklist-channels"));
|
||||
channelMode = ChannelMode.valueOf(document.getString("channel-mode", "ALL").toUpperCase(Locale.ENGLISH));
|
||||
Section bubbleSettings = document.getSection("bubble-settings");
|
||||
if (bubbleSettings != null) {
|
||||
for (Map.Entry<String, Object> entry : bubbleSettings.getStringRouteMappedValues(false).entrySet()) {
|
||||
String key = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
int maxLines = inner.getInt("max-lines", 1);
|
||||
Bubble[] bubbleArray = new Bubble[maxLines];
|
||||
for (int i = 0; i < maxLines; i++) {
|
||||
bubbleArray[i] = bubbleById(inner.getString("lines." + (i+1)));
|
||||
}
|
||||
this.bubbleConfigs.put(key, BubbleConfig.builder()
|
||||
.id(key)
|
||||
.maxLines(maxLines)
|
||||
.bubbles(bubbleArray)
|
||||
.displayName(inner.getString("display-name", key))
|
||||
.lineWidth(inner.getInt("line-width", 100))
|
||||
.backgroundColor(ConfigUtils.argb(inner.getString("background-color", "0,0,0,0")))
|
||||
.textPrefix(inner.getString("text-prefix", ""))
|
||||
.textSuffix(inner.getString("text-suffix", ""))
|
||||
.scale(ConfigUtils.vector3(inner.getString("scale", "1,1,1")))
|
||||
.build());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConfigs() {
|
||||
File bubbleFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "bubbles");
|
||||
if (!bubbleFolder.exists() && bubbleFolder.mkdirs()) {
|
||||
saveDefaultBubbles();
|
||||
}
|
||||
List<File> configFiles = ConfigUtils.getConfigsDeeply(bubbleFolder);
|
||||
for (File configFile : configFiles) {
|
||||
YamlDocument config = plugin.getConfigManager().loadData(configFile);
|
||||
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
|
||||
Bubble bubble = Bubble.builder()
|
||||
.id(id)
|
||||
.left(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
|
||||
config.getInt("left.ascent", 12),
|
||||
config.getInt("left.height", 16)
|
||||
))
|
||||
.middle(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.image") + ".png"),
|
||||
config.getInt("middle.ascent", 12),
|
||||
config.getInt("middle.height", 16)
|
||||
))
|
||||
.tail(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("tail.image") + ".png"),
|
||||
config.getInt("tail.ascent", 12),
|
||||
config.getInt("tail.height", 16)
|
||||
))
|
||||
.right(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
|
||||
config.getInt("right.ascent", 12),
|
||||
config.getInt("right.height", 16)
|
||||
))
|
||||
.build();
|
||||
this.bubbles.put(id, bubble);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDefaultBubbles() {
|
||||
String[] png_list = new String[]{"chat_1", "chat_2", "chat_3"};
|
||||
String[] part_list = new String[]{"_left.png", "_middle.png", "_right.png", "_tail.png", ".yml"};
|
||||
for (String name : png_list) {
|
||||
for (String part : part_list) {
|
||||
plugin.getConfigManager().saveResource("contents" + File.separator + "bubbles" + File.separatorChar + name + part);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerChat(CNPlayer player, String message, String channel) {
|
||||
if (!ConfigManager.bubbleModule()) return;
|
||||
// ignore blacklist channels
|
||||
if (blacklistChannels().contains(channel)) return;
|
||||
// check requirements
|
||||
if (!player.isMet(sendBubbleRequirements())) return;
|
||||
|
||||
String equippedBubble = player.equippedBubble();
|
||||
if (equippedBubble.equals("none")) equippedBubble = defaultBubbleId;
|
||||
|
||||
BubbleConfig config = bubbleConfigs.get(equippedBubble);
|
||||
if (config == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String fullText = config.textPrefix() + AdventureHelper.stripTags(message.replace("\\", "\\\\")) + config.textSuffix();
|
||||
int lines = plugin.getAdvanceManager().getLines(fullText, config.lineWidth());
|
||||
if (lines > config.maxLines()) return;
|
||||
|
||||
TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(player);
|
||||
if (renderer == null) return;
|
||||
int removed = renderer.removeTagIf(tag -> tag.id().equals("bubble"));
|
||||
int delay = 0;
|
||||
if (removed != 0) {
|
||||
delay += disappearDuration;
|
||||
}
|
||||
|
||||
Bubble bubble = config.bubbles()[lines - 1];
|
||||
float advance;
|
||||
if (lines == 1) {
|
||||
advance = plugin.getAdvanceManager().getLineAdvance(fullText);
|
||||
} else {
|
||||
advance = config.lineWidth();
|
||||
}
|
||||
|
||||
BubbleTag bubbleTagText = new BubbleTag(player, renderer, channel, config,
|
||||
AdventureHelper.miniMessageToMinecraftComponent(fullText),
|
||||
bubble == null ? null : AdventureHelper.miniMessageToMinecraftComponent(AdventureHelper.surroundWithNameplatesFont(bubble.createImage(advance, 1,1))), this);
|
||||
renderer.addTag(bubbleTagText);
|
||||
if (delay != 0) {
|
||||
plugin.getScheduler().asyncLater(() -> bubbleTagText.setCanShow(true), delay * 50L, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
bubbleTagText.setCanShow(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
/*
|
||||
* 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.backend.feature.bubble;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.BubbleManager;
|
||||
import net.momirealms.customnameplates.api.feature.tag.AbstractTag;
|
||||
import net.momirealms.customnameplates.api.feature.tag.Tag;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
import net.momirealms.customnameplates.api.util.Alignment;
|
||||
import net.momirealms.customnameplates.api.util.SelfIncreaseEntityID;
|
||||
import net.momirealms.customnameplates.api.util.Vector3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class BubbleTag extends AbstractTag {
|
||||
|
||||
private final Object text;
|
||||
@Nullable
|
||||
private final Object background;
|
||||
private final BubbleManager manager;
|
||||
private final BubbleConfig bubbleConfig;
|
||||
private int ticker;
|
||||
private boolean canShow;
|
||||
private final String channel;
|
||||
private final HashMap<UUID, Boolean> cachedVisibility = new HashMap<>();
|
||||
|
||||
protected final int subEntityID = SelfIncreaseEntityID.getAndIncrease();
|
||||
protected final UUID subEntityUUID = UUID.randomUUID();
|
||||
|
||||
public BubbleTag(CNPlayer owner, TagRenderer renderer, String channel, BubbleConfig bubbleConfig, Object text, @Nullable Object background, BubbleManager bubbleManager) {
|
||||
super(owner, renderer);
|
||||
this.text = text;
|
||||
this.manager = bubbleManager;
|
||||
this.bubbleConfig = bubbleConfig;
|
||||
this.channel = channel;
|
||||
this.background = background;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> spawnPacket(CNPlayer viewer) {
|
||||
Tracker tracker = owner.getTracker(viewer);
|
||||
Vector3 translation = translation(viewer);
|
||||
List<Object> packets = new ArrayList<>(CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
|
||||
entityID, uuid,
|
||||
owner.position().add(0, (1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1), 0),
|
||||
0f, 0f, 0d,
|
||||
-1, 0, 0,
|
||||
text, bubbleConfig.backgroundColor(), (byte) -1, false, false, false,
|
||||
Alignment.CENTER, manager.viewRange(), 0.0f, 1.0f,
|
||||
new Vector3(0.001, 0.001, 0.001),
|
||||
affectedByScaling() ? translation.multiply(tracker.getScale()).add(0.01, 0, 0.01) : translation,
|
||||
bubbleConfig.lineWidth(),
|
||||
(affectedByCrouching() && tracker.isCrouching())
|
||||
));
|
||||
if (background != null) {
|
||||
packets.addAll(CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
|
||||
subEntityID, subEntityUUID,
|
||||
owner.position().add(0,(1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1),0),
|
||||
0f, 0f, 0d,
|
||||
-1, 0, 0,
|
||||
background, 0, (byte) -1, false, false, false,
|
||||
Alignment.CENTER, manager.viewRange(), 0.0f, 1.0f,
|
||||
new Vector3(0.001, 0.001, 0.001),
|
||||
affectedByScaling() ? translation.multiply(tracker.getScale()) : translation,
|
||||
2048,
|
||||
(affectedByCrouching() && tracker.isCrouching())
|
||||
));
|
||||
}
|
||||
return packets;
|
||||
}
|
||||
|
||||
public void setCanShow(boolean canShow) {
|
||||
this.canShow = canShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow() {
|
||||
return canShow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow(CNPlayer viewer) {
|
||||
if (!viewer.isMet(owner, manager.viewBubbleRequirements())) {
|
||||
return false;
|
||||
}
|
||||
switch (manager.channelMode()) {
|
||||
case ALL -> {
|
||||
return true;
|
||||
}
|
||||
case JOINED -> {
|
||||
Boolean previous = cachedVisibility.get(viewer.uuid());
|
||||
if (previous != null) return previous;
|
||||
boolean can = CustomNameplates.getInstance().getChatManager().chatProvider().hasJoinedChannel(viewer, channel);
|
||||
cachedVisibility.put(viewer.uuid(), can);
|
||||
return can;
|
||||
}
|
||||
case CAN_JOIN -> {
|
||||
Boolean previous = cachedVisibility.get(viewer.uuid());
|
||||
if (previous != null) return previous;
|
||||
boolean can = CustomNameplates.getInstance().getChatManager().chatProvider().canJoinChannel(viewer, channel);
|
||||
cachedVisibility.put(viewer.uuid(), can);
|
||||
return can;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(CNPlayer viewer) {
|
||||
if (!isShown()) return;
|
||||
viewers.add(viewer);
|
||||
resetViewerArray();
|
||||
owner.trackPassengers(viewer, entityID);
|
||||
if (background != null)
|
||||
owner.trackPassengers(viewer, subEntityID);
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, spawnPacket(viewer));
|
||||
Tracker tracker = owner.getTracker(viewer);
|
||||
CustomNameplates.getInstance().getScheduler().asyncLater(() -> {
|
||||
Consumer<List<Object>> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1);
|
||||
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.appearDuration());
|
||||
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createScaleModifier(affectedByScaling() ? bubbleConfig.scale().multiply(tracker.getScale()) : bubbleConfig.scale());
|
||||
Object packet1 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2));
|
||||
if (background != null) {
|
||||
Object packet2 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier0, modifier1, modifier2));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, List.of(packet1, packet2));
|
||||
} else {
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet1);
|
||||
}
|
||||
}, 100, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (!canShow) return;
|
||||
if (ticker >= manager.stayDuration()) {
|
||||
renderer.removeTag(this);
|
||||
return;
|
||||
}
|
||||
ticker++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerScaleUpdate(CNPlayer viewer, double scale) {
|
||||
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createScaleModifier(scale(viewer).multiply(scale));
|
||||
Vector3 translation = translation(viewer);
|
||||
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation.multiply(scale).add(0.01,0,0.01));
|
||||
Object packet1 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier1, modifier2));
|
||||
if (background != null) {
|
||||
Consumer<List<Object>> modifier3 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation.multiply(scale));
|
||||
Object packet2 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier1, modifier3));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, List.of(packet1, packet2));
|
||||
} else {
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTranslation() {
|
||||
for (CNPlayer player : viewerArray) {
|
||||
Tracker tracker = owner.getTracker(player);
|
||||
if (tracker != null) {
|
||||
Vector3 translation = translation(player);
|
||||
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation.multiply(tracker.getScale()).add(0.01,0,0.01));
|
||||
Object packet1 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier1));
|
||||
if (background != null) {
|
||||
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createTranslationModifier(translation.multiply(tracker.getScale()));
|
||||
Object packet2 = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier2));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(player, List.of(packet1, packet2));
|
||||
} else {
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(player, packet1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
if (!isShown()) return;
|
||||
|
||||
CNPlayer[] viewers = viewerArray.clone();
|
||||
CustomNameplates.getInstance().getScheduler().asyncLater(() -> {
|
||||
Object removePacket = createRemovePacket();
|
||||
for (CNPlayer viewer : viewers) {
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, removePacket);
|
||||
}
|
||||
}, manager.disappearDuration() * 50L, TimeUnit.MILLISECONDS);
|
||||
|
||||
List<Object> disappearPacket = createDisappearPacket();
|
||||
for (CNPlayer viewer : viewers) {
|
||||
if (background != null) {
|
||||
owner.untrackPassengers(viewer, entityID, subEntityID);
|
||||
} else {
|
||||
owner.untrackPassengers(viewer, entityID);
|
||||
}
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, disappearPacket);
|
||||
}
|
||||
|
||||
this.cachedVisibility.clear();
|
||||
this.isShown = false;
|
||||
this.viewers.clear();
|
||||
resetViewerArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(CNPlayer viewer) {
|
||||
if (!isShown()) return;
|
||||
viewers.remove(viewer);
|
||||
resetViewerArray();
|
||||
if (background != null) {
|
||||
owner.untrackPassengers(viewer, entityID, subEntityID);
|
||||
} else {
|
||||
owner.untrackPassengers(viewer, entityID);
|
||||
}
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, createDisappearPacket());
|
||||
}
|
||||
|
||||
private Object createRemovePacket() {
|
||||
Object packet;
|
||||
if (background != null) {
|
||||
packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID, subEntityID);
|
||||
} else {
|
||||
packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
|
||||
}
|
||||
return packet;
|
||||
}
|
||||
|
||||
private List<Object> createDisappearPacket() {
|
||||
Consumer<List<Object>> modifier0 = CustomNameplates.getInstance().getPlatform().createInterpolationDelayModifier(-1);
|
||||
Consumer<List<Object>> modifier1 = CustomNameplates.getInstance().getPlatform().createTransformationInterpolationDurationModifier(manager.disappearDuration());
|
||||
Consumer<List<Object>> modifier2 = CustomNameplates.getInstance().getPlatform().createScaleModifier(new Vector3(0.001,0.001,0.001));
|
||||
if (background != null) {
|
||||
return List.of(CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2)), CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(subEntityID, List.of(modifier0, modifier1, modifier2)));
|
||||
} else {
|
||||
return List.of(CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(modifier0, modifier1, modifier2)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTextHeight(CNPlayer viewer) {
|
||||
return -Double.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3 scale(CNPlayer viewer) {
|
||||
return new Vector3(1,1,1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3 translation(CNPlayer viewer) {
|
||||
return new Vector3(0, manager.verticalOffset() + maxY(viewer), 0);
|
||||
}
|
||||
|
||||
public double maxY(CNPlayer viewer) {
|
||||
double y = 0;
|
||||
for (Tag tag : renderer.tags()) {
|
||||
if (tag.isShown() && tag.isShown(viewer) && !tag.id().equals(id())) {
|
||||
double currentY = tag.translation(viewer).y() + tag.getTextHeight(viewer);
|
||||
if (currentY > y) {
|
||||
y = currentY;
|
||||
}
|
||||
}
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return "bubble";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectedByCrouching() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectedByScaling() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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.backend.feature.chat;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ChatListener;
|
||||
import net.momirealms.customnameplates.api.feature.chat.AbstractChatMessageProvider;
|
||||
import net.momirealms.customnameplates.api.feature.chat.ChatManager;
|
||||
import net.momirealms.customnameplates.api.feature.chat.ChatMessageProvider;
|
||||
import net.momirealms.customnameplates.api.feature.chat.emoji.EmojiProvider;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbstractChatManager implements ChatManager {
|
||||
|
||||
protected final CustomNameplates plugin;
|
||||
protected final List<EmojiProvider> emojiProviders = new ArrayList<>();
|
||||
protected final List<ChatListener> listeners = new ArrayList<>();
|
||||
protected ChatMessageProvider chatProvider;
|
||||
protected ChatMessageProvider customProvider;
|
||||
|
||||
public AbstractChatManager(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.emojiProviders.clear();
|
||||
if (chatProvider instanceof AbstractChatMessageProvider chatMessageProvider) {
|
||||
chatMessageProvider.unregister();
|
||||
}
|
||||
chatProvider = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
setUpPlatformEmojiProviders();
|
||||
if (customProvider == null) {
|
||||
setUpPlatformProvider();
|
||||
} else {
|
||||
chatProvider = customProvider;
|
||||
}
|
||||
if (chatProvider instanceof AbstractChatMessageProvider chatMessageProvider) {
|
||||
chatMessageProvider.register();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void setUpPlatformProvider();
|
||||
|
||||
protected abstract void setUpPlatformEmojiProviders();
|
||||
|
||||
@Override
|
||||
public boolean setCustomChatProvider(ChatMessageProvider provider) {
|
||||
if (this.customProvider != null)
|
||||
return false;
|
||||
this.customProvider = provider;
|
||||
this.reload();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeCustomChatProvider() {
|
||||
if (this.customProvider != null) {
|
||||
this.customProvider = null;
|
||||
this.reload();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
unload();
|
||||
this.listeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerListener(final ChatListener listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener(final ChatListener listener) {
|
||||
this.listeners.remove(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatMessageProvider chatProvider() {
|
||||
return chatProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChat(CNPlayer player, String message, String channel) {
|
||||
String text = message;
|
||||
for (EmojiProvider provider : emojiProviders) {
|
||||
text = provider.replace(player, text);
|
||||
}
|
||||
text = AdventureHelper.legacyToMiniMessage(text);
|
||||
for (ChatListener listener : listeners) {
|
||||
listener.onPlayerChat(player, text, channel);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* 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.backend.feature.image;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
|
||||
import net.momirealms.customnameplates.api.feature.image.Image;
|
||||
import net.momirealms.customnameplates.api.feature.image.ImageManager;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
public class ImageManagerImpl implements ImageManager {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final HashMap<String, Image> images = new HashMap<>();
|
||||
|
||||
public ImageManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.images.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.loadConfigs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Image imageById(String id) {
|
||||
return images.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Image> images() {
|
||||
return new HashSet<>(images.values());
|
||||
}
|
||||
|
||||
private void loadConfigs() {
|
||||
File imageFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "images");
|
||||
if (!imageFolder.exists() && imageFolder.mkdirs()) {
|
||||
saveDefaultImages();
|
||||
}
|
||||
List<File> configFiles = ConfigUtils.getConfigsDeeply(imageFolder);
|
||||
for (File configFile : configFiles) {
|
||||
YamlDocument config = plugin.getConfigManager().loadData(configFile);
|
||||
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
|
||||
Image image = Image.builder()
|
||||
.id(id)
|
||||
.hasShadow(!config.getBoolean("shadow.remove", false))
|
||||
.opacity(config.getInt("shadow.opacity", 254))
|
||||
.character(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("image") + ".png"),
|
||||
config.getInt("ascent", 8),
|
||||
config.getInt("height", 10)
|
||||
))
|
||||
.build();
|
||||
this.images.put(id, image);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDefaultImages() {
|
||||
String[] png_list = new String[]{"bell", "bubble", "clock", "coin", "compass", "weather", "stamina_0", "stamina_1", "stamina_2"};
|
||||
String[] part_list = new String[]{".png", ".yml"};
|
||||
for (String name : png_list) {
|
||||
for (String part : part_list) {
|
||||
plugin.getConfigManager().saveResource("contents" + File.separator + "images" + File.separator + name + part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* 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.backend.feature.nameplate;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
|
||||
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.feature.nameplate.NameplateManager;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
|
||||
public class NameplateManagerImpl implements NameplateManager {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final Map<String, Nameplate> nameplates = new HashMap<>();
|
||||
private String defaultNameplateId;
|
||||
private String nameTag;
|
||||
|
||||
public NameplateManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.nameplates.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
this.loadConfig();
|
||||
this.loadConfigs();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Nameplate nameplateById(String id) {
|
||||
return this.nameplates.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Nameplate> nameplates() {
|
||||
return new HashSet<>(nameplates.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNameplate(CNPlayer player, String id) {
|
||||
if (!this.nameplates.containsKey(id)) {
|
||||
return false;
|
||||
}
|
||||
return player.hasPermission("nameplates.equip." + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Nameplate> availableNameplates(CNPlayer player) {
|
||||
ArrayList<Nameplate> available = new ArrayList<>();
|
||||
for (Nameplate nameplate : nameplates.values()) {
|
||||
if (player.hasPermission("nameplates.equip." + nameplate.id())) {
|
||||
available.add(nameplate);
|
||||
}
|
||||
}
|
||||
return available;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String defaultNameplateId() {
|
||||
return defaultNameplateId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String playerNameTag() {
|
||||
return nameTag;
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "nameplate.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "nameplate.yml"));
|
||||
defaultNameplateId = document.getString("default-nameplate", "none");
|
||||
String prefix = document.getString("nameplate.prefix", "");
|
||||
String name = document.getString("nameplate.player-name", "%player_name%");
|
||||
String suffix = document.getString("nameplate.suffix", "");
|
||||
nameTag = prefix + name + suffix;
|
||||
}
|
||||
|
||||
private void loadConfigs() {
|
||||
File nameplateFolder = new File(plugin.getDataDirectory().toFile(), "contents" + File.separator + "nameplates");
|
||||
if (!nameplateFolder.exists() && nameplateFolder.mkdirs()) {
|
||||
saveDefaultNameplates();
|
||||
}
|
||||
List<File> configFiles = ConfigUtils.getConfigsDeeply(nameplateFolder);
|
||||
for (File configFile : configFiles) {
|
||||
YamlDocument config = plugin.getConfigManager().loadData(configFile);
|
||||
String id = configFile.getName().substring(0, configFile.getName().lastIndexOf("."));
|
||||
Nameplate nameplate = Nameplate.builder()
|
||||
.id(id)
|
||||
.displayName(config.getString("display-name", id))
|
||||
.left(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
|
||||
config.getInt("left.ascent", 12),
|
||||
config.getInt("left.height", 16)
|
||||
))
|
||||
.middle(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("middle.image") + ".png"),
|
||||
config.getInt("middle.ascent", 12),
|
||||
config.getInt("middle.height", 16)
|
||||
))
|
||||
.right(ConfiguredCharacter.create(
|
||||
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("right.image") + ".png"),
|
||||
config.getInt("right.ascent", 12),
|
||||
config.getInt("right.height", 16)
|
||||
))
|
||||
.build();
|
||||
this.nameplates.put(id, nameplate);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveDefaultNameplates() {
|
||||
String[] png_list = new String[]{"cat", "egg", "cheems", "wither", "xmas", "halloween", "hutao", "starsky", "trident", "rabbit"};
|
||||
String[] part_list = new String[]{"_left.png", "_middle.png", "_right.png", ".yml"};
|
||||
for (String name : png_list) {
|
||||
for (String part : part_list) {
|
||||
plugin.getConfigManager().saveResource("contents" + File.separator + "nameplates" + File.separator + name + part);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
/*
|
||||
* 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.backend.feature.pack;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
|
||||
import net.momirealms.customnameplates.api.feature.OffsetFont;
|
||||
import net.momirealms.customnameplates.api.feature.advance.CharacterFontAdvanceData;
|
||||
import net.momirealms.customnameplates.api.feature.background.Background;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
|
||||
import net.momirealms.customnameplates.api.feature.image.Image;
|
||||
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.feature.pack.ResourcePackManager;
|
||||
import net.momirealms.customnameplates.api.helper.VersionHelper;
|
||||
import net.momirealms.customnameplates.api.util.CharacterUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
|
||||
public class ResourcePackManagerImpl implements ResourcePackManager {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
|
||||
public ResourcePackManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate() {
|
||||
File resourcePackFolder = new File(plugin.getDataFolder() + File.separator + "ResourcePack");
|
||||
// delete the old one
|
||||
this.deleteDirectory(resourcePackFolder);
|
||||
|
||||
// create folders
|
||||
File fontFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "assets" + File.separator + ConfigManager.namespace() + File.separatorChar + "font");
|
||||
File texturesFolder = new File(plugin.getDataFolder(), "ResourcePack" + File.separator+ "assets" + File.separator + ConfigManager.namespace() + File.separatorChar + "textures");
|
||||
if (!fontFolder.mkdirs() || !texturesFolder.mkdirs()) {
|
||||
plugin.getPluginLogger().severe("Failed to generate resource pack folders");
|
||||
return;
|
||||
}
|
||||
|
||||
// save BossBars
|
||||
this.saveBossBar();
|
||||
// save unicodes
|
||||
this.saveLegacyUnicodes();
|
||||
|
||||
if (!VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
this.generateShaders("ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, false);
|
||||
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
|
||||
} else {
|
||||
this.generateShaders("ResourcePack" + File.separator + "overlay_1_20_5" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
|
||||
try {
|
||||
FileUtils.copyDirectory(
|
||||
new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"),
|
||||
new File(plugin.getDataFolder(), "ResourcePack")
|
||||
);
|
||||
FileUtils.deleteDirectory(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_20_5"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// create json object
|
||||
JsonObject fontJson = new JsonObject();
|
||||
JsonArray providers = new JsonArray();
|
||||
fontJson.add("providers", providers);
|
||||
// add offset characters
|
||||
this.getOffsets(texturesFolder).forEach(providers::add);
|
||||
// add nameplate characters
|
||||
this.getNameplates(texturesFolder).forEach(providers::add);
|
||||
// add bubble characters
|
||||
this.getBubbles(texturesFolder).forEach(providers::add);
|
||||
// add background characters
|
||||
this.getBackgrounds(texturesFolder).forEach(providers::add);
|
||||
// add image characters
|
||||
this.getImages(texturesFolder).forEach(providers::add);
|
||||
// save json object to file
|
||||
this.saveFont(fontJson);
|
||||
// generate shift fonts
|
||||
this.generateFont();
|
||||
// set pack.mcmeta/pack.png
|
||||
this.setPackFormat();
|
||||
// copy the resource pack to hooked plugins
|
||||
this.copyResourcePackToHookedPlugins(resourcePackFolder);
|
||||
}
|
||||
|
||||
private void saveFont(JsonObject fontJson) {
|
||||
try (FileWriter fileWriter = new FileWriter(
|
||||
plugin.getDataFolder() +
|
||||
File.separator + "ResourcePack" +
|
||||
File.separator + "assets" +
|
||||
File.separator + ConfigManager.namespace() +
|
||||
File.separator + "font" +
|
||||
File.separator + ConfigManager.font() + ".json")
|
||||
) {
|
||||
fileWriter.write(fontJson.toString().replace("\\\\", "\\"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void generateFont() {
|
||||
YamlDocument document = ConfigManager.getMainConfig();
|
||||
Section section = document.getSection("other-settings.shift-fonts");
|
||||
if (section != null) {
|
||||
for (Object key : section.getKeys()) {
|
||||
if (key instanceof String font) {
|
||||
JsonObject jo = new JsonObject();
|
||||
JsonArray providers = new JsonArray();
|
||||
jo.add("providers", providers);
|
||||
List<String> order = section.getStringList(font);
|
||||
for (String f : order) {
|
||||
String[] split = f.split(":", 2);
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
if (split.length == 2) {
|
||||
properties.put("shift_y", Integer.parseInt(split[1]));
|
||||
}
|
||||
CharacterFontAdvanceData data = plugin.getAdvanceManager().templateFontDataById(split[0]);
|
||||
if (data == null) {
|
||||
plugin.getPluginLogger().warn("Font template [" + split[0] + "] not found");
|
||||
continue;
|
||||
}
|
||||
List<JsonObject> jsonObject = data.fontProvider(properties);
|
||||
if (jsonObject == null) {
|
||||
plugin.getPluginLogger().warn("Font template [" + split[0] + "] doesn't support shift");
|
||||
continue;
|
||||
}
|
||||
for (JsonObject o : jsonObject) {
|
||||
providers.add(o);
|
||||
}
|
||||
}
|
||||
|
||||
try (FileWriter file = new FileWriter(new File(plugin.getDataFolder(),
|
||||
"ResourcePack" +
|
||||
File.separator + "assets" +
|
||||
File.separator + ConfigManager.namespace() +
|
||||
File.separator + "font" +
|
||||
File.separator + font + ".json"))) {
|
||||
file.write(jo.toString().replace("\\\\", "\\"));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private void setPackFormat() {
|
||||
if (VersionHelper.isVersionNewerThan1_20_5()) {
|
||||
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack_1_20_5.mcmeta");
|
||||
File file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack_1_20_5.mcmeta");
|
||||
file.renameTo(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta"));
|
||||
} else {
|
||||
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack.mcmeta");
|
||||
}
|
||||
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack.png");
|
||||
}
|
||||
|
||||
private void copyResourcePackToHookedPlugins(File resourcePackFolder) {
|
||||
File pluginsFolder = plugin.getDataFolder().getParentFile();
|
||||
if (ConfigManager.packItemsAdder()) {
|
||||
File file = new File(pluginsFolder, "ItemsAdder" + File.separator + "config.yml");
|
||||
YamlDocument iaConfig = plugin.getConfigManager().loadData(file);
|
||||
List<String> folders = iaConfig.getStringList("resource-pack.zip.merge_other_plugins_resourcepacks_folders");
|
||||
boolean changed = false;
|
||||
if (!folders.contains("CustomNameplates/ResourcePack")) {
|
||||
folders.add("CustomNameplates/ResourcePack");
|
||||
iaConfig.set("resource-pack.zip.merge_other_plugins_resourcepacks_folders", folders);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) {
|
||||
try {
|
||||
iaConfig.save(file);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ConfigManager.packItemsAdderLegacy()){
|
||||
try {
|
||||
FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(pluginsFolder, "ItemsAdder" + File.separator + "contents" + File.separator + "nameplates" + File.separator + "resourcepack" + File.separator + "assets") );
|
||||
} catch (IOException e){
|
||||
plugin.getPluginLogger().warn("Failed to copy files to ItemsAdder", e);
|
||||
}
|
||||
}
|
||||
if (ConfigManager.packOraxen()){
|
||||
try {
|
||||
FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(pluginsFolder, "Oraxen" + File.separator + "pack" + File.separator + "assets"));
|
||||
} catch (IOException e){
|
||||
plugin.getPluginLogger().warn("Failed to copy files to Oraxen", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<JsonObject> getBubbles(File texturesFolder) {
|
||||
ArrayList<JsonObject> list = new ArrayList<>();
|
||||
if (!ConfigManager.bubbleModule()) return list;
|
||||
for (Bubble bubble : plugin.getBubbleManager().bubbles()) {
|
||||
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{bubble.left(), bubble.middle(), bubble.right(), bubble.tail()}) {
|
||||
JsonObject jo = new JsonObject();
|
||||
jo.add("type", new JsonPrimitive("bitmap"));
|
||||
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.bubblePath().replace("\\", "/") + configuredChar.imageFile().getName()));
|
||||
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
|
||||
jo.add("height", new JsonPrimitive(configuredChar.height()));
|
||||
JsonArray ja = new JsonArray();
|
||||
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
|
||||
jo.add("chars", ja);
|
||||
list.add(jo);
|
||||
try {
|
||||
FileUtils.copyFile(
|
||||
new File(plugin.getDataFolder(),
|
||||
"contents" + File.separator + "bubbles" + File.separator + configuredChar.imageFile().getName()),
|
||||
new File(texturesFolder,
|
||||
ConfigManager.bubblePath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<JsonObject> getBackgrounds(File texturesFolder) {
|
||||
ArrayList<JsonObject> list = new ArrayList<>();
|
||||
if (!ConfigManager.backgroundModule()) return list;
|
||||
for (Background backGround : plugin.getBackgroundManager().getBackgrounds()) {
|
||||
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{
|
||||
backGround.left(), backGround.width_1(),
|
||||
backGround.width_2(), backGround.width_4(),
|
||||
backGround.width_8(), backGround.width_16(),
|
||||
backGround.width_32(), backGround.width_64(),
|
||||
backGround.width_128(), backGround.right()}
|
||||
) {
|
||||
JsonObject jo = new JsonObject();
|
||||
jo.add("type", new JsonPrimitive("bitmap"));
|
||||
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.backgroundPath().replace("\\", "/") + configuredChar.imageFile().getName()));
|
||||
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
|
||||
jo.add("height", new JsonPrimitive(configuredChar.height()));
|
||||
JsonArray ja = new JsonArray();
|
||||
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
|
||||
jo.add("chars", ja);
|
||||
list.add(jo);
|
||||
try {
|
||||
FileUtils.copyFile(
|
||||
new File(plugin.getDataFolder(),
|
||||
"contents" + File.separator + "backgrounds" + File.separator + configuredChar.imageFile().getName()),
|
||||
new File(texturesFolder,
|
||||
ConfigManager.backgroundPath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<JsonObject> getImages(File texturesFolder) {
|
||||
ArrayList<JsonObject> list = new ArrayList<>();
|
||||
if (!ConfigManager.imageModule()) return list;
|
||||
for (Image image : plugin.getImageManager().images()) {
|
||||
ConfiguredCharacter character = image.character();
|
||||
JsonObject jo = new JsonObject();
|
||||
jo.add("type", new JsonPrimitive("bitmap"));
|
||||
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.imagePath().replace("\\", "/") + character.imageFile().getName()));
|
||||
jo.add("ascent", new JsonPrimitive(character.ascent()));
|
||||
jo.add("height", new JsonPrimitive(character.height()));
|
||||
JsonArray ja = new JsonArray();
|
||||
ja.add(CharacterUtils.char2Unicode(character.character()));
|
||||
jo.add("chars", ja);
|
||||
list.add(jo);
|
||||
try {
|
||||
FileUtils.copyFile(
|
||||
new File(plugin.getDataFolder(),
|
||||
"contents" + File.separator + "images" + File.separator + character.imageFile().getName()),
|
||||
new File(texturesFolder,
|
||||
ConfigManager.imagePath().replace("\\", File.separator) + character.imageFile().getName()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<JsonObject> getNameplates(File texturesFolder) {
|
||||
ArrayList<JsonObject> list = new ArrayList<>();
|
||||
if (!ConfigManager.nameplateModule()) return list;
|
||||
for (Nameplate nameplate : plugin.getNameplateManager().nameplates()) {
|
||||
for (ConfiguredCharacter configuredChar : new ConfiguredCharacter[]{nameplate.left(), nameplate.middle(), nameplate.right()}) {
|
||||
JsonObject jo = new JsonObject();
|
||||
jo.add("type", new JsonPrimitive("bitmap"));
|
||||
jo.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.nameplatePath().replace("\\", "/") + configuredChar.imageFile().getName()));
|
||||
jo.add("ascent", new JsonPrimitive(configuredChar.ascent()));
|
||||
jo.add("height", new JsonPrimitive(configuredChar.height()));
|
||||
JsonArray ja = new JsonArray();
|
||||
ja.add(CharacterUtils.char2Unicode(configuredChar.character()));
|
||||
jo.add("chars", ja);
|
||||
list.add(jo);
|
||||
try {
|
||||
FileUtils.copyFile(
|
||||
new File(plugin.getDataFolder(),
|
||||
"contents" + File.separator + "nameplates" + File.separator + configuredChar.imageFile().getName()),
|
||||
new File(texturesFolder,
|
||||
ConfigManager.nameplatePath().replace("\\", File.separator) + configuredChar.imageFile().getName()));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<JsonObject> getOffsets(File texturesFolder) {
|
||||
this.saveSplit(texturesFolder);
|
||||
ArrayList<JsonObject> list = new ArrayList<>();
|
||||
for (OffsetFont offsetFont : OffsetFont.values()) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.add("type", new JsonPrimitive("bitmap"));
|
||||
jsonObject.add("file", new JsonPrimitive(ConfigManager.namespace() + ":" + ConfigManager.spaceSplitPath().replace("\\","/") + "space_split.png"));
|
||||
jsonObject.add("ascent", new JsonPrimitive(-5000));
|
||||
jsonObject.add("height", new JsonPrimitive(offsetFont.height()));
|
||||
final JsonArray jsonArray = new JsonArray();
|
||||
jsonArray.add(CharacterUtils.char2Unicode(offsetFont.character()));
|
||||
jsonObject.add("chars", jsonArray);
|
||||
list.add(jsonObject);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
private void saveSplit(File texturesFolder) {
|
||||
try {
|
||||
plugin.getConfigManager().saveResource("space_split.png");
|
||||
FileUtils.copyFile(new File(plugin.getDataFolder(),"space_split.png"), new File(texturesFolder, ConfigManager.spaceSplitPath().replace("\\", File.separator) + "space_split.png"));
|
||||
File file = new File(plugin.getDataFolder(),"space_split.png");
|
||||
if (file.exists()) {
|
||||
file.delete();
|
||||
}
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void deleteDirectory(File file){
|
||||
if (file.exists()) {
|
||||
try {
|
||||
FileUtils.deleteDirectory(file);
|
||||
} catch (IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveBossBar() {
|
||||
if (ConfigManager.bossBar1_20_2()) {
|
||||
String color = ConfigManager.removedBarColor().name().toLowerCase(Locale.ENGLISH);
|
||||
String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "gui" + File.separator + "sprites" + File.separator + "boss_bar" + File.separator;
|
||||
plugin.getConfigManager().saveResource(path + color + "_background.png");
|
||||
plugin.getConfigManager().saveResource(path + color + "_progress.png");
|
||||
}
|
||||
if (ConfigManager.bossBar1_17()) {
|
||||
String path = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "gui" + File.separator + "bars.png";
|
||||
plugin.getConfigManager().saveResource(path);
|
||||
try {
|
||||
File inputFile = new File(plugin.getDataFolder(), path);
|
||||
BufferedImage image = ImageIO.read(inputFile);
|
||||
int y;
|
||||
switch (ConfigManager.removedBarColor()) {
|
||||
case PINK -> y = 0;
|
||||
case BLUE -> y = 10;
|
||||
case RED -> y = 20;
|
||||
case GREEN -> y = 30;
|
||||
case PURPLE -> y = 50;
|
||||
case WHITE -> y = 60;
|
||||
default -> y = 40;
|
||||
}
|
||||
int width = 182;
|
||||
int height = 10;
|
||||
for (int i = 0; i < width; i++) {
|
||||
for (int j = y; j < y + height; j++) {
|
||||
image.setRGB(i, j, 0);
|
||||
}
|
||||
}
|
||||
ImageIO.write(image, "png", inputFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLegacyUnicodes() {
|
||||
if (ConfigManager.legacyUnicodes()) {
|
||||
for (int i = 0; i < 256; i++) {
|
||||
var path = "font" + File.separator + "unicode_page_" + String.format("%02x", i) + ".png";
|
||||
var destination = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "font" + File.separator + "unicode_page_" + String.format("%02x", i) + ".png";
|
||||
File imageFile = new File(plugin.getDataFolder(), path);
|
||||
File destinationFile = new File(plugin.getDataFolder(), destination);
|
||||
if (imageFile.exists()) {
|
||||
try {
|
||||
FileUtils.copyFile(imageFile, destinationFile);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void generateShaders(String path, boolean v1_20_5) {
|
||||
if (!ConfigManager.enableShader()) return;
|
||||
|
||||
plugin.getConfigManager().saveResource(path + "rendertype_text.fsh");
|
||||
plugin.getConfigManager().saveResource(path + "rendertype_text.json");
|
||||
plugin.getConfigManager().saveResource(path + "rendertype_text.vsh");
|
||||
|
||||
String line;
|
||||
StringBuilder sb1 = new StringBuilder();
|
||||
File shader1 = new File(plugin.getDataFolder(), path + "rendertype_text.vsh");
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(shader1), StandardCharsets.UTF_8))) {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb1.append(line).append(System.lineSeparator());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
String mainShader = v1_20_5 ? ShaderConstants.Nameplates_Shader_1_20_5 : ShaderConstants.Nameplates_Shader_1_20_4;
|
||||
try (BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(shader1), StandardCharsets.UTF_8))) {
|
||||
writer.write(sb1.toString()
|
||||
.replace("%SHADER_0%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_Out)
|
||||
.replace("%SHADER_1%", !ConfigManager.itemsAdderEffect() ? mainShader : ShaderConstants.ItemsAdder_Text_Effects + mainShader)
|
||||
.replace("%SHADER_2%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_VSH)
|
||||
.replace("%SHADER_3%", !ConfigManager.hideScoreBoardNumber() ? "" : ShaderConstants.Hide_ScoreBoard_Numbers)
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
File shader2 = new File(plugin.getDataFolder(), path + "rendertype_text.fsh");
|
||||
StringBuilder sb2 = new StringBuilder();
|
||||
try (BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(shader2), StandardCharsets.UTF_8))) {
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb2.append(line).append(System.lineSeparator());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
try (BufferedWriter writer = new BufferedWriter(
|
||||
new OutputStreamWriter(new FileOutputStream(shader2), StandardCharsets.UTF_8))) {
|
||||
writer.write(sb2.toString()
|
||||
.replace("%SHADER_0%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_In)
|
||||
.replace("%SHADER_1%", !ConfigManager.animatedText() ? "" : ShaderConstants.Animated_Text_FSH)
|
||||
);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ShaderConstants {
|
||||
|
||||
public static final String Nameplates_Shader_1_20_5 =
|
||||
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.y += 1;\n" +
|
||||
" vertex.x += 1;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else if (Color.xyz == vec3(254., 254., 254.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.z *= 1.001;\n" +
|
||||
" vertex.x *= 1.001;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else if (Color.xyz == vec3(253., 254., 254.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.z *= 1.001001;\n" +
|
||||
" vertex.x *= 1.001001;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" }";
|
||||
|
||||
public static final String Nameplates_Shader_1_20_4 =
|
||||
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.y += 1;\n" +
|
||||
" vertex.x += 1;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else if (Color.xyz == vec3(254., 254., 254.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.z -= 0.001;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else if (Color.xyz == vec3(253., 254., 254.) / 255.) {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" vertex.z -= 0.0011;\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else {\n" +
|
||||
" vertexColor = Color*texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" }";
|
||||
|
||||
public static final String ItemsAdder_Text_Effects =
|
||||
"if (Color.xyz == vec3(255., 255., 254.) / 255.) {\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" } else if (Color.xyz == vec3(255., 255., 253.) / 255.) {\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" gl_Position.y = gl_Position.y + sin(GameTime * 12000. + (gl_Position.x * 6)) / 150.;\n" +
|
||||
" } else if (Color.xyz == vec3(255., 255., 252.) / 255.) {\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" gl_Position.y = gl_Position.y + sin(GameTime*12000. + (gl_Position.x*6)) / 150.;\n" +
|
||||
" } else if (Color.xyz == vec3(255., 255., 251.) / 255.) {\n" +
|
||||
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" float vertexId = mod(gl_VertexID, 4.0);\n" +
|
||||
" if (vertex.z <= 0.) {\n" +
|
||||
" if (vertexId == 3. || vertexId == 0.) vertex.y += cos(GameTime * 12000. / 4) * 0.1;\n" +
|
||||
" vertex.y += max(cos(GameTime*12000. / 4) * 0.1, 0.);\n" +
|
||||
" } else {\n" +
|
||||
" if (vertexId == 3. || vertexId == 0.) vertex.y -= cos(GameTime * 12000. / 4) * 3;\n" +
|
||||
" vertex.y -= max(cos(GameTime*12000. / 4) * 4, 0.);\n" +
|
||||
" }\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else if (Color.xyz == vec3(255., 254., 254.) / 255.) {\n" +
|
||||
" float vertexId = mod(gl_VertexID, 4.0);\n" +
|
||||
" if (vertex.z <= 0.) {\n" +
|
||||
" if (vertexId == 3. || vertexId == 0.) vertex.y += cos(GameTime * 12000. / 4) * 0.1;\n" +
|
||||
" vertex.y += max(cos(GameTime*12000. / 4) * 0.1, 0.);\n" +
|
||||
" } else {\n" +
|
||||
" if (vertexId == 3. || vertexId == 0.) vertex.y -= cos(GameTime * 12000. / 4) * 3;\n" +
|
||||
" vertex.y -= max(cos(GameTime*12000. / 4) * 4, 0.);\n" +
|
||||
" }\n" +
|
||||
" vertexColor = ((.6 + .6 * cos(6. * (gl_Position.x + GameTime * 1000.) + vec4(0, 23, 21, 1))) + vec4(0., 0., 0., 1.)) * texelFetch(Sampler2, UV2 / 16, 0);\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vertex;\n" +
|
||||
" } else ";
|
||||
public static final String Hide_ScoreBoard_Numbers =
|
||||
"\n" +
|
||||
" if (Position.z == 0.0\n" +
|
||||
" && gl_Position.x >= 0.94\n" +
|
||||
" && gl_Position.y >= -0.35\n" +
|
||||
" && vertexColor.g == 84.0/255.0\n" +
|
||||
" && vertexColor.g == 84.0/255.0\n" +
|
||||
" && vertexColor.r == 252.0/255.0\n" +
|
||||
" && gl_VertexID <= 7\n" +
|
||||
" ) {\n" +
|
||||
" gl_Position = ProjMat * ModelViewMat * vec4(ScreenSize + 100.0, 0.0, ScreenSize + 100.0);\n" +
|
||||
" }";
|
||||
|
||||
public static final String Animated_Text_FSH =
|
||||
"\n" +
|
||||
" vec2 p1 = round(pos1 / (posID == 0 ? 1 - coord.x : 1 - coord.y));\n" +
|
||||
" vec2 p2 = round(pos2 / (posID == 0 ? coord.y : coord.x));\n" +
|
||||
" ivec2 resolution = ivec2(abs(p1 - p2));\n" +
|
||||
" ivec2 corner = ivec2(min(p1, p2));\n" +
|
||||
" vec4 pixel = texture(Sampler0, corner / 256.0) * 255;\n" +
|
||||
" if (pixel.a == 1) {\n" +
|
||||
" ivec2 frames = ivec2(resolution / pixel.gb);\n" +
|
||||
" vec2 uv = (texCoord0 * 256 - corner) / frames.x;\n" +
|
||||
" if (uv.x > pixel.y || uv.y > pixel.z)\n" +
|
||||
" discard;\n" +
|
||||
" int time = int(GameTime * pixel.r * 10 * pixel.x) % int(frames.x * frames.y);\n" +
|
||||
" uv = corner + mod(uv, pixel.yz) + vec2(time % frames.x, time / frames.x % frames.y) * pixel.yz;\n" +
|
||||
" color = texture(Sampler0, uv / 256.0) * vertexColor * ColorModulator;\n" +
|
||||
" }";
|
||||
|
||||
public static final String Animated_Text_VSH =
|
||||
"\n" +
|
||||
" pos1 = pos2 = vec2(0);\n" +
|
||||
" posID = gl_VertexID % 4;\n" +
|
||||
" const vec2[4] corners = vec2[4](vec2(0), vec2(0, 1), vec2(1), vec2(1, 0));\n" +
|
||||
" coord = corners[posID];\n" +
|
||||
" if (posID == 0) pos1 = UV0 * 256;\n" +
|
||||
" if (posID == 2) pos2 = UV0 * 256;";
|
||||
|
||||
public static final String Animated_Text_Out =
|
||||
"\n" +
|
||||
"out vec2 pos1;\n" +
|
||||
"out vec2 pos2;\n" +
|
||||
"out vec2 coord;\n" +
|
||||
"flat out int posID;\n";
|
||||
|
||||
public static final String Animated_Text_In =
|
||||
"\n" +
|
||||
"in vec2 pos1;\n" +
|
||||
"in vec2 pos2;\n" +
|
||||
"in vec2 coord;\n" +
|
||||
"flat in int posID;\n";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* 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.backend.feature.tag;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.DynamicText;
|
||||
import net.momirealms.customnameplates.api.feature.RelationalFeature;
|
||||
import net.momirealms.customnameplates.api.feature.tag.AbstractTag;
|
||||
import net.momirealms.customnameplates.api.feature.tag.NameTagConfig;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
import net.momirealms.customnameplates.api.placeholder.Placeholder;
|
||||
import net.momirealms.customnameplates.api.util.Vector3;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class NameTag extends AbstractTag implements RelationalFeature {
|
||||
|
||||
private final NameTagConfig config;
|
||||
private int order;
|
||||
private int timeLeft;
|
||||
private DynamicText currentText;
|
||||
|
||||
public NameTag(CNPlayer owner, NameTagConfig config, TagRenderer renderer) {
|
||||
super(owner, renderer);
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
order = config.carouselTexts().length - 1;
|
||||
timeLeft = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Object> spawnPacket(CNPlayer viewer) {
|
||||
String newName = currentText.render(viewer);
|
||||
Object component = AdventureHelper.miniMessageToMinecraftComponent(newName);
|
||||
Tracker tracker = owner.getTracker(viewer);
|
||||
return CustomNameplates.getInstance().getPlatform().createTextDisplayPacket(
|
||||
entityID, uuid,
|
||||
owner.position().add(0,(1.8 + (affectedByCrouching() && tracker.isCrouching() && !owner.isFlying() ? -0.3 : 0) + renderer.hatOffset()) * (affectedByScaling() ? tracker.getScale() : 1),0),
|
||||
0f, 0f, 0d,
|
||||
0, 0, 0,
|
||||
component, config.backgroundColor(), config.opacity(), config.hasShadow(), config.isSeeThrough(), config.useDefaultBackgroundColor(),
|
||||
config.alignment(), config.viewRange(), config.shadowRadius(), config.shadowStrength(),
|
||||
(affectedByScaling() ? scale(viewer).multiply(tracker.getScale()) : scale(viewer)),
|
||||
(affectedByScaling() ? translation(viewer).multiply(tracker.getScale()) : translation(viewer)),
|
||||
config.lineWidth(),
|
||||
(affectedByCrouching() && tracker.isCrouching())
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (timeLeft > 0)
|
||||
timeLeft--;
|
||||
|
||||
if (timeLeft == 0) {
|
||||
int triedTimes = 0;
|
||||
|
||||
do {
|
||||
if (triedTimes == config.carouselTexts().length) {
|
||||
timeLeft = 20;
|
||||
currentText = null;
|
||||
CustomNameplates.getInstance().getPluginLogger().warn("No text in order is available for player " + owner.name() + ". Please check your tag's conditions.");
|
||||
return;
|
||||
}
|
||||
order++;
|
||||
if (order >= config.carouselTexts().length) {
|
||||
order = 0;
|
||||
}
|
||||
triedTimes++;
|
||||
} while (
|
||||
!owner.isMet(config.carouselTexts()[order].requirements())
|
||||
);
|
||||
|
||||
CarouselText carouselText = config.carouselTexts()[order];
|
||||
timeLeft = carouselText.duration();
|
||||
currentText = carouselText.preParsedDynamicText().fastCreate(owner);
|
||||
|
||||
if (carouselText.updateOnDisplay()) {
|
||||
owner.forceUpdate(currentText.placeholders(), owner.nearbyPlayers());
|
||||
}
|
||||
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPlaceholderUpdates(CNPlayer p1, CNPlayer p2, boolean force) {
|
||||
refresh(p2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPlaceholderUpdates(CNPlayer p1, boolean force) {
|
||||
refresh();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow() {
|
||||
return owner.isMet(config.ownerRequirements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canShow(CNPlayer viewer) {
|
||||
return viewer.isMet(owner, config.viewerRequirements());
|
||||
}
|
||||
|
||||
public void refresh() {
|
||||
for (CNPlayer viewer : viewerArray) {
|
||||
refresh(viewer);
|
||||
}
|
||||
}
|
||||
|
||||
public void refresh(CNPlayer viewer) {
|
||||
String newName = currentText.render(viewer);
|
||||
Object component = AdventureHelper.miniMessageToMinecraftComponent(newName);
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().updateTextDisplayPacket(entityID, List.of(CustomNameplates.getInstance().getPlatform().createTextComponentModifier(component)));
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getTextHeight(CNPlayer viewer) {
|
||||
String current = currentText.render(viewer);
|
||||
Tracker tracker = viewer.getTracker(owner);
|
||||
int lines = CustomNameplates.getInstance().getAdvanceManager().getLines(current, config.lineWidth());
|
||||
return ((lines * (9+1) + config.translation().y()) * config.scale().y() * (config.affectedByScaling() ? tracker.getScale() : 1)) / 40;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide() {
|
||||
if (!isShown()) return;
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
|
||||
for (CNPlayer viewer : viewerArray) {
|
||||
owner.untrackPassengers(viewer, entityID);
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(viewer, packet);
|
||||
}
|
||||
this.isShown = false;
|
||||
this.viewers.clear();
|
||||
resetViewerArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3 scale(CNPlayer viewer) {
|
||||
return config.scale();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vector3 translation(CNPlayer viewer) {
|
||||
return config.translation().add(0, renderer.hatOffset(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "UnlimitedTag";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> activePlaceholders() {
|
||||
if (currentText == null || !isShown()) return Collections.emptySet();
|
||||
return currentText.placeholders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Placeholder> allPlaceholders() {
|
||||
HashSet<Placeholder> placeholders = new HashSet<>();
|
||||
for (CarouselText text : config.carouselTexts()) {
|
||||
placeholders.addAll(text.preParsedDynamicText().placeholders());
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte opacity() {
|
||||
return config.opacity();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String id() {
|
||||
return "nametag";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectedByCrouching() {
|
||||
return config.affectedByScaling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectedByScaling() {
|
||||
return affectedByCrouching();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* 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.backend.feature.tag;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.Feature;
|
||||
import net.momirealms.customnameplates.api.feature.tag.NameTagConfig;
|
||||
import net.momirealms.customnameplates.api.feature.tag.Tag;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||
import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class TagRendererImpl implements TagRenderer {
|
||||
|
||||
private final CNPlayer owner;
|
||||
private final UnlimitedTagManager manager;
|
||||
private Tag[] tags;
|
||||
private double hatOffset;
|
||||
|
||||
public TagRendererImpl(UnlimitedTagManager manager, CNPlayer owner) {
|
||||
this.owner = owner;
|
||||
this.manager = manager;
|
||||
List<NameTag> senderList = new ArrayList<>();
|
||||
for (NameTagConfig config : manager.nameTagConfigs()) {
|
||||
NameTag sender = new NameTag(owner, config, this);
|
||||
senderList.add(sender);
|
||||
this.owner.addFeature(sender);
|
||||
}
|
||||
this.tags = senderList.toArray(new Tag[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double hatOffset() {
|
||||
return hatOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hatOffset(double hatOffset) {
|
||||
if (hatOffset != this.hatOffset) {
|
||||
this.hatOffset = hatOffset;
|
||||
for (Tag tag : tags) {
|
||||
tag.updateTranslation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
HashSet<CNPlayer> playersToUpdatePassengers = new HashSet<>();
|
||||
for (Tag display : tags) {
|
||||
boolean canShow = display.canShow();
|
||||
// 能大众显示
|
||||
if (canShow) {
|
||||
// 当前大众显示
|
||||
if (display.isShown()) {
|
||||
for (CNPlayer nearby : owner.nearbyPlayers()) {
|
||||
// 如果已经展示了
|
||||
if (display.isShown(nearby)) {
|
||||
// 不满足条件就撤掉
|
||||
if (!display.canShow(nearby)) {
|
||||
display.hide(nearby);
|
||||
}
|
||||
} else {
|
||||
// 未展示,则检测条件,可以就上
|
||||
if (display.canShow(nearby)) {
|
||||
display.show(nearby);
|
||||
playersToUpdatePassengers.add(nearby);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 更新一下文字顺序,放在后面是为了防止已经被hide的玩家多收一个包
|
||||
display.tick();
|
||||
} else {
|
||||
// 之前隐藏,现在开始大众显示
|
||||
// 需要重置文字顺序
|
||||
display.init();
|
||||
// 更新一下文字顺序
|
||||
display.tick();
|
||||
display.show();
|
||||
for (CNPlayer nearby : owner.nearbyPlayers()) {
|
||||
if (display.canShow(nearby) && !display.isShown(nearby)) {
|
||||
display.show(nearby);
|
||||
playersToUpdatePassengers.add(nearby);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 不能展示的情况
|
||||
// 如果已经展示了,就咔掉所有玩家
|
||||
if (display.isShown()) {
|
||||
display.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update passengers
|
||||
Set<Integer> realPassengers = owner.passengers();
|
||||
for (CNPlayer nearby : playersToUpdatePassengers) {
|
||||
updatePassengers(nearby, realPassengers);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (Tag tag : this.tags) {
|
||||
tag.hide();
|
||||
if (tag instanceof Feature feature) {
|
||||
this.owner.removeFeature(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerRemove(CNPlayer another) {
|
||||
for (Tag display : this.tags) {
|
||||
if (display.isShown()) {
|
||||
if (display.isShown(another)) {
|
||||
display.hide(another);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTag(Tag tag) {
|
||||
Tag[] newTags = new Tag[this.tags.length + 1];
|
||||
System.arraycopy(this.tags, 0, newTags, 0, this.tags.length);
|
||||
newTags[this.tags.length] = tag;
|
||||
this.tags = newTags;
|
||||
if (tag instanceof Feature feature) {
|
||||
this.owner.addFeature(feature);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag[] tags() {
|
||||
return this.tags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeTagIf(Predicate<Tag> predicate) {
|
||||
Set<Integer> removedIndexes = new HashSet<>();
|
||||
for (int i = 0; i < this.tags.length; i++) {
|
||||
if (predicate.test(this.tags[i])) {
|
||||
removedIndexes.add(i);
|
||||
this.tags[i].hide();
|
||||
if (this.tags[i] instanceof Feature feature) {
|
||||
this.owner.removeFeature(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removedIndexes.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
Tag[] newTags = new Tag[this.tags.length - removedIndexes.size()];
|
||||
int newIndex = 0;
|
||||
for (int i = 0; i < this.tags.length; i++) {
|
||||
if (!removedIndexes.contains(i)) {
|
||||
newTags[newIndex++] = this.tags[i];
|
||||
}
|
||||
}
|
||||
this.tags = newTags;
|
||||
return removedIndexes.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int tagIndex(Tag tag) {
|
||||
for (int i = 0; i < this.tags.length; i++) {
|
||||
if (this.tags[i].equals(tag)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTag(Tag tag, int index) {
|
||||
if (index < 0 || index > this.tags.length) {
|
||||
throw new IndexOutOfBoundsException("Index out of bounds: " + index);
|
||||
}
|
||||
Tag[] newTags = new Tag[this.tags.length + 1];
|
||||
System.arraycopy(this.tags, 0, newTags, 0, index);
|
||||
newTags[index] = tag;
|
||||
System.arraycopy(this.tags, index, newTags, index + 1, this.tags.length - index);
|
||||
this.tags = newTags;
|
||||
tag.show();
|
||||
if (tag instanceof Feature feature) {
|
||||
this.owner.addFeature(feature);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTag(Tag tag) {
|
||||
int i = 0;
|
||||
boolean has = false;
|
||||
for (Tag display : this.tags) {
|
||||
if (display == tag) {
|
||||
has = true;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (has) {
|
||||
Tag[] newTags = new Tag[this.tags.length - 1];
|
||||
System.arraycopy(this.tags, 0, newTags, 0, i);
|
||||
System.arraycopy(this.tags, i + 1, newTags, i, (this.tags.length - i) - 1);
|
||||
this.tags = newTags;
|
||||
tag.hide();
|
||||
if (tag instanceof Feature feature) {
|
||||
this.owner.removeFeature(feature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerAdd(CNPlayer another) {
|
||||
boolean updatePassengers = false;
|
||||
for (Tag display : this.tags) {
|
||||
if (display.isShown()) {
|
||||
if (!display.isShown(another)) {
|
||||
if (display.canShow(another)) {
|
||||
display.show(another);
|
||||
updatePassengers = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updatePassengers) {
|
||||
Set<Integer> realPassengers = owner.passengers();
|
||||
updatePassengers(another, realPassengers);
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePassengers(CNPlayer another, Set<Integer> realPassengers) {
|
||||
Set<Integer> fakePassengers = owner.getTrackedPassengerIds(another);
|
||||
fakePassengers.addAll(realPassengers);
|
||||
int[] passengers = new int[fakePassengers.size()];
|
||||
int index = 0;
|
||||
for (int passenger : fakePassengers) {
|
||||
passengers[index++] = passenger;
|
||||
}
|
||||
Object packet = CustomNameplates.getInstance().getPlatform().setPassengersPacket(owner.entityID(), passengers);
|
||||
CustomNameplates.getInstance().getPacketSender().sendPacket(another, packet);
|
||||
}
|
||||
|
||||
public void handleEntityDataChange(CNPlayer another, boolean isCrouching) {
|
||||
Tracker properties = owner.getTracker(another);
|
||||
// should never be null
|
||||
if (properties == null) return;
|
||||
properties.setCrouching(isCrouching);
|
||||
for (Tag display : this.tags) {
|
||||
if (display.affectedByCrouching()) {
|
||||
if (display.isShown()) {
|
||||
if (display.isShown(another)) {
|
||||
display.onPlayerCrouching(another, isCrouching);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleAttributeChange(CNPlayer another, double scale) {
|
||||
boolean updatePassengers = false;
|
||||
Tracker properties = owner.getTracker(another);
|
||||
// should never be null
|
||||
if (properties == null) return;
|
||||
properties.setScale(scale);
|
||||
for (Tag display : this.tags) {
|
||||
if (display.affectedByScaling()) {
|
||||
if (display.isShown()) {
|
||||
if (display.isShown(another)) {
|
||||
display.onPlayerScaleUpdate(another, scale);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* 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.backend.feature.tag;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.AbstractCNPlayer;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.CarouselText;
|
||||
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
|
||||
import net.momirealms.customnameplates.api.feature.tag.NameTagConfig;
|
||||
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
|
||||
import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager;
|
||||
import net.momirealms.customnameplates.api.helper.VersionHelper;
|
||||
import net.momirealms.customnameplates.api.network.Tracker;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.util.Alignment;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import net.momirealms.customnameplates.api.util.Vector3;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitListener {
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final LinkedHashMap<String, NameTagConfig> configs = new LinkedHashMap<>();
|
||||
private final ConcurrentHashMap<UUID, TagRendererImpl> tagRenderers = new ConcurrentHashMap<>();
|
||||
private NameTagConfig[] configArray = new NameTagConfig[0];
|
||||
private int previewDuration;
|
||||
private boolean alwaysShow;
|
||||
|
||||
public UnlimitedTagManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPreviewing(CNPlayer player, boolean preview) {
|
||||
boolean isPreviewing = player.isPreviewing();
|
||||
if (isPreviewing) {
|
||||
if (preview) return;
|
||||
plugin.getUnlimitedTagManager().onRemovePlayer(player, player);
|
||||
player.removePlayerFromTracker(player);
|
||||
((AbstractCNPlayer) player).setPreviewing(false);
|
||||
} else {
|
||||
if (!preview) return;
|
||||
Tracker tracker = player.addPlayerToTracker(player);
|
||||
tracker.setScale(player.scale());
|
||||
tracker.setCrouching(player.isCrouching());
|
||||
plugin.getUnlimitedTagManager().onAddPlayer(player, player);
|
||||
((AbstractCNPlayer) player).setPreviewing(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int previewDuration() {
|
||||
return previewDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerJoin(CNPlayer player) {
|
||||
TagRendererImpl sender = new TagRendererImpl(this, player);
|
||||
sender.onTick();
|
||||
TagRendererImpl previous = tagRenderers.put(player.uuid(), sender);
|
||||
if (previous != null) {
|
||||
previous.destroy();
|
||||
}
|
||||
setPreviewing(player, isAlwaysShow());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerQuit(CNPlayer player) {
|
||||
TagRendererImpl sender = tagRenderers.remove(player.uuid());
|
||||
if (sender != null) {
|
||||
sender.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if (!ConfigManager.nameplateModule()) return;
|
||||
this.loadConfig();
|
||||
this.resetArray();
|
||||
for (CNPlayer online : plugin.getOnlinePlayers()) {
|
||||
onPlayerJoin(online);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
for (TagRendererImpl sender : tagRenderers.values()) {
|
||||
sender.destroy();
|
||||
}
|
||||
this.tagRenderers.clear();
|
||||
this.configs.clear();
|
||||
this.resetArray();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick() {
|
||||
for (TagRendererImpl sender : tagRenderers.values()) {
|
||||
sender.onTick();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAlwaysShow() {
|
||||
return alwaysShow;
|
||||
}
|
||||
|
||||
private void resetArray() {
|
||||
configArray = configs.values().toArray(new NameTagConfig[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameTagConfig configById(String name) {
|
||||
return configs.get(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameTagConfig[] nameTagConfigs() {
|
||||
return configArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAddPlayer(CNPlayer owner, CNPlayer added) {
|
||||
TagRendererImpl controller = tagRenderers.get(owner.uuid());
|
||||
if (controller != null) {
|
||||
controller.handlePlayerAdd(added);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TagRenderer getTagRender(CNPlayer owner) {
|
||||
return tagRenderers.get(owner.uuid());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemovePlayer(CNPlayer owner, CNPlayer removed) {
|
||||
TagRendererImpl controller = tagRenderers.get(owner.uuid());
|
||||
if (controller != null) {
|
||||
controller.handlePlayerRemove(removed);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerDataSet(CNPlayer owner, CNPlayer viewer, boolean isCrouching) {
|
||||
TagRendererImpl controller = tagRenderers.get(owner.uuid());
|
||||
if (controller != null) {
|
||||
controller.handleEntityDataChange(viewer, isCrouching);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerAttributeSet(CNPlayer owner, CNPlayer viewer, double scale) {
|
||||
TagRendererImpl controller = tagRenderers.get(owner.uuid());
|
||||
if (controller != null) {
|
||||
controller.handleAttributeChange(viewer, scale);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConfig() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "nameplate.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "nameplate.yml"));
|
||||
previewDuration = document.getInt("preview-duration", 5);
|
||||
alwaysShow = document.getBoolean("always-show", false);
|
||||
Section unlimitedSection = document.getSection("unlimited");
|
||||
if (unlimitedSection == null) return;
|
||||
for (Map.Entry<String, Object> entry : unlimitedSection.getStringRouteMappedValues(false).entrySet()) {
|
||||
if (!(entry.getValue() instanceof Section section))
|
||||
return;
|
||||
Vector3 translation = ConfigUtils.vector3(section.getString("translation", "0,0,0"));
|
||||
this.configs.put(entry.getKey(),
|
||||
NameTagConfig.builder()
|
||||
.id(entry.getKey())
|
||||
.ownerRequirement(plugin.getRequirementManager().parseRequirements(section.getSection("owner-conditions")))
|
||||
.viewerRequirement(plugin.getRequirementManager().parseRequirements(section.getSection("viewer-conditions")))
|
||||
.translation(VersionHelper.isVersionNewerThan1_20_2() ? translation : translation.add(0,0.5,0))
|
||||
.scale(ConfigUtils.vector3(section.getString("scale", "1,1,1")))
|
||||
.alignment(Alignment.valueOf(section.getString("alignment", "CENTER")))
|
||||
.viewRange(section.getFloat("view-range", 1f))
|
||||
.shadowRadius(section.getFloat("shadow-radius", 0f))
|
||||
.shadowStrength(section.getFloat("shadow-strength", 1f))
|
||||
.lineWidth(section.getInt("line-width", 200))
|
||||
.hasShadow(section.getBoolean("has-shadow", false))
|
||||
.seeThrough(section.getBoolean("is-see-through", false))
|
||||
.opacity(section.getByte("opacity", (byte) -1))
|
||||
.useDefaultBackgroundColor(section.getBoolean("use-default-background-color", false))
|
||||
.backgroundColor(ConfigUtils.argb(section.getString("background-color", "64,0,0,0")))
|
||||
.affectedByCrouching(section.getBoolean("affected-by-crouching", true))
|
||||
.affectedByScaling(section.getBoolean("affected-by-scale-attribute", true))
|
||||
.carouselText(
|
||||
section.contains("text") ?
|
||||
new CarouselText[]{new CarouselText(-1, new Requirement[0], section.getString("text"), false)} :
|
||||
ConfigUtils.carouselTexts(section.getSection("text-display-order"))
|
||||
)
|
||||
.build()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,708 @@
|
||||
/*
|
||||
* 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.backend.placeholder;
|
||||
|
||||
import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.ConfigManager;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.MainTask;
|
||||
import net.momirealms.customnameplates.api.feature.*;
|
||||
import net.momirealms.customnameplates.api.feature.background.Background;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
|
||||
import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig;
|
||||
import net.momirealms.customnameplates.api.feature.image.Image;
|
||||
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
|
||||
import net.momirealms.customnameplates.api.helper.AdventureHelper;
|
||||
import net.momirealms.customnameplates.api.placeholder.*;
|
||||
import net.momirealms.customnameplates.api.placeholder.internal.*;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.util.Vector3;
|
||||
import net.momirealms.customnameplates.common.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
@SuppressWarnings("DuplicatedCode")
|
||||
public class PlaceholderManagerImpl implements PlaceholderManager {
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("%([^%]*)%");
|
||||
|
||||
private final CustomNameplates plugin;
|
||||
private final HashMap<String, Integer> refreshIntervals = new HashMap<>();
|
||||
private final Map<String, Placeholder> registeredPlaceholders = new HashMap<>();
|
||||
private final HashMap<Placeholder, List<PreParsedDynamicText>> childrenText = new HashMap<>();
|
||||
private final List<PreParsedDynamicText> delayedInitTexts = new ArrayList<>();
|
||||
private final HashMap<String, Placeholder> nestedPlaceholders = new HashMap<>();
|
||||
|
||||
public PlaceholderManagerImpl(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
YamlDocument document = ConfigManager.getMainConfig();
|
||||
Section section = document.getSection("other-settings.placeholder-refresh-interval");
|
||||
if (section != null) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
if (value instanceof Integer) {
|
||||
refreshIntervals.put(id, (Integer) value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.loadInternalPlaceholders();
|
||||
this.loadNestNameplatePlaceholders();
|
||||
this.loadCustomPlaceholders();
|
||||
|
||||
for (Map.Entry<Placeholder, List<PreParsedDynamicText>> entry : this.childrenText.entrySet()) {
|
||||
Placeholder placeholder = entry.getKey();
|
||||
for (PreParsedDynamicText preParsedText : entry.getValue()) {
|
||||
preParsedText.init();
|
||||
placeholder.addChildren(preParsedText.placeholders());
|
||||
}
|
||||
this.nestedPlaceholders.put(placeholder.id().replace("%np_", "%nameplates_").replace("%rel_np_", "%rel_nameplates_"), placeholder);
|
||||
}
|
||||
this.childrenText.clear();
|
||||
|
||||
for (PreParsedDynamicText preParsedText : this.delayedInitTexts) {
|
||||
preParsedText.init();
|
||||
}
|
||||
this.delayedInitTexts.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unload() {
|
||||
this.refreshIntervals.clear();
|
||||
this.registeredPlaceholders.clear();
|
||||
this.delayedInitTexts.clear();
|
||||
this.childrenText.clear();
|
||||
this.nestedPlaceholders.clear();
|
||||
PlaceholderCounter.reset();
|
||||
}
|
||||
|
||||
private void loadNestNameplatePlaceholders() {
|
||||
PreParsedDynamicText nameTag = new PreParsedDynamicText(plugin.getNameplateManager().playerNameTag());
|
||||
Placeholder placeholder1 = this.registerPlayerPlaceholder("%np_tag-image%", (player -> {
|
||||
String equippedNameplate = player.equippedNameplate();
|
||||
if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId();
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(equippedNameplate);
|
||||
if (nameplate == null) return "";
|
||||
String tag = nameTag.fastCreate(player).render(player);
|
||||
float advance = plugin.getAdvanceManager().getLineAdvance(tag);
|
||||
return AdventureHelper.surroundWithNameplatesFont(nameplate.createImage(advance, 1, 0));
|
||||
}));
|
||||
Placeholder placeholder2 = this.registerRelationalPlaceholder("%rel_np_tag-image%", (p1, p2) -> {
|
||||
String equippedNameplate = p1.equippedNameplate();
|
||||
if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId();
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(equippedNameplate);
|
||||
if (nameplate == null) return "";
|
||||
String tag = nameTag.fastCreate(p1).render(p2);
|
||||
float advance = plugin.getAdvanceManager().getLineAdvance(tag);
|
||||
return AdventureHelper.surroundWithNameplatesFont(nameplate.createImage(advance, 1, 0));
|
||||
});
|
||||
Placeholder placeholder3 = this.registerPlayerPlaceholder("%np_tag-text%", (player -> nameTag.fastCreate(player).render(player)));
|
||||
Placeholder placeholder4 = this.registerRelationalPlaceholder("%rel_np_tag-text%", (p1, p2) -> nameTag.fastCreate(p1).render(p2));
|
||||
Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_tag%", (player -> {
|
||||
String equippedNameplate = player.equippedNameplate();
|
||||
if (equippedNameplate.equals("none")) equippedNameplate = plugin.getNameplateManager().defaultNameplateId();
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(equippedNameplate);
|
||||
String tag = nameTag.fastCreate(player).render(player);
|
||||
if (nameplate == null) return tag;
|
||||
float advance = plugin.getAdvanceManager().getLineAdvance(tag);
|
||||
return AdventureHelper.surroundWithNameplatesFont(nameplate.createImagePrefix(advance, 1, 0))
|
||||
+ tag + AdventureHelper.surroundWithNameplatesFont(nameplate.createImageSuffix(advance, 1, 0));
|
||||
}));
|
||||
Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_tag%", (p1, p2) -> {
|
||||
String equippedNameplate = p1.equippedNameplate();
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(equippedNameplate);
|
||||
if (nameplate == null) return p1.name();
|
||||
String tag = nameTag.fastCreate(p1).render(p2);
|
||||
float advance = plugin.getAdvanceManager().getLineAdvance(tag);
|
||||
return AdventureHelper.surroundWithNameplatesFont(nameplate.createImagePrefix(advance, 1, 0))
|
||||
+ tag + AdventureHelper.surroundWithNameplatesFont(nameplate.createImageSuffix(advance, 1, 0));
|
||||
});
|
||||
List<PreParsedDynamicText> list = List.of(nameTag);
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
childrenText.put(placeholder4, list);
|
||||
childrenText.put(placeholder5, list);
|
||||
childrenText.put(placeholder6, list);
|
||||
}
|
||||
|
||||
private void loadInternalPlaceholders() {
|
||||
this.registerPlayerPlaceholder("%player_name%", CNPlayer::name);
|
||||
this.registerPlayerPlaceholder("%player_x%", (player -> String.valueOf((int) Math.floor(player.position().x()))));
|
||||
this.registerPlayerPlaceholder("%player_y%", (player -> String.valueOf((int) Math.floor(player.position().y()))));
|
||||
this.registerPlayerPlaceholder("%player_z%", (player -> String.valueOf((int) Math.floor(player.position().z()))));
|
||||
this.registerPlayerPlaceholder("%player_world%", (CNPlayer::world));
|
||||
this.registerPlayerPlaceholder("%player_remaining_air%", (player -> String.valueOf(player.remainingAir())));
|
||||
for (int i = -256; i <= 256; i++) {
|
||||
String characters = OffsetFont.createOffsets(i);
|
||||
this.registerPlayerPlaceholder("%np_offset_" + i + "%", (p) -> AdventureHelper.surroundWithNameplatesFont(characters));
|
||||
}
|
||||
this.registerPlayerPlaceholder("%np_equipped_nameplate%", CNPlayer::equippedNameplate);
|
||||
this.registerPlayerPlaceholder("%np_equipped_bubble%", CNPlayer::equippedBubble);
|
||||
this.registerPlayerPlaceholder("%np_equipped_nameplate-name%", (player) -> {
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(player.equippedNameplate());
|
||||
return Optional.ofNullable(nameplate).map(Nameplate::displayName).orElse("");
|
||||
});
|
||||
this.registerPlayerPlaceholder("%np_equipped_bubble-name%", (player) -> {
|
||||
BubbleConfig bubble = plugin.getBubbleManager().bubbleConfigById(player.equippedBubble());
|
||||
return Optional.ofNullable(bubble).map(BubbleConfig::displayName).orElse("");
|
||||
});
|
||||
this.registerSharedPlaceholder("%shared_np_is-latest%", () -> String.valueOf(plugin.isUpToDate()));
|
||||
this.registerPlayerPlaceholder("%np_is-latest%", (player) -> String.valueOf(plugin.isUpToDate()));
|
||||
this.registerPlayerPlaceholder("%np_time%", (player) -> {
|
||||
long time = player.playerTime() % 24_000;
|
||||
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;
|
||||
});
|
||||
this.registerPlayerPlaceholder("%np_actionbar%", (player) -> Optional.ofNullable(plugin.getActionBarManager().getExternalActionBar(player)).orElse(""));
|
||||
for (Image image : plugin.getImageManager().images()) {
|
||||
this.registerSharedPlaceholder("%shared_np_image_" + image.id() + "%",
|
||||
() -> AdventureHelper.surroundWithNameplatesFont(String.valueOf(image.character().character())));
|
||||
this.registerPlayerPlaceholder("%np_image_" + image.id() + "%",
|
||||
(player) -> AdventureHelper.surroundWithNameplatesFont(String.valueOf(image.character().character())));
|
||||
this.registerSharedPlaceholder("%shared_np_img_" + image.id() + "%",
|
||||
() -> String.valueOf(image.character().character()));
|
||||
this.registerPlayerPlaceholder("%np_img_" + image.id() + "%",
|
||||
(player) -> String.valueOf(image.character().character()));
|
||||
}
|
||||
this.registerRelationalPlaceholder("%rel_np_distance%", (p1, p2) -> {
|
||||
Vector3 loc1 = p1.position();
|
||||
Vector3 loc2 = p2.position();
|
||||
double distance = Math.sqrt(Math.pow(loc1.x() - loc2.x(), 2) + Math.pow(loc1.y() - loc2.y(), 2) + Math.pow(loc1.z() - loc2.z(), 2));
|
||||
return String.format("%.2f", distance);
|
||||
});
|
||||
}
|
||||
|
||||
private void loadCustomPlaceholders() {
|
||||
plugin.getConfigManager().saveResource("configs" + File.separator + "custom-placeholders.yml");
|
||||
YamlDocument document = plugin.getConfigManager().loadData(new File(plugin.getDataDirectory().toFile(), "configs" + File.separator + "custom-placeholders.yml"));
|
||||
Section section1 = document.getSection("conditional-text");
|
||||
if (section1 != null) loadConditionTextSection(section1);
|
||||
Section section2 = document.getSection("background-text");
|
||||
if (section2 != null) loadBackgroundTextSection(section2);
|
||||
Section section3 = document.getSection("nameplate-text");
|
||||
if (section3 != null) loadNameplateTextSection(section3);
|
||||
Section section4 = document.getSection("bubble-text");
|
||||
if (section4 != null) loadBubbleTextSection(section4);
|
||||
Section section5 = document.getSection("switch-text");
|
||||
if (section5 != null) loadSwitchTextSection(section5);
|
||||
Section section6 = document.getSection("vanilla-hud");
|
||||
if (section6 != null) loadVanillaHud(section6);
|
||||
Section section7 = document.getSection("static-text");
|
||||
if (section7 != null) loadStaticText(section7);
|
||||
Section section8 = document.getSection("shift-text");
|
||||
if (section8 != null) loadShiftText(section8);
|
||||
}
|
||||
|
||||
private void loadShiftText(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
String text = inner.getString("text");
|
||||
String font = inner.getString("font");
|
||||
ShiftText shiftText = new ShiftText(font, text);
|
||||
Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_shift_" + id + "%", () -> {
|
||||
String parsed = shiftText.getText().fastCreate(null).render(null);
|
||||
return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font);
|
||||
});
|
||||
Placeholder placeholder2 = registerPlayerPlaceholder("%np_shift_" + id + "%", (player) -> {
|
||||
String parsed = shiftText.getText().fastCreate(player).render(player);
|
||||
return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font);
|
||||
});
|
||||
Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_shift_" + id + "%", (p1, p2) -> {
|
||||
String parsed = shiftText.getText().fastCreate(p1).render(p2);
|
||||
return AdventureHelper.surroundWithMiniMessageFont(parsed, ConfigManager.namespace() + ":" + font);
|
||||
});
|
||||
List<PreParsedDynamicText> list = List.of(shiftText.getText());
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadStaticText(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
StaticPosition position = StaticPosition.valueOf(inner.getString("position", "middle").toUpperCase(Locale.ENGLISH));
|
||||
int totalWidth = inner.getInt("value", 100);
|
||||
StaticText staticText = new StaticText(totalWidth, position, inner.getString("text"));
|
||||
Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_static_" + id + "%", staticText::create);
|
||||
Placeholder placeholder2 = registerPlayerPlaceholder("%np_static_" + id + "%", staticText::create);
|
||||
Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_static_" + id + "%", staticText::create);
|
||||
List<PreParsedDynamicText> list = List.of(staticText.getText());
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadVanillaHud(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
boolean reverse = inner.getBoolean("reverse", true);
|
||||
Image empty = requireNonNull(plugin.getImageManager().imageById(inner.getString("images.empty")), "image.empty should not be null");
|
||||
Image half = requireNonNull(plugin.getImageManager().imageById(inner.getString("images.half")), "image.half should not be null");
|
||||
Image full = requireNonNull(plugin.getImageManager().imageById(inner.getString("images.full")), "image.full should not be null");
|
||||
String currentValue = section.getString("placeholder.value", "1");
|
||||
String maxValue = section.getString("placeholder.max-value", currentValue);
|
||||
boolean removeShadow = section.getBoolean("remove-shadow", false);
|
||||
VanillaHud vanillaHud = new VanillaHud(empty, half, full, reverse, currentValue, maxValue, removeShadow);
|
||||
List<PreParsedDynamicText> list = List.of(vanillaHud.getCurrent(), vanillaHud.getMax());
|
||||
Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_vanilla_" + id + "%", vanillaHud::create);
|
||||
Placeholder placeholder2 = registerPlayerPlaceholder("%np_vanilla_" + id + "%", vanillaHud::create);
|
||||
Placeholder placeholder3 = registerRelationalPlaceholder("%np_vanilla_" + id + "%", vanillaHud::create);
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadSwitchTextSection(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
PreParsedDynamicText placeholderToSwitch = new PreParsedDynamicText(inner.getString("switch"));
|
||||
PreParsedDynamicText defaultValue = new PreParsedDynamicText(inner.getString("default", ""));
|
||||
ArrayList<PreParsedDynamicText> list = new ArrayList<>();
|
||||
list.add(placeholderToSwitch);
|
||||
this.delayedInitTexts.add(defaultValue);
|
||||
Map<String, PreParsedDynamicText> valueMap = new HashMap<>();
|
||||
Section results = inner.getSection("case");
|
||||
if (results != null) {
|
||||
for (Map.Entry<String, Object> strEntry : results.getStringRouteMappedValues(false).entrySet()) {
|
||||
if (strEntry.getValue() instanceof String string) {
|
||||
PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(string);
|
||||
valueMap.put(strEntry.getKey(), preParsedDynamicText);
|
||||
this.delayedInitTexts.add(preParsedDynamicText);
|
||||
}
|
||||
}
|
||||
}
|
||||
Placeholder placeholder1 = registerSharedPlaceholder("%shared_np_switch_" + id + "%", () -> {
|
||||
String value = placeholderToSwitch.fastCreate(null).render(null);
|
||||
PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue);
|
||||
return text.fastCreate(null).render(null);
|
||||
});
|
||||
Placeholder placeholder2 = registerPlayerPlaceholder("%np_switch_" + id + "%", (player) -> {
|
||||
String value = placeholderToSwitch.fastCreate(player).render(player);
|
||||
PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue);
|
||||
player.forceUpdate(text.placeholders(), Collections.emptySet());
|
||||
return text.fastCreate(player).render(player);
|
||||
});
|
||||
Placeholder placeholder3 = registerRelationalPlaceholder("%rel_np_switch_" + id + "%", (p1, p2) -> {
|
||||
String value = placeholderToSwitch.fastCreate(p1).render(p2);
|
||||
PreParsedDynamicText text = valueMap.getOrDefault(value, defaultValue);
|
||||
p1.forceUpdate(text.placeholders(), Set.of(p2));
|
||||
return text.fastCreate(p1).render(p2);
|
||||
});
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBubbleTextSection(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
String bbID = inner.getString("bubble");
|
||||
Bubble bubble = plugin.getBubbleManager().bubbleById(bbID);
|
||||
if (bubble != null) {
|
||||
AdaptiveImageText<Bubble> adaptiveImageText = AdaptiveImageText.create(
|
||||
id, inner.getString("text"), bubble, inner.getBoolean("remove-shadow"),
|
||||
inner.getInt("left-margin", 1), inner.getInt("right-margin", 1)
|
||||
);
|
||||
List<PreParsedDynamicText> list = new ArrayList<>();
|
||||
list.add(adaptiveImageText.getPreParsedDynamicText());
|
||||
Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_bubble_" + id + "%", () -> adaptiveImageText.getFull(null, null));
|
||||
Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_bubble_" + id + "%", (p) -> adaptiveImageText.getFull(p, p));
|
||||
Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_bubble_" + id + "%", adaptiveImageText::getFull);
|
||||
Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_bubble-text_" + id + "%", () -> adaptiveImageText.getText(null, null));
|
||||
Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_bubble-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p));
|
||||
Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_bubble-text_" + id + "%", adaptiveImageText::getText);
|
||||
Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_bubble-image_" + id + "%", () -> adaptiveImageText.getImage(null, null));
|
||||
Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_bubble-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p));
|
||||
Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_bubble-image_" + id + "%", adaptiveImageText::getImage);
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
childrenText.put(placeholder4, list);
|
||||
childrenText.put(placeholder5, list);
|
||||
childrenText.put(placeholder6, list);
|
||||
childrenText.put(placeholder7, list);
|
||||
childrenText.put(placeholder8, list);
|
||||
childrenText.put(placeholder9, list);
|
||||
} else {
|
||||
plugin.getPluginLogger().warn("Nameplate [" + bbID + "] not exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadNameplateTextSection(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
String npID = inner.getString("nameplate");
|
||||
Nameplate nameplate = plugin.getNameplateManager().nameplateById(npID);
|
||||
if (nameplate != null) {
|
||||
AdaptiveImageText<Nameplate> adaptiveImageText = AdaptiveImageText.create(
|
||||
id, inner.getString("text"), nameplate, inner.getBoolean("remove-shadow"),
|
||||
inner.getInt("left-margin", 1), inner.getInt("right-margin", 1)
|
||||
);
|
||||
List<PreParsedDynamicText> list = new ArrayList<>();
|
||||
list.add(adaptiveImageText.getPreParsedDynamicText());
|
||||
Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_nameplate_" + id + "%", () -> adaptiveImageText.getFull(null, null));
|
||||
Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_nameplate_" + id + "%", (p) -> adaptiveImageText.getFull(p, p));
|
||||
Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_nameplate_" + id + "%", adaptiveImageText::getFull);
|
||||
Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_nameplate-text_" + id + "%", () -> adaptiveImageText.getText(null, null));
|
||||
Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_nameplate-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p));
|
||||
Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_nameplate-text_" + id + "%", adaptiveImageText::getText);
|
||||
Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_nameplate-image_" + id + "%", () -> adaptiveImageText.getImage(null, null));
|
||||
Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_nameplate-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p));
|
||||
Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_nameplate-image_" + id + "%", adaptiveImageText::getImage);
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
childrenText.put(placeholder4, list);
|
||||
childrenText.put(placeholder5, list);
|
||||
childrenText.put(placeholder6, list);
|
||||
childrenText.put(placeholder7, list);
|
||||
childrenText.put(placeholder8, list);
|
||||
childrenText.put(placeholder9, list);
|
||||
} else {
|
||||
plugin.getPluginLogger().warn("Nameplate [" + npID + "] not exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadBackgroundTextSection(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section inner) {
|
||||
String bgID = inner.getString("background");
|
||||
Background background = plugin.getBackgroundManager().backgroundById(bgID);
|
||||
if (background != null) {
|
||||
AdaptiveImageText<Background> adaptiveImageText = AdaptiveImageText.create(
|
||||
id, inner.getString("text"), background, inner.getBoolean("remove-shadow"),
|
||||
inner.getInt("left-margin", 2), inner.getInt("right-margin", 0)
|
||||
);
|
||||
List<PreParsedDynamicText> list = new ArrayList<>();
|
||||
list.add(adaptiveImageText.getPreParsedDynamicText());
|
||||
Placeholder placeholder1 = this.registerSharedPlaceholder("%shared_np_background_" + id + "%", () -> adaptiveImageText.getFull(null, null));
|
||||
Placeholder placeholder2 = this.registerPlayerPlaceholder("%np_background_" + id + "%", (p) -> adaptiveImageText.getFull(p, p));
|
||||
Placeholder placeholder3 = this.registerRelationalPlaceholder("%rel_np_background_" + id + "%", adaptiveImageText::getFull);
|
||||
Placeholder placeholder4 = this.registerSharedPlaceholder("%shared_np_background-text_" + id + "%", () -> adaptiveImageText.getText(null, null));
|
||||
Placeholder placeholder5 = this.registerPlayerPlaceholder("%np_background-text_" + id + "%", (p) -> adaptiveImageText.getText(p, p));
|
||||
Placeholder placeholder6 = this.registerRelationalPlaceholder("%rel_np_background-text_" + id + "%", adaptiveImageText::getText);
|
||||
Placeholder placeholder7 = this.registerSharedPlaceholder("%shared_np_background-image_" + id + "%", () -> adaptiveImageText.getImage(null, null));
|
||||
Placeholder placeholder8 = this.registerPlayerPlaceholder("%np_background-image_" + id + "%", (p) -> adaptiveImageText.getImage(p, p));
|
||||
Placeholder placeholder9 = this.registerRelationalPlaceholder("%rel_np_background-image_" + id + "%", adaptiveImageText::getImage);
|
||||
childrenText.put(placeholder1, list);
|
||||
childrenText.put(placeholder2, list);
|
||||
childrenText.put(placeholder3, list);
|
||||
childrenText.put(placeholder4, list);
|
||||
childrenText.put(placeholder5, list);
|
||||
childrenText.put(placeholder6, list);
|
||||
childrenText.put(placeholder7, list);
|
||||
childrenText.put(placeholder8, list);
|
||||
childrenText.put(placeholder9, list);
|
||||
} else {
|
||||
plugin.getPluginLogger().warn("Background [" + bgID + "] not exists");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void loadConditionTextSection(Section section) {
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String id = entry.getKey();
|
||||
if (entry.getValue() instanceof Section placeholderSection) {
|
||||
ArrayList<Pair<PreParsedDynamicText, Requirement[]>> orderedTexts = new ArrayList<>();
|
||||
for (Map.Entry<String, Object> conditionEntry : placeholderSection.getStringRouteMappedValues(false).entrySet()) {
|
||||
if (conditionEntry.getValue() instanceof Section inner) {
|
||||
String text = inner.getString("text");
|
||||
Requirement[] requirements = plugin.getRequirementManager().parseRequirements(inner.getSection("conditions"));
|
||||
PreParsedDynamicText preParsedDynamicText = new PreParsedDynamicText(text);
|
||||
orderedTexts.add(Pair.of(preParsedDynamicText, requirements));
|
||||
this.delayedInitTexts.add(preParsedDynamicText);
|
||||
}
|
||||
}
|
||||
this.registerSharedPlaceholder("%shared_np_conditional_" + id + "%", () -> {
|
||||
outer:
|
||||
for (Pair<PreParsedDynamicText, Requirement[]> orderedText : orderedTexts) {
|
||||
for (Requirement requirement : orderedText.right()) {
|
||||
if (!requirement.isSatisfied(null, null)) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
return orderedText.left().fastCreate(null).render(null);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
this.registerPlayerPlaceholder("%np_conditional_" + id + "%", (player) -> {
|
||||
for (Pair<PreParsedDynamicText, Requirement[]> orderedText : orderedTexts) {
|
||||
if (!player.isMet(orderedText.right())) {
|
||||
continue;
|
||||
}
|
||||
PreParsedDynamicText text = orderedText.left();
|
||||
player.forceUpdate(text.placeholders(), Collections.emptySet());
|
||||
return text.fastCreate(player).render(player);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
this.registerRelationalPlaceholder("%rel_np_conditional_" + id + "%", (p1, p2) -> {
|
||||
for (Pair<PreParsedDynamicText, Requirement[]> orderedText : orderedTexts) {
|
||||
if (!p1.isMet(p2, orderedText.right())) {
|
||||
continue;
|
||||
}
|
||||
PreParsedDynamicText text = orderedText.left();
|
||||
p1.forceUpdate(text.placeholders(), Set.of(p2));
|
||||
return text.fastCreate(p1).render(p2);
|
||||
}
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refreshPlaceholders() {
|
||||
for (CNPlayer player : plugin.getOnlinePlayers()) {
|
||||
if (!player.isOnline()) continue;
|
||||
Set<Feature> featuresToNotifyUpdates = new HashSet<>();
|
||||
Map<Feature, List<CNPlayer>> relationalFeaturesToNotifyUpdates = new HashMap<>();
|
||||
List<Placeholder> placeholdersToUpdate = player.activePlaceholdersToRefresh();
|
||||
List<RelationalPlaceholder> delayedPlaceholdersToUpdate = new ArrayList<>();
|
||||
for (Placeholder placeholder : placeholdersToUpdate) {
|
||||
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
|
||||
TickStampData<String> previous = player.getValue(placeholder);
|
||||
if (previous == null) {
|
||||
String value = playerPlaceholder.request(player);
|
||||
player.setValue(placeholder, new TickStampData<>(value, MainTask.getTicks(), true));
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
} else {
|
||||
if (previous.ticks() == MainTask.getTicks()) {
|
||||
if (previous.hasValueChanged()) {
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String value = playerPlaceholder.request(player);
|
||||
if (!previous.data().equals(value)) {
|
||||
previous.data(value);
|
||||
previous.updateTicks(true);
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
} else {
|
||||
previous.updateTicks(false);
|
||||
}
|
||||
}
|
||||
} else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
|
||||
delayedPlaceholdersToUpdate.add(relationalPlaceholder);
|
||||
} else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
|
||||
TickStampData<String> previous = player.getValue(placeholder);
|
||||
if (previous == null) {
|
||||
String value;
|
||||
// if the shared placeholder has been updated by other players
|
||||
if (MainTask.hasRequested(sharedPlaceholder.countId())) {
|
||||
value = sharedPlaceholder.getLatestValue();
|
||||
} else {
|
||||
value = sharedPlaceholder.request();
|
||||
}
|
||||
player.setValue(placeholder, new TickStampData<>(value, MainTask.getTicks(), true));
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
} else {
|
||||
// The placeholder has been refreshed by other codes
|
||||
if (previous.ticks() == MainTask.getTicks()) {
|
||||
if (previous.hasValueChanged()) {
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String value;
|
||||
// if the shared placeholder has been updated by other players
|
||||
if (MainTask.hasRequested(sharedPlaceholder.countId())) {
|
||||
value = sharedPlaceholder.getLatestValue();
|
||||
} else {
|
||||
value = sharedPlaceholder.request();
|
||||
}
|
||||
if (!previous.data().equals(value)) {
|
||||
previous.data(value);
|
||||
previous.updateTicks(true);
|
||||
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
|
||||
} else {
|
||||
previous.updateTicks(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (RelationalPlaceholder placeholder : delayedPlaceholdersToUpdate) {
|
||||
for (CNPlayer nearby : player.nearbyPlayers()) {
|
||||
TickStampData<String> previous = player.getRelationalValue(placeholder, nearby);
|
||||
if (previous == null) {
|
||||
String value = placeholder.request(player, nearby);
|
||||
player.setRelationalValue(placeholder, nearby, new TickStampData<>(value, MainTask.getTicks(), true));
|
||||
for (Feature feature : player.activeFeatures(placeholder)) {
|
||||
// Filter features that will not be updated for all players
|
||||
if (!featuresToNotifyUpdates.contains(feature)) {
|
||||
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>());
|
||||
players.add(nearby);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (previous.ticks() == MainTask.getTicks()) {
|
||||
if (previous.hasValueChanged()) {
|
||||
for (Feature feature : player.activeFeatures(placeholder)) {
|
||||
// Filter features that will not be updated for all players
|
||||
if (!featuresToNotifyUpdates.contains(feature)) {
|
||||
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>());
|
||||
players.add(nearby);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
String value = placeholder.request(player, nearby);
|
||||
if (!previous.data().equals(value)) {
|
||||
previous.data(value);
|
||||
previous.updateTicks(true);
|
||||
for (Feature feature : player.activeFeatures(placeholder)) {
|
||||
// Filter features that will not be updated for all players
|
||||
if (!featuresToNotifyUpdates.contains(feature)) {
|
||||
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ArrayList<>());
|
||||
players.add(nearby);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
previous.updateTicks(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Switch to another thread for updating
|
||||
plugin.getScheduler().async().execute(() -> {
|
||||
// Async task takes time and the player might have been offline
|
||||
if (!player.isOnline()) return;
|
||||
for (Feature feature : featuresToNotifyUpdates) {
|
||||
feature.notifyPlaceholderUpdates(player, false);
|
||||
}
|
||||
for (Map.Entry<Feature, List<CNPlayer>> innerEntry : relationalFeaturesToNotifyUpdates.entrySet()) {
|
||||
Feature feature = innerEntry.getKey();
|
||||
if (feature instanceof RelationalFeature relationalFeature) {
|
||||
for (CNPlayer other : innerEntry.getValue()) {
|
||||
relationalFeature.notifyPlaceholderUpdates(player, other, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRefreshInterval(String id) {
|
||||
return refreshIntervals.getOrDefault(id, ConfigManager.defaultRefreshInterval());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Placeholder> T registerPlaceholder(T placeholder) {
|
||||
Placeholder nested = nestedPlaceholders.get(placeholder.id());
|
||||
if (nested != null) {
|
||||
placeholder.addChildren(nested.children());
|
||||
}
|
||||
registeredPlaceholders.put(placeholder.id(), placeholder);
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlayerPlaceholder registerPlayerPlaceholder(String id, int refreshInterval, Function<CNPlayer, String> function) {
|
||||
PlayerPlaceholderImpl impl = new PlayerPlaceholderImpl(this, id, refreshInterval, function);
|
||||
return registerPlaceholder(impl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RelationalPlaceholder registerRelationalPlaceholder(String id, int refreshInterval, BiFunction<CNPlayer, CNPlayer, String> function) {
|
||||
RelationalPlaceholderImpl impl = new RelationalPlaceholderImpl(this, id, refreshInterval, function);
|
||||
return registerPlaceholder(impl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SharedPlaceholder registerSharedPlaceholder(String id, int refreshInterval, Supplier<String> contextSupplier) {
|
||||
SharedPlaceholderImpl impl = new SharedPlaceholderImpl(this, id, refreshInterval, contextSupplier);
|
||||
return registerPlaceholder(impl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholder getPlaceholder(String id) {
|
||||
Placeholder placeholder = registeredPlaceholders.get(id);
|
||||
if (placeholder != null) return placeholder;
|
||||
return plugin.getPlatform().registerPlatformPlaceholder(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Placeholder getRegisteredPlaceholder(String id) {
|
||||
return registeredPlaceholders.get(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterPlaceholder(String id) {
|
||||
registeredPlaceholders.remove(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> detectPlaceholders(String text) {
|
||||
int firstPercent = text.indexOf('%');
|
||||
if (firstPercent == -1) return Collections.emptyList();
|
||||
if (firstPercent == 0 && text.charAt(text.length() - 1) == '%' && text.indexOf('%', 1) == text.length() - 1) {
|
||||
return Collections.singletonList(text);
|
||||
}
|
||||
List<String> placeholders = new ArrayList<>();
|
||||
Matcher m = PATTERN.matcher(text);
|
||||
while (m.find()) {
|
||||
placeholders.add(m.group());
|
||||
}
|
||||
return placeholders;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.backend.placeholder;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.placeholder.AbstractPlaceholder;
|
||||
import net.momirealms.customnameplates.api.placeholder.PlaceholderManager;
|
||||
import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PlayerPlaceholderImpl extends AbstractPlaceholder implements PlayerPlaceholder {
|
||||
|
||||
private final Function<CNPlayer, String> function;
|
||||
|
||||
protected PlayerPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, Function<CNPlayer, String> function) {
|
||||
super(manager, id, refreshInterval);
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String request(CNPlayer player) {
|
||||
return function.apply(player);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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.backend.placeholder;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.placeholder.AbstractPlaceholder;
|
||||
import net.momirealms.customnameplates.api.placeholder.PlaceholderManager;
|
||||
import net.momirealms.customnameplates.api.placeholder.RelationalPlaceholder;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public class RelationalPlaceholderImpl extends AbstractPlaceholder implements RelationalPlaceholder {
|
||||
|
||||
private final BiFunction<CNPlayer, CNPlayer, String> function;
|
||||
|
||||
protected RelationalPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, BiFunction<CNPlayer, CNPlayer, String> function) {
|
||||
super(manager, id, refreshInterval);
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String request(CNPlayer p1, CNPlayer p2) {
|
||||
return function.apply(p1, p2);
|
||||
}
|
||||
}
|
||||
@@ -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.backend.placeholder;
|
||||
|
||||
import net.momirealms.customnameplates.api.placeholder.AbstractPlaceholder;
|
||||
import net.momirealms.customnameplates.api.placeholder.PlaceholderManager;
|
||||
import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class SharedPlaceholderImpl extends AbstractPlaceholder implements SharedPlaceholder {
|
||||
|
||||
private final Supplier<String> supplier;
|
||||
private String latestValue;
|
||||
|
||||
protected SharedPlaceholderImpl(PlaceholderManager manager, String id, int refreshInterval, Supplier<String> supplier) {
|
||||
super(manager, id, refreshInterval);
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String request() {
|
||||
return supplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
latestValue = request();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLatestValue() {
|
||||
return latestValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.backend.requirement;
|
||||
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
|
||||
public abstract class AbstractRequirement implements Requirement {
|
||||
|
||||
protected final int refreshInterval;
|
||||
|
||||
public AbstractRequirement(int refreshInterval) {
|
||||
this.refreshInterval = refreshInterval;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int refreshInterval() {
|
||||
return refreshInterval;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* 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.backend.requirement;
|
||||
|
||||
import dev.dejvokep.boostedyaml.block.implementation.Section;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.api.requirement.RequirementFactory;
|
||||
import net.momirealms.customnameplates.api.requirement.RequirementManager;
|
||||
import net.momirealms.customnameplates.api.util.ConfigUtils;
|
||||
import net.momirealms.customnameplates.backend.requirement.builtin.*;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public abstract class AbstractRequirementManager implements RequirementManager {
|
||||
|
||||
protected final CustomNameplates plugin;
|
||||
private final HashMap<String, RequirementFactory> requirementFactoryMap = new HashMap<>();
|
||||
|
||||
public AbstractRequirementManager(CustomNameplates plugin) {
|
||||
this.plugin = plugin;
|
||||
this.registerInternalRequirements();
|
||||
this.registerPlatformRequirements();
|
||||
}
|
||||
|
||||
private void registerInternalRequirements() {
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("papi", ""), true);
|
||||
return new NotInListRequirement(interval, dynamicText1, new HashSet<>(section.getStringList("values")));
|
||||
}, "!in-list");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("papi", ""), true);
|
||||
return new InListRequirement(interval, dynamicText1, new HashSet<>(section.getStringList("values")));
|
||||
}, "in-list");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
String value2 = section.getString("value2", "");
|
||||
return new NotEndWithRequirement(interval, dynamicText1, value2);
|
||||
}, "!ends-with", "!endsWith", "!end-with", "!endWith");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
String value2 = section.getString("value2", "");
|
||||
return new EndWithRequirement(interval, dynamicText1, value2);
|
||||
}, "ends-with", "endsWith", "end-with", "endWith");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
String value2 = section.getString("value2", "");
|
||||
return new NotStartWithRequirement(interval, dynamicText1, value2);
|
||||
}, "!starts-with", "!startsWith", "!start-with", "!startWith");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
String value2 = section.getString("value2", "");
|
||||
return new StartWithRequirement(interval, dynamicText1, value2);
|
||||
}, "starts-with", "startsWith", "start-with", "startWith");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NotContainsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "!contains", "!contain");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new ContainsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "contains", "contain");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
String regex = section.getString("value2", "");
|
||||
return new RegexRequirement(interval, dynamicText1, regex);
|
||||
}, "regex");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberLessRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "<");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberNoGreaterRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "<=");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberGreaterRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, ">");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberNoLessRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, ">=");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberEqualsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "=");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NumberNotEqualsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "!=");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new EqualsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "equals", "equal");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
if (section == null) return Requirement.empty();
|
||||
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("value1", ""), true);
|
||||
PreParsedDynamicText dynamicText2 = new PreParsedDynamicText(section.getString("value2", ""), true);
|
||||
return new NotEqualsRequirement(interval, dynamicText1, dynamicText2);
|
||||
}, "!equals", "!equal");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
Requirement[] requirements = parseRequirements(section);
|
||||
return new OrRequirement(interval, requirements);
|
||||
}, "||", "or");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
Section section = ConfigUtils.safeCast(args, Section.class);
|
||||
Requirement[] requirements = parseRequirements(section);
|
||||
return new AndRequirement(interval, requirements);
|
||||
}, "&&", "and");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
boolean has = (boolean) args;
|
||||
return new HasNameplateRequirement(interval, has);
|
||||
}, "has-nameplate");
|
||||
this.registerRequirement((args, interval) -> {
|
||||
boolean has = (boolean) args;
|
||||
return new HasBubbleRequirement(interval, has);
|
||||
}, "has-bubble");
|
||||
}
|
||||
|
||||
protected abstract void registerPlatformRequirements();
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
this.requirementFactoryMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean registerRequirement(@NotNull RequirementFactory requirementFactory, @NotNull String... types) {
|
||||
for (String type : types) {
|
||||
if (this.requirementFactoryMap.containsKey(type)) return false;
|
||||
}
|
||||
for (String type : types) {
|
||||
this.requirementFactoryMap.put(type, requirementFactory);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unregisterRequirement(@NotNull String type) {
|
||||
return this.requirementFactoryMap.remove(type) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RequirementFactory getRequirementFactory(@NotNull String type) {
|
||||
return requirementFactoryMap.get(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasRequirement(@NotNull String type) {
|
||||
return requirementFactoryMap.containsKey(type);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Requirement[] parseRequirements(Section section) {
|
||||
List<Requirement> requirements = new ArrayList<>();
|
||||
if (section != null)
|
||||
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
|
||||
String typeOrName = entry.getKey();
|
||||
if (hasRequirement(typeOrName)) {
|
||||
requirements.add(parseSimpleRequirement(typeOrName, entry.getValue()));
|
||||
} else {
|
||||
Section inner = section.getSection(typeOrName);
|
||||
if (inner != null) {
|
||||
requirements.add(parseRichRequirement(inner));
|
||||
} else {
|
||||
plugin.getPluginLogger().warn("Invalid requirement type found at " + section.getRouteAsString() + "." + typeOrName);
|
||||
}
|
||||
}
|
||||
}
|
||||
return requirements.toArray(new Requirement[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Requirement parseRichRequirement(@Nullable Section section) {
|
||||
if (section == null) {
|
||||
return Requirement.empty();
|
||||
}
|
||||
String type = section.getString("type");
|
||||
if (type == null) {
|
||||
plugin.getPluginLogger().warn("No requirement type found at " + section.getRouteAsString());
|
||||
return Requirement.empty();
|
||||
}
|
||||
var factory = getRequirementFactory(type);
|
||||
if (factory == null) {
|
||||
plugin.getPluginLogger().warn("Requirement type: " + type + " not exists");
|
||||
return Requirement.empty();
|
||||
}
|
||||
return factory.process(section.get("value"), section.getInt("refresh-interval", 10));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Requirement parseSimpleRequirement(@NotNull String type, @NotNull Object value) {
|
||||
RequirementFactory factory = getRequirementFactory(type);
|
||||
if (factory == null) {
|
||||
plugin.getPluginLogger().warn("Requirement type: " + type + " doesn't exist.");
|
||||
return Requirement.empty();
|
||||
}
|
||||
return factory.process(value, 10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
public class AndRequirement extends AbstractRequirement {
|
||||
|
||||
private final Requirement[] requirements;
|
||||
|
||||
public AndRequirement(int refreshInterval, Requirement[] requirements) {
|
||||
super(refreshInterval);
|
||||
this.requirements = requirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
for (Requirement requirement : requirements) {
|
||||
if (!requirement.isSatisfied(p1, p2)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "&&";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
AndRequirement that = (AndRequirement) object;
|
||||
int length = requirements.length;
|
||||
if (that.requirements.length != length)
|
||||
return false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object e1 = requirements[i];
|
||||
Object e2 = that.requirements[i];
|
||||
if (e1 == e2)
|
||||
continue;
|
||||
if (!e1.equals(e2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 281;
|
||||
for (Requirement requirement : requirements) {
|
||||
hash += requirement.hashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class ContainsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public ContainsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return a1.contains(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "contains";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class EndWithRequirement extends PlaceholderRequirement<String> {
|
||||
|
||||
public EndWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return a1.endsWith(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "ends-with";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class EqualsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public EqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return a1.equals(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "equals";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
public class HasBubbleRequirement extends AbstractRequirement {
|
||||
|
||||
private final boolean has;
|
||||
|
||||
public HasBubbleRequirement(int refreshInterval, boolean has) {
|
||||
super(refreshInterval);
|
||||
this.has = has;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
String bubble = p1.equippedBubble();
|
||||
if (bubble.equals("none")) bubble = CustomNameplates.getInstance().getBubbleManager().defaultBubbleId();
|
||||
if (has) {
|
||||
return !bubble.equals("none");
|
||||
} else {
|
||||
return bubble.equals("none");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "has-bubble";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
HasBubbleRequirement that = (HasBubbleRequirement) object;
|
||||
return has == that.has;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (has ? 781 : 3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
public class HasNameplateRequirement extends AbstractRequirement {
|
||||
|
||||
private final boolean has;
|
||||
|
||||
public HasNameplateRequirement(int refreshInterval, boolean has) {
|
||||
super(refreshInterval);
|
||||
this.has = has;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
String nameplate = p1.equippedNameplate();
|
||||
if (nameplate.equals("none")) nameplate = CustomNameplates.getInstance().getNameplateManager().defaultNameplateId();
|
||||
if (has) {
|
||||
return !nameplate.equals("none");
|
||||
} else {
|
||||
return nameplate.equals("none");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "has-nameplate";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
HasNameplateRequirement that = (HasNameplateRequirement) object;
|
||||
return has == that.has;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return (has ? 211 : 73);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class InListRequirement extends PlaceholderRequirement<Set<String>> {
|
||||
|
||||
public InListRequirement(int refreshInterval, PreParsedDynamicText text, Set<String> any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, Set<String> a2) {
|
||||
return a2.contains(a1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "in-list";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NotContainsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NotContainsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return !a1.contains(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!contains";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NotEndWithRequirement extends PlaceholderRequirement<String> {
|
||||
|
||||
public NotEndWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return !a1.endsWith(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!ends-with";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NotEqualsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NotEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return !a1.equals(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!equals";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class NotInListRequirement extends PlaceholderRequirement<Set<String>> {
|
||||
|
||||
public NotInListRequirement(int refreshInterval, PreParsedDynamicText text, Set<String> any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, Set<String> a2) {
|
||||
return !a2.contains(a1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!in-list";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NotStartWithRequirement extends PlaceholderRequirement<String> {
|
||||
|
||||
public NotStartWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return !a1.startsWith(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!starts-with";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberEqualsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 == d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "=";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberGreaterRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberGreaterRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 > d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return ">";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberLessRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberLessRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 < d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "<";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberNoGreaterRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberNoGreaterRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 <= d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "<=";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberNoLessRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberNoLessRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 >= d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return ">=";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class NumberNotEqualsRequirement extends PlaceholdersRequirement {
|
||||
|
||||
public NumberNotEqualsRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval, t1, t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
double d1 = Double.parseDouble(a1);
|
||||
double d2 = Double.parseDouble(a2);
|
||||
return d1 != d2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "!=";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.requirement.Requirement;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
public class OrRequirement extends AbstractRequirement {
|
||||
|
||||
private final Requirement[] requirements;
|
||||
|
||||
public OrRequirement(int refreshInterval, Requirement[] requirements) {
|
||||
super(refreshInterval);
|
||||
this.requirements = requirements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
if (requirements.length == 0) return true;
|
||||
for (Requirement requirement : requirements) {
|
||||
if (requirement.isSatisfied(p1, p2)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "||";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
OrRequirement that = (OrRequirement) object;
|
||||
int length = requirements.length;
|
||||
if (that.requirements.length != length)
|
||||
return false;
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object e1 = requirements[i];
|
||||
Object e2 = that.requirements[i];
|
||||
if (e1 == e2)
|
||||
continue;
|
||||
if (!e1.equals(e2)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hash = 271;
|
||||
for (Requirement requirement : requirements) {
|
||||
hash += requirement.hashCode();
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class PlaceholderRequirement<T> extends AbstractRequirement {
|
||||
|
||||
private final PreParsedDynamicText text;
|
||||
private final T any;
|
||||
|
||||
public PlaceholderRequirement(int refreshInterval, PreParsedDynamicText text, T any) {
|
||||
super(refreshInterval);
|
||||
this.text = text;
|
||||
this.any = any;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
p1.forceUpdate(text.placeholders(), Set.of(p2));
|
||||
String a1 = text.fastCreate(p1).render(p2);
|
||||
return checkArgument(a1, any);
|
||||
}
|
||||
|
||||
protected abstract boolean checkArgument(String a1, T a2);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
PlaceholderRequirement that = (PlaceholderRequirement) object;
|
||||
return Objects.equals(text, that.text) && Objects.equals(any, that.any);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 283 + text.hashCode() * 53 + any.hashCode() * 457 + type().hashCode() * 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class PlaceholdersRequirement extends AbstractRequirement {
|
||||
|
||||
private final PreParsedDynamicText t1;
|
||||
private final PreParsedDynamicText t2;
|
||||
|
||||
public PlaceholdersRequirement(int refreshInterval, PreParsedDynamicText t1, PreParsedDynamicText t2) {
|
||||
super(refreshInterval);
|
||||
this.t1 = t1;
|
||||
this.t2 = t2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
|
||||
p1.forceUpdate(t1.placeholders(), Set.of(p2));
|
||||
p1.forceUpdate(t2.placeholders(), Set.of(p2));
|
||||
String a1 = t1.fastCreate(p1).render(p2);
|
||||
String a2 = t2.fastCreate(p1).render(p2);
|
||||
return checkArgument(a1, a2);
|
||||
}
|
||||
|
||||
protected abstract boolean checkArgument(String a1, String a2);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (this == object) return true;
|
||||
if (object == null || getClass() != object.getClass()) return false;
|
||||
PlaceholdersRequirement that = (PlaceholdersRequirement) object;
|
||||
return Objects.equals(t1, that.t1) && Objects.equals(t2, that.t2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 73 + t1.hashCode() * 509 + t2.hashCode() * 211 + type().hashCode() * 311;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class RegexRequirement extends PlaceholderRequirement<String> {
|
||||
|
||||
public RegexRequirement(int refreshInterval, PreParsedDynamicText text, String any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return a1.matches(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "regex";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.backend.requirement.builtin;
|
||||
|
||||
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
|
||||
|
||||
public class StartWithRequirement extends PlaceholderRequirement<String> {
|
||||
|
||||
public StartWithRequirement(int refreshInterval, PreParsedDynamicText text, String any) {
|
||||
super(refreshInterval, text, any);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkArgument(String a1, String a2) {
|
||||
return a1.startsWith(a2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String type() {
|
||||
return "starts-with";
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import dev.dejvokep.boostedyaml.YamlDocument;
|
||||
import net.momirealms.customnameplates.api.AbstractCNPlayer;
|
||||
import net.momirealms.customnameplates.api.CNPlayer;
|
||||
import net.momirealms.customnameplates.api.CustomNameplates;
|
||||
import net.momirealms.customnameplates.api.event.DataLoadEvent;
|
||||
import net.momirealms.customnameplates.api.feature.JoinQuitListener;
|
||||
import net.momirealms.customnameplates.api.helper.GsonHelper;
|
||||
import net.momirealms.customnameplates.api.storage.DataStorageProvider;
|
||||
@@ -53,17 +54,19 @@ public class StorageManagerImpl implements StorageManager, JoinQuitListener {
|
||||
PlayerData data = playerData1.get();
|
||||
handleDataLoad(player, data);
|
||||
((AbstractCNPlayer) player).setLoaded(true);
|
||||
plugin.getEventManager().dispatch(DataLoadEvent.class, data);
|
||||
this.redisManager.updatePlayerData(data, async).thenAccept(result -> {
|
||||
if (!result) {
|
||||
plugin.getPluginLogger().warn("Failed to refresh redis player data for " + player.name());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.getDataSource().getPlayerData(uuid, async).thenAccept(playerData2 -> {
|
||||
this.dataSource().getPlayerData(uuid, async).thenAccept(playerData2 -> {
|
||||
if (playerData2.isPresent()) {
|
||||
PlayerData data = playerData2.get();
|
||||
handleDataLoad(player, data);
|
||||
((AbstractCNPlayer) player).setLoaded(true);
|
||||
plugin.getEventManager().dispatch(DataLoadEvent.class, data);
|
||||
this.redisManager.updatePlayerData(data, async).thenAccept(result -> {
|
||||
if (!result) {
|
||||
plugin.getPluginLogger().warn("Failed to refresh redis player data for " + player.name());
|
||||
@@ -76,7 +79,7 @@ public class StorageManagerImpl implements StorageManager, JoinQuitListener {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.getDataSource().getPlayerData(uuid, async).thenAccept(playerData -> {
|
||||
this.dataSource().getPlayerData(uuid, async).thenAccept(playerData -> {
|
||||
if (playerData.isPresent()) {
|
||||
PlayerData data = playerData.get();
|
||||
handleDataLoad(player, data);
|
||||
@@ -151,13 +154,13 @@ public class StorageManagerImpl implements StorageManager, JoinQuitListener {
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public String getServerID() {
|
||||
public String serverID() {
|
||||
return serverID;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public DataStorageProvider getDataSource() {
|
||||
public DataStorageProvider dataSource() {
|
||||
return dataSource;
|
||||
}
|
||||
|
||||
@@ -174,7 +177,7 @@ public class StorageManagerImpl implements StorageManager, JoinQuitListener {
|
||||
@NotNull
|
||||
@Override
|
||||
public String toJson(@NotNull PlayerData data) {
|
||||
return GsonHelper.get().toJson(data.toGsonData());
|
||||
return GsonHelper.get().toJson(data.toJsonData());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DummyStorage extends AbstractStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.NONE;
|
||||
}
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public class MongoDBProvider extends AbstractStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.MongoDB;
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ public class RedisManager extends AbstractStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.Redis;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,14 +21,14 @@ public abstract class AbstractHikariDatabase extends AbstractSQLDatabase {
|
||||
|
||||
public AbstractHikariDatabase(CustomNameplates plugin) {
|
||||
super(plugin);
|
||||
this.driverClass = getStorageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
|
||||
this.sqlBrand = getStorageType() == StorageType.MariaDB ? "MariaDB" : "MySQL";
|
||||
this.driverClass = storageType() == StorageType.MariaDB ? "org.mariadb.jdbc.Driver" : "com.mysql.cj.jdbc.Driver";
|
||||
this.sqlBrand = storageType() == StorageType.MariaDB ? "MariaDB" : "MySQL";
|
||||
try {
|
||||
Class.forName(this.driverClass);
|
||||
} catch (ClassNotFoundException e1) {
|
||||
if (getStorageType() == StorageType.MariaDB) {
|
||||
if (storageType() == StorageType.MariaDB) {
|
||||
plugin.getPluginLogger().warn("No MariaDB driver is found");
|
||||
} else if (getStorageType() == StorageType.MySQL) {
|
||||
} else if (storageType() == StorageType.MySQL) {
|
||||
try {
|
||||
Class.forName("com.mysql.jdbc.Driver");
|
||||
} catch (ClassNotFoundException e2) {
|
||||
|
||||
@@ -37,7 +37,7 @@ public abstract class AbstractSQLDatabase extends AbstractStorage {
|
||||
*/
|
||||
public void createTableIfNotExist() {
|
||||
try (Connection connection = getConnection()) {
|
||||
final String[] databaseSchema = getSchema(getStorageType().name().toLowerCase(Locale.ENGLISH));
|
||||
final String[] databaseSchema = getSchema(storageType().name().toLowerCase(Locale.ENGLISH));
|
||||
try (Statement statement = connection.createStatement()) {
|
||||
for (String tableCreationStatement : databaseSchema) {
|
||||
statement.execute(tableCreationStatement);
|
||||
|
||||
@@ -58,7 +58,7 @@ public class H2Provider extends AbstractSQLDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.H2;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ public class MariaDBProvider extends AbstractHikariDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.MariaDB;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ public class MySQLProvider extends AbstractHikariDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.MySQL;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ public class SQLiteProvider extends AbstractSQLDatabase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.SQLite;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class JsonProvider extends AbstractStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.JSON;
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ public class JsonProvider extends AbstractStorage {
|
||||
if (executor == null) executor = plugin.getScheduler().async();
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
this.saveToJsonFile(playerData.toGsonData(), getPlayerDataFile(playerData.uuid()));
|
||||
this.saveToJsonFile(playerData.toJsonData(), getPlayerDataFile(playerData.uuid()));
|
||||
} catch (Exception e) {
|
||||
future.complete(false);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ public class YAMLProvider extends AbstractStorage {
|
||||
}
|
||||
|
||||
@Override
|
||||
public StorageType getStorageType() {
|
||||
public StorageType storageType() {
|
||||
return StorageType.YAML;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user