73 Commits
Author SHA1 Message Date
XiaoMoMi 26b593950d bump version 3.0.15.2 2024-12-10 16:24:46 +08:00
XiaoMoMi 63896acda6 use snapshot adventure 2024-12-10 16:24:06 +08:00
XiaoMoMi 6eb5dee611 Update custom-placeholders.yml 2024-12-10 16:23:48 +08:00
XiaoMoMi 69869d1cb1 update pack format 2024-12-10 16:02:48 +08:00
XiaoMoMi 2e702d8945 Fix thread safety issue 2024-12-09 03:29:06 +08:00
XiaoMoMi 56c77bd294 3.0.15.1 2024-12-04 15:35:12 +08:00
XiaoMoMi 4d195820a0 improve send delay 2024-11-29 17:40:54 +08:00
XiaoMoMi 2874ab7c4e send bossbar immediately 2024-11-29 17:34:40 +08:00
XiaoMoMi cd1a0bffb6 chatty 2024-11-28 18:49:59 +08:00
XiaoMoMi 20d3919c6b Fix regex 2024-11-22 00:57:24 +08:00
XiaoMoMi fccaa059ff Added generate option for bitmaps 2024-11-21 02:14:49 +08:00
XiaoMoMi ed2fb60748 Update settings.gradle.kts 2024-11-19 18:38:26 +08:00
XiaoMoMi 378dc2e3bc Update gradle.properties 2024-11-19 18:37:38 +08:00
XiaoMoMi 6144423110 Update gradle.properties 2024-11-19 18:33:13 +08:00
XiaoMoMi d21fa8a3f7 work 2024-11-19 18:33:04 +08:00
XiaoMoMi 7e68a6cda7 move packages 2024-11-19 16:52:02 +08:00
XiaoMoMi 3df6d66efb better support 2024-11-19 16:46:01 +08:00
XiaoMoMi 001ecd28ed remove debug 2024-11-18 15:14:05 +08:00
XiaoMoMi 6fe8625722 Update TagRendererImpl.java 2024-11-18 03:06:23 +08:00
XiaoMoMi 43e006f49d 3.0.12 2024-11-18 03:03:45 +08:00
XiaoMoMi 8d63ad9c74 fix bugs 2024-11-18 02:51:49 +08:00
XiaoMoMi 5c2411e420 We need more work 2024-11-18 01:53:08 +08:00
XiaoMoMi a57cfb0842 more work 2024-11-18 01:30:54 +08:00
XiaoMoMi e4d7fb4346 work 2024-11-18 01:22:23 +08:00
XiaoMoMi 9a9865c424 improved default configs 2024-11-14 16:26:11 +08:00
XiaoMoMi f6b8ce253f Fixed advance not instantly updated 2024-11-11 22:11:38 +08:00
XiaoMoMi 144b358768 3.0.10.1 2024-11-11 15:37:37 +08:00
XiaoMoMi 0fd7ac1a79 Update OraxenEmojiProvider.java 2024-11-10 20:32:56 +08:00
XiaoMoMi 67f0e60327 Update README.md 2024-11-10 19:54:00 +08:00
XiaoMoMi d10eb6de67 code clean up 2024-11-10 19:39:00 +08:00
XiaoMoMi 8cfd098be9 add passenger condition 2024-11-10 19:21:24 +08:00
XiaoMoMi e3d81e7c0c optimize 2024-11-10 18:29:25 +08:00
XiaoMoMi 018dc18eee 3.0.9 2024-11-09 22:56:29 +08:00
XiaoMoMi 366cda6bef 3.0.8 2024-11-09 02:22:37 +08:00
XiaoMoMi ca70dfb2b4 3.0.8 2024-11-07 17:59:03 +08:00
XiaoMoMi d59ac96f48 3.0.7.1 2024-11-04 04:20:38 +08:00
XiaoMoMi f5d2d6a0bc 1.20.3 support 2024-11-02 20:12:38 +08:00
XiaoMoMi 7d6fba25e6 bypass other names 2024-10-30 20:00:45 +08:00
XiaoMoMi fcb045e0c8 Update gradle.properties 2024-10-28 20:54:37 +08:00
XiaoMoMi df098a053e update config version to 32 2024-10-27 01:22:11 +08:00
XiaoMoMi 2958b4e1db Merge pull request #49 from BitByLogics/main
Add creative-central resourcepack integration support
2024-10-25 21:25:33 +08:00
Justin 84a37ed619 Add creative-central resourcepack integration support 2024-10-24 22:58:27 -04:00
XiaoMoMi f66c800f3a Update NameplatesExtraExpansion.java 2024-10-24 19:52:17 +08:00
XiaoMoMi 0fb4ae37e4 3.0.6 2024-10-24 03:54:07 +08:00
XiaoMoMi 9a9add1f03 fix reset 2024-10-22 00:12:23 +08:00
XiaoMoMi 5d48530eb6 3.0.5 2024-10-21 03:08:51 +08:00
XiaoMoMi f5e33f5647 3.0.5 2024-10-18 23:43:29 +08:00
XiaoMoMi ffdc7c49ce 3.0.5 2024-10-18 23:43:17 +08:00
XiaoMoMi 46d922ab15 fix folia again 2024-10-15 01:58:53 +08:00
XiaoMoMi aee8ebf9ae increase the delay for folia 2024-10-15 01:40:11 +08:00
XiaoMoMi be086cd91f folia fix 2024-10-15 01:10:25 +08:00
XiaoMoMi 0b96cefcf2 Update BukkitCNPlayer.java 2024-10-14 03:51:13 +08:00
XiaoMoMi 8867b25307 3.0.3 2024-10-13 21:54:40 +08:00
XiaoMoMi bd6caee58f update libs 2024-10-12 21:47:58 +08:00
XiaoMoMi d0b057b391 Update BukkitCustomNameplates.java 2024-10-11 23:35:45 +08:00
XiaoMoMi 2e03248555 3.0.2 2024-10-11 23:31:02 +08:00
XiaoMoMi 5898d8e3d4 3.0.2 2024-10-11 23:11:37 +08:00
XiaoMoMi 8bcb7d3380 to fix teams 2024-10-11 05:30:24 +08:00
XiaoMoMi 59dda019d8 Update PlayerListener.java 2024-10-11 03:19:42 +08:00
XiaoMoMi 7512a97461 fix teleport 2024-10-11 03:15:44 +08:00
XiaoMoMi 63075ad1bb 3.0.1 2024-10-11 02:53:43 +08:00
XiaoMoMi 2c9bdc662a added nametag module 2024-10-10 17:09:28 +08:00
XiaoMoMi 545826801a Update HasBubbleRequirement.java 2024-10-10 16:01:10 +08:00
XiaoMoMi 26c30b8bab Merge pull request #47 from thomasz05/main
Fix HasNameplateRequirement to return false if nameplate module is di…
2024-10-10 15:59:19 +08:00
thomasz_05 c022319b67 Fix HasNameplateRequirement to return false if nameplate module is disabled 2024-10-09 23:41:09 +02:00
XiaoMoMi 37da5df7e6 beta5 2024-10-09 23:17:41 +08:00
XiaoMoMi aa919d18e0 Fix bugs 2024-10-09 22:33:22 +08:00
XiaoMoMi 7eb3296294 beta4 2024-10-09 21:38:05 +08:00
XiaoMoMi d84c9f7031 new event 2024-10-09 00:22:21 +08:00
XiaoMoMi ec840036ac Update PlaceholderManagerImpl.java 2024-10-08 23:23:15 +08:00
XiaoMoMi a168cb8591 modules 2024-10-08 23:14:33 +08:00
XiaoMoMi 8856831a94 code clean up 2024-10-08 22:53:51 +08:00
XiaoMoMi a387cf5127 beta3 2024-10-08 22:26:48 +08:00
243 changed files with 4879 additions and 990 deletions

No files matched your search

+3
View File
@@ -5,6 +5,9 @@ target/
!**/src/main/**/build/
!**/src/test/**/build/
### Unfinished ###
platforms/sponge/
### IntelliJ IDEA ###
.idea
*.iws
+11 -4
View File
@@ -11,7 +11,7 @@
![bStats Players](https://img.shields.io/bstats/players/16649)
![GitHub](https://img.shields.io/github/license/Xiao-MoMi/Custom-Nameplates)
## How to build
## How to Build
#### Command Line
Install JDK 17 & 21. \
@@ -22,10 +22,17 @@ Execute ".\gradlew build" and get the artifact under /target folder
Import the project and execute gradle build action. \
Get the artifact under /target folder
## Support the developer
## How to Contribute
Polymart: https://polymart.org/resource/customnameplates.2543 \
Afdian: https://afdian.com/@xiaomomi
#### Translations
Clone this project and create a new language file in the /common/src/main/resources/translations directory. \
Once your changes are ready, open a pull request for review. We appreciate your works!
## Support the Developer
Polymart: https://polymart.org/resource/customnameplates.2543/ \
BuiltByBit: https://builtbybit.com/resources/customnameplates.36359/ \
Afdian: https://afdian.com/@xiaomomi/
## CustomNameplates API
+3 -1
View File
@@ -6,7 +6,7 @@ plugins {
repositories {
maven("https://jitpack.io/")
maven("https://papermc.io/repo/repository/maven-public/")
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
@@ -23,6 +23,8 @@ dependencies {
compileOnly("io.netty:netty-all:4.1.113.Final")
// GSON
compileOnly("com.google.code.gson:gson:${rootProject.properties["gson_version"]}")
// Fast util
compileOnly("it.unimi.dsi:fastutil:${rootProject.properties["fastutil_version"]}")
}
java {
@@ -17,8 +17,12 @@
package net.momirealms.customnameplates.api;
import io.netty.channel.Channel;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.feature.Feature;
import net.momirealms.customnameplates.api.feature.TimeStampData;
import net.momirealms.customnameplates.api.feature.tag.TeamView;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
@@ -27,108 +31,135 @@ import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.storage.data.PlayerData;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public abstract class AbstractCNPlayer implements CNPlayer {
protected final CustomNameplates plugin;
protected final Object player;
protected final Channel channel;
private boolean isLoaded = false;
protected Object player;
private boolean isPreviewing = false;
private volatile boolean isLoaded = false;
private volatile boolean tempPreviewing = false;
private volatile boolean toggleablePreviewing = false;
private String equippedNameplate;
private String equippedBubble;
private final Map<Integer, TimeStampData<String>> cachedValues = new ConcurrentHashMap<>();
private final Map<Integer, WeakHashMap<CNPlayer, TimeStampData<String>>> cachedRelationalValues = new ConcurrentHashMap<>();
private final TeamView teamView = new TeamView();
private final Map<Requirement, TimeStampData<Boolean>> cachedRequirements = new ConcurrentHashMap<>();
private final Map<Requirement, WeakHashMap<CNPlayer, TimeStampData<Boolean>>> cachedRelationalRequirements = new ConcurrentHashMap<>();
// these maps might be visited by other threads through PlaceholderAPI
private final Map<Integer, TimeStampData<String>> cachedValues = new ConcurrentHashMap<>(128);
private final Map<Integer, WeakHashMap<CNPlayer, TimeStampData<String>>> cachedRelationalValues = new ConcurrentHashMap<>(128);
private final Map<Integer, TimeStampData<Boolean>> cachedRequirements = new ConcurrentHashMap<>(32);
private final Map<Integer, WeakHashMap<CNPlayer, TimeStampData<Boolean>>> cachedRelationalRequirements = new ConcurrentHashMap<>(32);
private final Set<Feature> activeFeatures = new CopyOnWriteArraySet<>();
private final Map<Placeholder, Set<Feature>> placeholder2Features = new ConcurrentHashMap<>();
private final Map<Feature, Set<Placeholder>> feature2Placeholders = new ConcurrentHashMap<>();
private final Map<CNPlayer, Tracker> trackers = Collections.synchronizedMap(new WeakHashMap<>());
private final Map<CNPlayer, Tracker> trackers = new WeakHashMap<>();
private final ReadWriteLock trackerLock = new ReentrantReadWriteLock();
protected AbstractCNPlayer(CustomNameplates plugin, Object player) {
protected AbstractCNPlayer(CustomNameplates plugin, Channel channel) {
this.plugin = plugin;
this.player = player;
this.channel = channel;
}
@Override
public List<Placeholder> activePlaceholdersToRefresh() {
Placeholder[] activePlaceholders = activePlaceholders();
List<Placeholder> placeholderWithChildren = new ArrayList<>();
List<Placeholder> placeholderWithChildren = new ObjectArrayList<>();
for (Placeholder placeholder : activePlaceholders) {
childrenFirstList(placeholder, placeholderWithChildren);
}
return placeholderWithChildren.stream().distinct().toList();
}
private String updatePlayerPlaceholder(PlayerPlaceholder placeholder) {
TimeStampData<String> value = getRawPlayerValue(placeholder);
if (value == null) {
value = new TimeStampData<>(placeholder.request(this), MainTask.getTicks(), true);
setPlayerValue(placeholder, value);
return value.data();
}
if (value.ticks() != MainTask.getTicks()) {
String newValue = placeholder.request(this);
value.updateTicks(!value.data().equals(newValue));
value.data(newValue);
}
return value.data();
}
private void updateRelationalPlaceholder(RelationalPlaceholder placeholder, Collection<CNPlayer> others) {
for (CNPlayer another : others) {
updateRelationalPlaceholder(placeholder, another);
}
}
private String updateRelationalPlaceholder(RelationalPlaceholder placeholder, CNPlayer another) {
TimeStampData<String> value = getRawRelationalValue(placeholder, another);
if (value == null) {
value = new TimeStampData<>(placeholder.request(this, another), MainTask.getTicks(), true);
setRelationalValue(placeholder, another, value);
return value.data();
}
if (value.ticks() != MainTask.getTicks()) {
String newValue = placeholder.request(this, another);
value.updateTicks(!value.data().equals(newValue));
value.data(newValue);
}
return value.data();
}
private String updateSharedPlaceholder(SharedPlaceholder placeholder) {
TimeStampData<String> value = getRawSharedValue(placeholder);
if (value == null) {
String latest;
if (MainTask.hasRequested(placeholder.countId())) {
latest = placeholder.getLatestValue();
} else {
latest = placeholder.request();
}
value = new TimeStampData<>(latest, MainTask.getTicks(), true);
setSharedValue(placeholder, value);
return value.data();
}
if (value.ticks() != MainTask.getTicks()) {
String latest;
if (MainTask.hasRequested(placeholder.countId())) {
latest = placeholder.getLatestValue();
} else {
latest = placeholder.request();
}
value.updateTicks(!value.data().equals(latest));
value.data(latest);
}
return value.data();
}
@Override
public void forceUpdatePlaceholders(Set<Placeholder> placeholders, Set<CNPlayer> others) {
public void forceUpdatePlaceholders(Set<Placeholder> placeholders, Collection<CNPlayer> others) {
if (placeholders.isEmpty()) return;
List<Placeholder> placeholderWithChildren = new ArrayList<>();
List<Placeholder> placeholderWithChildren = new ObjectArrayList<>();
for (Placeholder placeholder : placeholders) {
childrenFirstList(placeholder, placeholderWithChildren);
}
placeholderWithChildren = placeholderWithChildren.stream().distinct().toList();
for (Placeholder placeholder : placeholderWithChildren) {
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
TimeStampData<String> value = getValue(placeholder);
if (value == null) {
value = new TimeStampData<>(playerPlaceholder.request(this), MainTask.getTicks(), true);
setValue(placeholder, value);
continue;
}
if (value.ticks() != MainTask.getTicks()) {
String newValue = playerPlaceholder.request(this);
value.updateTicks(!value.data().equals(newValue));
value.data(newValue);
}
} else if (placeholder instanceof RelationalPlaceholder relational) {
for (CNPlayer player : others) {
TimeStampData<String> value = getRelationalValue(placeholder, player);
if (value == null) {
value = new TimeStampData<>(relational.request(this, player), MainTask.getTicks(), true);
setRelationalValue(placeholder, player, value);
continue;
}
if (value.ticks() != MainTask.getTicks()) {
String newValue = relational.request(this, player);
value.updateTicks(!value.data().equals(newValue));
value.data(newValue);
}
}
updatePlayerPlaceholder(playerPlaceholder);
} else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
updateRelationalPlaceholder(relationalPlaceholder, others);
} else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
TimeStampData<String> value = getValue(placeholder);
if (value == null) {
String latest;
if (MainTask.hasRequested(sharedPlaceholder.countId())) {
latest = sharedPlaceholder.getLatestValue();
} else {
latest = sharedPlaceholder.request();
}
value = new TimeStampData<>(latest, MainTask.getTicks(), true);
setValue(placeholder, value);
continue;
}
if (value.ticks() != MainTask.getTicks()) {
String latest;
if (MainTask.hasRequested(sharedPlaceholder.countId())) {
latest = sharedPlaceholder.getLatestValue();
} else {
latest = sharedPlaceholder.request();
}
value.updateTicks(!value.data().equals(latest));
value.data(latest);
}
updateSharedPlaceholder(sharedPlaceholder);
}
}
}
@@ -165,6 +196,15 @@ public abstract class AbstractCNPlayer implements CNPlayer {
feature2Placeholders.clear();
}
public void setPlayer(Object player) {
this.player = player;
}
@Override
public Channel channel() {
return channel;
}
@Override
public Set<Feature> activeFeatures(Placeholder placeholder) {
return placeholder2Features.getOrDefault(placeholder, Collections.emptySet());
@@ -179,13 +219,22 @@ public abstract class AbstractCNPlayer implements CNPlayer {
isLoaded = loaded;
}
public void setPreviewing(boolean previewing) {
isPreviewing = previewing;
public void setTempPreviewing(boolean previewing) {
this.tempPreviewing = previewing;
}
@Override
public boolean isPreviewing() {
return isPreviewing;
public boolean isTempPreviewing() {
return tempPreviewing;
}
public void setToggleablePreviewing(boolean previewing) {
this.toggleablePreviewing = previewing;
}
@Override
public boolean isToggleablePreviewing() {
return toggleablePreviewing;
}
@Override
@@ -196,13 +245,10 @@ public abstract class AbstractCNPlayer implements CNPlayer {
@Override
public void addFeature(Feature feature) {
activeFeatures.add(feature);
Set<Placeholder> allPlaceholdersUsedInFeature = feature.allPlaceholders();
feature2Placeholders.put(feature, allPlaceholdersUsedInFeature);
for (Placeholder placeholder : allPlaceholdersUsedInFeature) {
Set<Feature> featureSet = placeholder2Features.computeIfAbsent(placeholder, k -> {
forceUpdatePlaceholders(Set.of(placeholder), nearbyPlayers());
return new HashSet<>();
});
Set<Placeholder> allPlaceholders = feature.allPlaceholders();
feature2Placeholders.put(feature, allPlaceholders);
for (Placeholder placeholder : allPlaceholders) {
Set<Feature> featureSet = placeholder2Features.computeIfAbsent(placeholder, k -> new ObjectOpenHashSet<>());
featureSet.add(feature);
}
}
@@ -221,84 +267,98 @@ public abstract class AbstractCNPlayer implements CNPlayer {
}
@Override
public void setValue(Placeholder placeholder, TimeStampData<String> value) {
public void setPlayerValue(PlayerPlaceholder placeholder, TimeStampData<String> value) {
cachedValues.put(placeholder.countId(), value);
}
@Override
public boolean setValue(Placeholder placeholder, String value) {
TimeStampData<String> previous = cachedValues.get(placeholder.countId());
int currentTicks = MainTask.getTicks();
boolean changed = false;
if (previous != null) {
if (previous.ticks() == currentTicks) {
return false;
}
String data = previous.data();
if (!data.equals(value)) {
changed = true;
previous.data(value);
previous.updateTicks(true);
}
} else {
changed= true;
previous = new TimeStampData<>(value, currentTicks, true);
cachedValues.put(placeholder.countId(), previous);
}
return changed;
public void setSharedValue(SharedPlaceholder placeholder, TimeStampData<String> value) {
cachedValues.put(placeholder.countId(), value);
}
@Override
public void setRelationalValue(Placeholder placeholder, CNPlayer another, TimeStampData<String> value) {
public void setRelationalValue(RelationalPlaceholder placeholder, CNPlayer another, TimeStampData<String> value) {
WeakHashMap<CNPlayer, TimeStampData<String>> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
map.put(another, value);
}
// @Override
// public boolean setPlayerValue(PlayerPlaceholder placeholder, String value) {
// TimeStampData<String> previous = cachedValues.get(placeholder.countId());
// int currentTicks = MainTask.getTicks();
// boolean changed = false;
// if (previous != null) {
// if (previous.ticks() == currentTicks) {
// return false;
// }
// String data = previous.data();
// if (!data.equals(value)) {
// changed = true;
// previous.data(value);
// previous.updateTicks(true);
// }
// } else {
// changed= true;
// previous = new TimeStampData<>(value, currentTicks, true);
// cachedValues.put(placeholder.countId(), previous);
// }
// return changed;
// }
// @Override
// public boolean setRelationalValue(RelationalPlaceholder placeholder, CNPlayer another, String value) {
// WeakHashMap<CNPlayer, TimeStampData<String>> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
// TimeStampData<String> previous = map.get(another);
// int currentTicks = MainTask.getTicks();
// boolean changed = false;
// if (previous != null) {
// if (previous.ticks() == currentTicks) {
// return false;
// }
// String data = previous.data();
// if (!data.equals(value)) {
// changed = true;
// previous.data(value);
// previous.updateTicks(true);
// }
// } else {
// changed= true;
// previous = new TimeStampData<>(value, currentTicks, true);
// map.put(another, previous);
// }
// return changed;
// }
@Override
public boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value) {
WeakHashMap<CNPlayer, TimeStampData<String>> map = cachedRelationalValues.computeIfAbsent(placeholder.countId(), k -> new WeakHashMap<>());
TimeStampData<String> previous = map.get(another);
int currentTicks = MainTask.getTicks();
boolean changed = false;
if (previous != null) {
if (previous.ticks() == currentTicks) {
return false;
}
String data = previous.data();
if (!data.equals(value)) {
changed = true;
previous.data(value);
previous.updateTicks(true);
}
} else {
changed= true;
previous = new TimeStampData<>(value, currentTicks, true);
map.put(another, previous);
}
return changed;
public @NotNull String getCachedSharedValue(SharedPlaceholder placeholder) {
return updateSharedPlaceholder(placeholder);
}
@Override
public @NotNull String getData(Placeholder placeholder) {
return Optional.ofNullable(cachedValues.get(placeholder.countId())).map(TimeStampData::data).orElse(placeholder.id());
public @NotNull String getCachedPlayerValue(PlayerPlaceholder placeholder) {
return updatePlayerPlaceholder(placeholder);
}
@Override
public TimeStampData<String> getValue(Placeholder placeholder) {
public @NotNull String getCachedRelationalValue(RelationalPlaceholder placeholder, CNPlayer another) {
return updateRelationalPlaceholder(placeholder, another);
}
@Nullable
@Override
public TimeStampData<String> getRawPlayerValue(PlayerPlaceholder placeholder) {
return cachedValues.get(placeholder.countId());
}
@Nullable
@Override
public @NotNull String getRelationalData(Placeholder placeholder, CNPlayer another) {
WeakHashMap<CNPlayer, TimeStampData<String>> map = cachedRelationalValues.get(placeholder.countId());
if (map == null) {
return placeholder.id();
}
return Optional.ofNullable(map.get(another)).map(TimeStampData::data).orElse(placeholder.id());
public TimeStampData<String> getRawSharedValue(SharedPlaceholder placeholder) {
return cachedValues.get(placeholder.countId());
}
@Nullable
@Override
public TimeStampData<String> getRelationalValue(Placeholder placeholder, CNPlayer another) {
public TimeStampData<String> getRawRelationalValue(RelationalPlaceholder placeholder, CNPlayer another) {
WeakHashMap<CNPlayer, TimeStampData<String>> map = cachedRelationalValues.get(placeholder.countId());
if (map == null) {
return null;
@@ -308,7 +368,7 @@ public abstract class AbstractCNPlayer implements CNPlayer {
@Override
public Placeholder[] activePlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
Set<Placeholder> placeholders = new ObjectOpenHashSet<>();
for (Feature feature : activeFeatures) {
placeholders.addAll(feature.activePlaceholders());
}
@@ -319,7 +379,7 @@ public abstract class AbstractCNPlayer implements CNPlayer {
public boolean isMet(Requirement[] requirements) {
int currentTicks = MainTask.getTicks();
for (Requirement requirement : requirements) {
TimeStampData<Boolean> data = cachedRequirements.get(requirement);
TimeStampData<Boolean> data = cachedRequirements.get(requirement.countId());
if (data != null) {
if (data.ticks() + requirement.refreshInterval() > currentTicks) {
if (!data.data()) {
@@ -336,7 +396,7 @@ public abstract class AbstractCNPlayer implements CNPlayer {
} else {
boolean satisfied = requirement.isSatisfied(this, this);
data = new TimeStampData<>(satisfied, currentTicks, true);
cachedRequirements.put(requirement, data);
cachedRequirements.put(requirement.countId(), data);
if (!satisfied) {
return false;
}
@@ -349,7 +409,7 @@ public abstract class AbstractCNPlayer implements CNPlayer {
public boolean isMet(CNPlayer another, Requirement[] requirements) {
int currentTicks = MainTask.getTicks();
for (Requirement requirement : requirements) {
WeakHashMap<CNPlayer, TimeStampData<Boolean>> innerMap = cachedRelationalRequirements.computeIfAbsent(requirement, k -> new WeakHashMap<>());
WeakHashMap<CNPlayer, TimeStampData<Boolean>> innerMap = cachedRelationalRequirements.computeIfAbsent(requirement.countId(), k -> new WeakHashMap<>());
TimeStampData<Boolean> data = innerMap.get(another);
if (data != null) {
if (data.ticks() + requirement.refreshInterval() > currentTicks) {
@@ -378,58 +438,90 @@ public abstract class AbstractCNPlayer implements CNPlayer {
@Override
public Tracker addPlayerToTracker(CNPlayer another) {
Tracker tracker = trackers.get(another);
if (tracker != null) {
return tracker;
}
tracker = new Tracker(another);
trackers.put(another, tracker);
for (Placeholder placeholder : activePlaceholders()) {
if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
String value = relationalPlaceholder.request(this, another);
setRelationalValue(placeholder, another, value);
trackerLock.writeLock().lock();
try {
Tracker tracker = trackers.get(another);
if (tracker != null) {
return tracker;
}
tracker = new Tracker(another);
trackers.put(another, tracker);
return tracker;
} finally {
trackerLock.writeLock().unlock();
}
return tracker;
}
@Override
public void removePlayerFromTracker(CNPlayer another) {
trackers.remove(another);
trackerLock.writeLock().lock();
try {
trackers.remove(another);
} finally {
trackerLock.writeLock().unlock();
}
}
@Override
public Set<CNPlayer> nearbyPlayers() {
return new HashSet<>(trackers.keySet());
public Collection<CNPlayer> nearbyPlayers() {
trackerLock.readLock().lock();
try {
// Create a snapshot of keys to avoid concurrent modification
return new ObjectArrayList<>(trackers.keySet());
} finally {
trackerLock.readLock().unlock();
}
}
@Override
public void trackPassengers(CNPlayer another, int... passengers) {
Tracker tracker = trackers.get(another);
if (tracker != null) {
for (int passenger : passengers) {
tracker.addPassengerID(passenger);
}
}
trackerLock.writeLock().lock();
try {
Tracker tracker = trackers.get(another);
if (tracker != null) {
for (int passenger : passengers) {
tracker.addPassengerID(passenger);
}
}
} finally {
trackerLock.writeLock().unlock();
}
}
@Override
public void untrackPassengers(CNPlayer another, int... passengers) {
Optional.ofNullable(trackers.get(another)).ifPresent(tracker -> {
for (int passenger : passengers) {
tracker.removePassengerID(passenger);
trackerLock.writeLock().lock();
try {
Tracker tracker = trackers.get(another);
if (tracker != null) {
for (int passenger : passengers) {
tracker.removePassengerID(passenger);
}
}
});
} finally {
trackerLock.writeLock().unlock();
}
}
@Override
public Set<Integer> getTrackedPassengerIds(CNPlayer another) {
return Optional.ofNullable(trackers.get(another)).map(Tracker::getPassengerIDs).orElse(new HashSet<>());
trackerLock.readLock().lock();
try {
Tracker tracker = trackers.get(another);
return tracker != null ? tracker.getPassengerIDs() : new ObjectOpenHashSet<>();
} finally {
trackerLock.readLock().unlock();
}
}
@Override
public Tracker getTracker(CNPlayer another) {
return trackers.get(another);
trackerLock.readLock().lock();
try {
return trackers.get(another);
} finally {
trackerLock.readLock().unlock();
}
}
@Override
@@ -468,9 +560,15 @@ public abstract class AbstractCNPlayer implements CNPlayer {
.uuid(uuid())
.nameplate(equippedNameplate())
.bubble(equippedBubble())
.previewTags(isToggleablePreviewing())
.build(), plugin.getScheduler().async());
}
@Override
public TeamView teamView() {
return teamView;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
@@ -17,15 +17,21 @@
package net.momirealms.customnameplates.api;
import io.netty.channel.Channel;
import net.momirealms.customnameplates.api.feature.Feature;
import net.momirealms.customnameplates.api.feature.TimeStampData;
import net.momirealms.customnameplates.api.feature.tag.TeamView;
import net.momirealms.customnameplates.api.network.Tracker;
import net.momirealms.customnameplates.api.placeholder.Placeholder;
import net.momirealms.customnameplates.api.placeholder.PlayerPlaceholder;
import net.momirealms.customnameplates.api.placeholder.RelationalPlaceholder;
import net.momirealms.customnameplates.api.placeholder.SharedPlaceholder;
import net.momirealms.customnameplates.api.requirement.Requirement;
import net.momirealms.customnameplates.api.util.Vector3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
@@ -53,6 +59,13 @@ public interface CNPlayer {
*/
Object player();
/**
* Returns the channel of the player
*
* @return the channel
*/
Channel channel();
/**
* Returns the entity ID of the player.
*
@@ -115,7 +128,14 @@ public interface CNPlayer {
*
* @return true if the player is previewing, false otherwise
*/
boolean isPreviewing();
boolean isTempPreviewing();
/**
* Checks if the player has turned their nameplate on
*
* @return true if the player has turned their nameplate on, false otherwise
*/
boolean isToggleablePreviewing();
/**
* Checks if the player has the specified permission.
@@ -174,7 +194,7 @@ public interface CNPlayer {
* @param placeholders the placeholders to update
* @param another the players related to the placeholders
*/
void forceUpdatePlaceholders(Set<Placeholder> placeholders, Set<CNPlayer> another);
void forceUpdatePlaceholders(Set<Placeholder> placeholders, Collection<CNPlayer> another);
/**
* Retrieves the cached data for a given placeholder.
@@ -183,16 +203,16 @@ public interface CNPlayer {
* @return the cached data as a string
*/
@NotNull
String getData(Placeholder placeholder);
String getCachedSharedValue(SharedPlaceholder placeholder);
/**
* Retrieves the cached {@link TimeStampData} for a given placeholder.
* Retrieves the cached data for a given placeholder.
*
* @param placeholder the placeholder to retrieve data for
* @return the cached TickStampData, or null if none exists
* @return the cached data as a string
*/
@Nullable
TimeStampData<String> getValue(Placeholder placeholder);
@NotNull
String getCachedPlayerValue(PlayerPlaceholder placeholder);
/**
* Retrieves the cached relational data between this player and another for a given placeholder.
@@ -202,7 +222,25 @@ public interface CNPlayer {
* @return the relational data as a string
*/
@NotNull
String getRelationalData(Placeholder placeholder, CNPlayer another);
String getCachedRelationalValue(RelationalPlaceholder placeholder, CNPlayer another);
/**
* Retrieves the cached {@link TimeStampData} for a given placeholder.
*
* @param placeholder the placeholder to retrieve data for
* @return the cached TickStampData, or null if none exists
*/
@Nullable
TimeStampData<String> getRawPlayerValue(PlayerPlaceholder placeholder);
/**
* Retrieves the cached {@link TimeStampData} for a given placeholder.
*
* @param placeholder the placeholder to retrieve data for
* @return the cached TickStampData, or null if none exists
*/
@Nullable
TimeStampData<String> getRawSharedValue(SharedPlaceholder placeholder);
/**
* Retrieves the cached relational {@link TimeStampData} for a given placeholder.
@@ -212,7 +250,7 @@ public interface CNPlayer {
* @return the cached relational TickStampData, or null if none exists
*/
@Nullable
TimeStampData<String> getRelationalValue(Placeholder placeholder, CNPlayer another);
TimeStampData<String> getRawRelationalValue(RelationalPlaceholder placeholder, CNPlayer another);
/**
* Caches the specified {@link TimeStampData} for the given placeholder.
@@ -220,16 +258,15 @@ public interface CNPlayer {
* @param placeholder the placeholder to cache
* @param value the value to cache
*/
void setValue(Placeholder placeholder, TimeStampData<String> value);
void setPlayerValue(PlayerPlaceholder placeholder, TimeStampData<String> value);
/**
* Caches the specified value for the given placeholder.
* Caches the specified {@link TimeStampData} for the given placeholder.
*
* @param placeholder the placeholder to cache
* @param value the value to cache
* @return true if the value was changed, false otherwise
*/
boolean setValue(Placeholder placeholder, String value);
void setSharedValue(SharedPlaceholder placeholder, TimeStampData<String> value);
/**
* Caches the specified relational {@link TimeStampData} for a given placeholder and player.
@@ -238,17 +275,26 @@ public interface CNPlayer {
* @param another the other player
* @param value the value to cache
*/
void setRelationalValue(Placeholder placeholder, CNPlayer another, TimeStampData<String> value);
void setRelationalValue(RelationalPlaceholder placeholder, CNPlayer another, TimeStampData<String> value);
/**
* Caches the specified relational value for a given placeholder and player.
*
* @param placeholder the relational placeholder
* @param another the other player
* @param value the value to cache
* @return true if the value was changed, false otherwise
*/
boolean setRelationalValue(Placeholder placeholder, CNPlayer another, String value);
// /**
// * Caches the specified value for the given placeholder.
// *
// * @param placeholder the placeholder to cache
// * @param value the value to cache
// * @return true if the value was changed, false otherwise
// */
// boolean setPlayerValue(PlayerPlaceholder placeholder, String value);
// /**
// * Caches the specified relational value for a given placeholder and player.
// *
// * @param placeholder the relational placeholder
// * @param another the other player
// * @param value the value to cache
// * @return true if the value was changed, false otherwise
// */
// boolean setRelationalValue(RelationalPlaceholder placeholder, CNPlayer another, String value);
/**
* Adds a feature to the player
@@ -301,7 +347,7 @@ public interface CNPlayer {
*
* @return the set of nearby players
*/
Set<CNPlayer> nearbyPlayers();
Collection<CNPlayer> nearbyPlayers();
/**
* Adds passenger entities to the tracker for another player.
@@ -378,4 +424,18 @@ public interface CNPlayer {
* Save the player's current nameplate/bubble to database
*/
void save();
/**
* Get the player's team view
*
* @return team view
*/
TeamView teamView();
/**
* Check if the player is initialized
*
* @return initialized or not
*/
boolean isInitialized();
}
@@ -60,16 +60,20 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
protected boolean debug;
protected boolean checkUpdate;
protected boolean metrics;
protected boolean nametagModule;
protected boolean actionbarModule;
protected boolean imageModule;
protected boolean bossBarModule;
protected boolean bubbleModule;
protected boolean nameplateModule;
protected boolean backgroundModule;
protected boolean hideTeamNames;
protected int defaultPlaceholderRefreshInterval;
protected int defaultConditionRefreshInterval;
protected int delaySend;
protected boolean catchOtherActionBar;
protected boolean displaySystemChat;
protected int otherActionBarStayTime;
protected String namespace;
protected String font;
@@ -98,6 +102,8 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
protected boolean packItemsAdder;
protected boolean packItemsAdderLegacy;
protected boolean packOraxen;
protected boolean packNexo;
protected boolean packCreativeCentral;
protected boolean chatUnsafe;
protected boolean chatTR;
@@ -106,6 +112,10 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
protected boolean chatCarbon;
protected boolean chatAdvanced;
protected boolean chatEss;
protected boolean chatChatControlRed;
protected boolean chatChatty;
protected String configVersion;
public ConfigManager(CustomNameplates plugin) {
this.plugin = plugin;
@@ -114,42 +124,39 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
@Override
public void load() {
String configVersion = CustomNameplatesProperties.getValue("config");
try (InputStream inputStream = new FileInputStream(resolveConfig("config.yml").toFile())) {
MAIN_CONFIG = YamlDocument.create(
inputStream,
plugin.getResourceStream("config.yml"),
GeneralSettings.builder()
.setRouteSeparator('.')
.setUseDefaults(false)
.build(),
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.builder()
.setEscapeUnprintable(false)
.setScalarFormatter((tag, value, role, def) -> {
if (role == NodeRole.KEY) {
return ScalarStyle.PLAIN;
} else {
return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
}
})
.build(),
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(configVersion, "other-settings.placeholder-refresh-interval", '.')
.addIgnoredRoute(configVersion, "other-settings.font-templates", '.')
.addIgnoredRoute(configVersion, "other-settings.shift-fonts", '.')
.build()
);
configVersion = CustomNameplatesProperties.getValue("config");
MAIN_CONFIG = loadConfig("config.yml",
GeneralSettings.builder()
.setRouteSeparator('.')
.setUseDefaults(false)
.build(),
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.builder()
.setEscapeUnprintable(false)
.setScalarFormatter((tag, value, role, def) -> {
if (role == NodeRole.KEY) {
return ScalarStyle.PLAIN;
} else {
return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
}
})
.build(),
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(configVersion, "other-settings.placeholder-refresh-interval", '.')
.addIgnoredRoute(configVersion, "other-settings.font-templates", '.')
.addIgnoredRoute(configVersion, "other-settings.shift-fonts", '.')
.build());
try {
MAIN_CONFIG.save(resolveConfig("config.yml").toFile());
loadSettings();
} catch (IOException e) {
throw new RuntimeException(e);
}
loadSettings();
}
private void loadSettings() {
@@ -163,6 +170,7 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
checkUpdate = config.getBoolean("update-checker", false);
// Modules
nametagModule = config.getBoolean("modules.nametags", true);
actionbarModule = config.getBoolean("modules.actionbars", true);
imageModule = config.getBoolean("modules.images", true);
bossBarModule = config.getBoolean("modules.bossbars", true);
@@ -175,6 +183,8 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
packItemsAdderLegacy = config.getBoolean("integrations.resource-pack.ItemsAdder-old-method", false);
if (packItemsAdder) packItemsAdderLegacy = false;
packOraxen = config.getBoolean("integrations.resource-pack.Oraxen", false);
packCreativeCentral = config.getBoolean("integrations.resource-pack.Creative-Central");
packNexo = config.getBoolean("integrations.resource-pack.Nexo");
chatUnsafe = config.getBoolean("other-settings.unsafe-chat-event", false);
chatEss = config.getBoolean("integrations.chat.Essentials", false);
@@ -183,6 +193,8 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
chatAdvanced = config.getBoolean("integrations.chat.AdvancedChat", false);
chatTR = config.getBoolean("integrations.chat.TrChat", false);
chatVenture = config.getBoolean("integrations.chat.VentureChat", false);
chatChatControlRed = config.getBoolean("integrations.chat.ChatControlRed", false);
chatChatty = config.getBoolean("integrations.chat.Chatty", false);
// Packs
generateOnStart = !config.getBoolean("resource-pack.disable-generation-on-start", false);
@@ -211,8 +223,11 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
// Other settings
delaySend = config.getInt("other-settings.send-delay", 0);
defaultPlaceholderRefreshInterval = config.getInt("other-settings.default-placeholder-refresh-interval", 1);
defaultConditionRefreshInterval = config.getInt("other-settings.ddefault-condition-refresh-interval", 1);
defaultConditionRefreshInterval = config.getInt("other-settings.default-condition-refresh-interval", 20);
catchOtherActionBar = config.getBoolean("other-settings.catch-other-plugin-actionbar", true);
otherActionBarStayTime = config.getInt("other-settings.other-actionbar-stay-time", 3000);
displaySystemChat = config.getBoolean("other-settings.display-system-actionbar", true);
hideTeamNames = config.getBoolean("other-settings.hide-team-names", true);
}
@Override
@@ -256,18 +271,34 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
return instance.font;
}
public static String configVersion() {
return instance.configVersion;
}
public static char initialChar() {
return instance.initialChar;
}
public static boolean hideTeamNames() {
return instance.hideTeamNames;
}
public static boolean actionbarModule() {
return instance.actionbarModule;
}
public static boolean nametagModule() {
return instance.nametagModule;
}
public static boolean catchOtherActionBar() {
return instance.catchOtherActionBar;
}
public static boolean displaySystemChat() {
return instance.displaySystemChat;
}
public static boolean packItemsAdder() {
return instance.packItemsAdder;
}
@@ -280,6 +311,12 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
return instance.packOraxen;
}
public static boolean packNexo() {
return instance.packNexo;
}
public static boolean packCreativeCentral() { return instance.packCreativeCentral; }
public static boolean hideScoreBoardNumber() {
return instance.hideScoreBoardNumber;
}
@@ -336,6 +373,10 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
return instance.defaultConditionRefreshInterval;
}
public static int otherActionBarStayTime() {
return instance.otherActionBarStayTime;
}
public static boolean enableShader() {
return instance.enableShader;
}
@@ -380,6 +421,14 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
return instance.chatAdvanced;
}
public static boolean chatChatControlRed() {
return instance.chatChatControlRed;
}
public static boolean chatChatty() {
return instance.chatChatty;
}
@Override
public YamlDocument loadConfig(String filePath) {
return loadConfig(filePath, '.');
@@ -416,6 +465,23 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
}
}
@Override
public YamlDocument loadConfig(String filePath, GeneralSettings generalSettings, LoaderSettings loaderSettings, DumperSettings dumperSettings, UpdaterSettings updaterSettings) {
try (InputStream inputStream = new FileInputStream(resolveConfig(filePath).toFile())) {
return YamlDocument.create(
inputStream,
plugin.getResourceStream(filePath),
generalSettings,
loaderSettings,
dumperSettings,
updaterSettings
);
} catch (IOException e) {
plugin.getPluginLogger().severe("Failed to load config " + filePath, e);
throw new RuntimeException(e);
}
}
@Override
public YamlDocument loadData(File file) {
try (InputStream inputStream = new FileInputStream(file)) {
@@ -426,7 +492,7 @@ public abstract class ConfigManager implements ConfigLoader, Reloadable {
}
}
protected Path resolveConfig(String filePath) {
public Path resolveConfig(String filePath) {
if (filePath == null || filePath.isEmpty()) {
throw new IllegalArgumentException("ResourcePath cannot be null or empty");
}
@@ -17,6 +17,8 @@
package net.momirealms.customnameplates.api;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.momirealms.customnameplates.api.feature.actionbar.ActionBarManager;
import net.momirealms.customnameplates.api.feature.advance.AdvanceManager;
import net.momirealms.customnameplates.api.feature.background.BackgroundManager;
@@ -27,6 +29,7 @@ import net.momirealms.customnameplates.api.feature.image.ImageManager;
import net.momirealms.customnameplates.api.feature.nameplate.NameplateManager;
import net.momirealms.customnameplates.api.feature.pack.ResourcePackManager;
import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager;
import net.momirealms.customnameplates.api.helper.VersionHelper;
import net.momirealms.customnameplates.api.network.PacketSender;
import net.momirealms.customnameplates.api.network.PipelineInjector;
import net.momirealms.customnameplates.api.placeholder.PlaceholderManager;
@@ -39,8 +42,6 @@ import net.momirealms.customnameplates.common.plugin.NameplatesPlugin;
import net.momirealms.customnameplates.common.plugin.scheduler.SchedulerTask;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Consumer;
@@ -66,10 +67,10 @@ public abstract class CustomNameplates implements NameplatesPlugin {
protected BossBarManager bossBarManager;
protected UnlimitedTagManager unlimitedTagManager;
protected Platform platform;
protected MainTask mainTask = new MainTask(this);
protected MainTask mainTask;
protected SchedulerTask scheduledMainTask;
protected ConcurrentHashMap<UUID, CNPlayer> onlinePlayerMap = new ConcurrentHashMap<>();
protected HashMap<Integer, CNPlayer> entityIDFastLookup = new HashMap<>();
protected Int2ObjectOpenHashMap<CNPlayer> entityIDFastLookup = new Int2ObjectOpenHashMap<>();
protected AdvanceManager advanceManager;
protected BackgroundManager backgroundManager;
protected EventManager eventManager;
@@ -79,6 +80,14 @@ public abstract class CustomNameplates implements NameplatesPlugin {
protected ImageManager imageManager;
protected NameplateManager nameplateManager;
protected ResourcePackManager resourcePackManager;
protected CustomNameplatesAPI api;
private String buildByBit = "%%__BUILTBYBIT__%%";
private String polymart = "%%__POLYMART__%%";
private String time = "%%__TIMESTAMP__%%";
private String user = "%%__USER__%%";
private String username = "%%__USERNAME__%%";
private boolean isLatest = false;
protected CustomNameplates() {
instance = this;
@@ -96,6 +105,36 @@ public abstract class CustomNameplates implements NameplatesPlugin {
@Override
public abstract void disable();
@Override
public void enable() {
boolean downloadFromPolymart = polymart.equals("1");
boolean downloadFromBBB = buildByBit.equals("true");
if (ConfigManager.checkUpdate()) {
VersionHelper.UPDATE_CHECKER.apply(this).thenAccept(result -> {
String link;
if (downloadFromPolymart) {
link = "https://polymart.org/resource/2543/";
} else if (downloadFromBBB) {
link = "https://builtbybit.com/resources/36359/";
} else {
link = "https://github.com/Xiao-MoMi/Custom-Nameplates/";
}
if (!result) {
this.getPluginLogger().info("You are using the latest version.");
isLatest = true;
} else {
this.getPluginLogger().warn("Update is available: " + link);
isLatest = false;
}
});
}
}
@Override
public boolean isUpToDate() {
return isLatest;
}
/**
* Logs debug messages through the provided supplier.
*
@@ -204,6 +243,7 @@ public abstract class CustomNameplates implements NameplatesPlugin {
*
* @return the {@link EventManager} instance
*/
@Override
public EventManager getEventManager() {
return eventManager;
}
@@ -262,6 +302,15 @@ public abstract class CustomNameplates implements NameplatesPlugin {
return resourcePackManager;
}
/**
* Get the API class
*
* @return api
*/
public CustomNameplatesAPI getAPI() {
return api;
}
/**
* Returns the platform object for accessing platform-specific functionality.
*
@@ -277,7 +326,7 @@ public abstract class CustomNameplates implements NameplatesPlugin {
* @return a collection of {@link CNPlayer} instances
*/
public Collection<CNPlayer> getOnlinePlayers() {
return new HashSet<>(onlinePlayerMap.values());
return new ObjectArrayList<>(onlinePlayerMap.values());
}
/**
@@ -0,0 +1,167 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api;
import net.momirealms.customnameplates.api.feature.AdaptiveImage;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import net.momirealms.customnameplates.api.feature.background.Background;
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import net.momirealms.customnameplates.api.placeholder.internal.StaticPosition;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Optional;
import java.util.UUID;
public record CustomNameplatesAPI(CustomNameplates plugin) {
private static CustomNameplatesAPI instance;
public CustomNameplatesAPI(CustomNameplates plugin) {
this.plugin = plugin;
instance = this;
}
/**
* Gets the CustomNameplates plugin instance.
*
* @return the CustomNameplates plugin
*/
@Override
public CustomNameplates plugin() {
return plugin;
}
/**
* Retrieves a player by their UUID.
*
* @param uuid the player's UUID
* @return the CNPlayer object if found, otherwise null
*/
@Nullable
public CNPlayer getPlayer(UUID uuid) {
return plugin.getPlayer(uuid);
}
/**
* Retrieves a Background by its ID.
*
* @param id the background ID
* @return an Optional containing the Background if found, or empty if not
*/
@NotNull
public Optional<Background> getBackground(@NotNull String id) {
return Optional.ofNullable(plugin.getBackgroundManager().backgroundById(id));
}
/**
* Retrieves a Nameplate by its ID.
*
* @param id the nameplate ID
* @return an Optional containing the Nameplate if found, or empty if not
*/
@NotNull
public Optional<Nameplate> getNameplate(@NotNull String id) {
return Optional.ofNullable(plugin.getNameplateManager().nameplateById(id));
}
/**
* Retrieves a Bubble by its ID.
*
* @param id the bubble ID
* @return an Optional containing the Bubble if found, or empty if not
*/
@NotNull
public Optional<Bubble> getBubble(@NotNull String id) {
return Optional.ofNullable(plugin.getBubbleManager().bubbleById(id));
}
/**
* Creates a text string with an image, applying margins around the image.
*
* @param text the text to be displayed
* @param adaptiveImage the image to insert with the text
* @param leftMargin the margin to apply on the left of the image
* @param rightMargin the margin to apply on the right of the image
* @return the resulting string with the image and margins
*/
@NotNull
public String createTextWithImage(@NotNull String text, @NotNull AdaptiveImage adaptiveImage, float leftMargin, float rightMargin) {
if (AdventureHelper.legacySupport) {
text = AdventureHelper.legacyToMiniMessage(text);
}
float advance = plugin.getAdvanceManager().getLineAdvance(text);
return adaptiveImage.createImagePrefix(advance, leftMargin, rightMargin) + text + adaptiveImage.createImageSuffix(advance, leftMargin, rightMargin);
}
/**
* Gets the width advance (text length in visual representation) for a given text.
*
* @param text the text to measure
* @return the advance width of the text
*/
public float getTextAdvance(@NotNull String text) {
return plugin.getAdvanceManager().getLineAdvance(text);
}
/**
* Creates a statically positioned text, aligning it to the left, right, or middle
* of the specified width.
*
* @param text the text to be positioned
* @param width the total width for the text positioning
* @param position the desired static position (LEFT, RIGHT, MIDDLE)
* @return the resulting string with the appropriate positioning
*/
@NotNull
public String createStaticText(@NotNull String text, int width, @NotNull StaticPosition position) {
float parsedWidth = CustomNameplates.getInstance().getAdvanceManager().getLineAdvance(text);
switch (position) {
case LEFT -> {
return text + AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth));
}
case RIGHT -> {
return AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth)) + text;
}
case MIDDLE -> {
int half = (int) ((width - parsedWidth) / 2);
String left = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(half));
String right = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth - half));
return left + text + right;
}
default -> {
return "";
}
}
}
/**
* Returns the singleton instance of CustomNameplatesAPI.
* Throws an exception if the API has not been initialized.
*
* @return the CustomNameplatesAPI instance
*/
public static CustomNameplatesAPI getInstance() {
if (instance == null) {
throw new RuntimeException("Nameplates API has not been initialized yet.");
}
return instance;
}
}
@@ -17,8 +17,9 @@
package net.momirealms.customnameplates.api;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -29,10 +30,11 @@ public class MainTask implements Runnable {
private static final Map<Integer, Integer> TIME_1 = new ConcurrentHashMap<>(2048);
private static final Map<Integer, Integer> TIME_2 = new ConcurrentHashMap<>(2048);
private static final Set<Integer> requestedSharedPlaceholders = Collections.synchronizedSet(new HashSet<>());
private static final Set<Integer> requestedSharedPlaceholders = Collections.synchronizedSet(new ObjectOpenHashSet<>());
private int timer;
private final CustomNameplates plugin;
private boolean state;
public MainTask(CustomNameplates plugin) {
this.plugin = plugin;
@@ -52,24 +54,31 @@ public class MainTask implements Runnable {
@Override
public void run() {
RUN_TICKS++;
requestedSharedPlaceholders.clear();
long time1 = System.nanoTime();
plugin.actionBarManager.refreshConditions();
plugin.bossBarManager.onTick();
plugin.unlimitedTagManager.onTick();
long time2 = System.nanoTime();
plugin.placeholderManager.refreshPlaceholders();
long time3 = System.nanoTime();
plugin.actionBarManager.checkHeartBeats();
int diff1 = (int) (time2 - time1);
TIME_1.put(timer, diff1);
int diff2 = (int) (time3 - time2);
TIME_2.put(timer, diff2);
timer++;
if (timer >= 1200) timer = 0;
if (RUN_TICKS < 0) {
CustomNameplates.getInstance().reload();
// we should skip the task if the server is heavily loaded
if (this.state) return;
this.state = true;
try {
RUN_TICKS++;
requestedSharedPlaceholders.clear();
long time1 = System.nanoTime();
plugin.actionBarManager.refreshConditions();
plugin.bossBarManager.onTick();
plugin.unlimitedTagManager.onTick();
long time2 = System.nanoTime();
plugin.placeholderManager.refreshPlaceholders();
long time3 = System.nanoTime();
plugin.actionBarManager.checkHeartBeats();
int diff1 = (int) (time2 - time1);
TIME_1.put(timer, diff1);
int diff2 = (int) (time3 - time2);
TIME_2.put(timer, diff2);
timer++;
if (timer >= 1200) timer = 0;
if (RUN_TICKS < 0) {
CustomNameplates.getInstance().reload();
}
} finally {
this.state = false;
}
}
@@ -222,4 +222,15 @@ public interface Platform {
* @return the remove entity packet object
*/
Object removeEntityPacket(int... entityID);
/**
* Gets the biome based on location
*
* @param world world
* @param x x
* @param y y
* @param z z
* @return the biome key
*/
String getBiome(String world, int x, int y, int z);
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature;
import net.momirealms.customnameplates.api.CNPlayer;
/**
* Listener for handling player actions
*/
public interface PlayerListener {
/**
* Called when a player changes the world
*
* @param player the player who changes the world
*/
void onChangeWorld(CNPlayer player);
/**
* Called when a player respawns
*
* @param player the player who respawns
*/
void onRespawn(CNPlayer player);
/**
* Called when a player teleports
*
* @param player the player who respawns
*/
void onTeleport(CNPlayer player);
}
@@ -17,29 +17,31 @@
package net.momirealms.customnameplates.api.feature;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.placeholder.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
public class PreParsedDynamicText {
private final String text;
private final List<Function<CNPlayer, Function<CNPlayer, String>>> textFunctions = new ArrayList<>();
private final Set<Placeholder> set = new HashSet<>();
private final List<Function<CNPlayer, Function<CNPlayer, String>>> textFunctions = new ObjectArrayList<>();
private final Set<Placeholder> set = new ObjectOpenHashSet<>();
private boolean init = false;
public PreParsedDynamicText(String text) {
this.text = text;
this.text = Objects.requireNonNull(text);
}
public PreParsedDynamicText(String text, boolean init) {
this.text = text;
this.text = Objects.requireNonNull(text);
if (init) init();
}
@@ -53,18 +55,24 @@ public class PreParsedDynamicText {
for (String id : detectedPlaceholders) {
Placeholder placeholder = manager.getPlaceholder(id);
placeholders.add(placeholder);
if (placeholder instanceof RelationalPlaceholder) {
convertor.add((owner) -> (viewer) -> owner.getRelationalData(placeholder, viewer));
if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
convertor.add((owner) -> (viewer) -> owner.getCachedRelationalValue(relationalPlaceholder, viewer));
} else if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
convertor.add((owner) -> (viewer) -> {
if (owner != null) {
return owner.getData(placeholder);
return owner.getCachedPlayerValue(playerPlaceholder);
} else {
return playerPlaceholder.request(null);
}
});
} else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
convertor.add((owner) -> (viewer) -> sharedPlaceholder.getLatestValue());
convertor.add((owner) -> (viewer) -> {
if (owner != null) {
return owner.getCachedSharedValue(sharedPlaceholder);
} else {
return sharedPlaceholder.request();
}
});
} else {
convertor.add((owner) -> (viewer) -> id);
}
@@ -89,12 +97,12 @@ public class PreParsedDynamicText {
String remaining = original0.substring(lastIndex);
textFunctions.add((owner) -> (viewer) -> remaining);
}
// To optimize the tree height, call new HashSet twice here
set.addAll(new HashSet<>(placeholders));
// To optimize the tree height
set.addAll(new ObjectArrayList<>(placeholders));
}
public DynamicText fastCreate(CNPlayer player) {
List<Function<CNPlayer, String>> functions = new ArrayList<>();
List<Function<CNPlayer, String>> functions = new ObjectArrayList<>();
for (Function<CNPlayer, Function<CNPlayer, String>> textFunction : textFunctions) {
functions.add(textFunction.apply(player));
}
@@ -18,8 +18,8 @@
package net.momirealms.customnameplates.api.feature.advance;
import com.google.gson.JsonObject;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -28,11 +28,11 @@ import static java.util.Objects.requireNonNull;
public class CharacterFontAdvanceDataImpl implements CharacterFontAdvanceData {
private final HashMap<Integer, Float> data;
private final Map<Integer, Float> data;
private final String id;
private final Function<Map<String, Object>, List<JsonObject>> fontProviderFunction;
public CharacterFontAdvanceDataImpl(String id, HashMap<Integer, Float> data, Function<Map<String, Object>, List<JsonObject>> fontProviderFunction) {
public CharacterFontAdvanceDataImpl(String id, Map<Integer, Float> data, Function<Map<String, Object>, List<JsonObject>> fontProviderFunction) {
this.data = data;
this.id = requireNonNull(id);
this.fontProviderFunction = requireNonNull(fontProviderFunction);
@@ -65,7 +65,7 @@ public class CharacterFontAdvanceDataImpl implements CharacterFontAdvanceData {
public static class BuilderImpl implements Builder {
private final HashMap<Integer, Float> data = new HashMap<>();
private final Int2FloatOpenHashMap data = new Int2FloatOpenHashMap();
private String id;
private Function<Map<String, Object>, List<JsonObject>> fontProviderFunction = (stringObjectMap -> null);
@@ -17,18 +17,18 @@
package net.momirealms.customnameplates.api.feature.advance;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import it.unimi.dsi.fastutil.ints.Int2FloatOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import java.util.*;
import static java.util.Objects.requireNonNull;
public class ConfigurableFontAdvanceDataImpl implements ConfigurableFontAdvanceData {
private final float defaultAdvance;
private final HashMap<Integer, Float> data = new HashMap<>();
private final List<CharacterFontAdvanceData> parents = new ArrayList<>();
private final Map<Integer, Float> data = new Int2FloatOpenHashMap();
private final List<CharacterFontAdvanceData> parents = new ObjectArrayList<>();
private final String id;
public ConfigurableFontAdvanceDataImpl(String id, float defaultAdvance, HashMap<Integer, Float> customData, List<CharacterFontAdvanceData> parentFonts) {
@@ -119,47 +119,48 @@ public class BackgroundImpl implements Background {
}
@Override
public String createImagePrefix(float n, float leftMargin, float rightMargin) {
String offset1 = OffsetFont.shortestNegChars(n + rightMargin + right.advance());
n = leftMargin + n + rightMargin;
public String createImagePrefix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
String offset1 = OffsetFont.shortestNegChars(advance + rightMargin + right.advance());
advance = leftMargin + advance + rightMargin;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(left.character());
while (n >= 128) {
while (advance >= 128) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_128.character());
n -= 128;
advance -= 128;
}
if (n - 64 >= 0) {
if (advance - 64 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_64.character());
n -= 64;
advance -= 64;
}
if (n - 32 >= 0) {
if (advance - 32 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_32.character());
n -= 32;
advance -= 32;
}
if (n - 16 >= 0) {
if (advance - 16 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_16.character());
n -= 16;
advance -= 16;
}
if (n - 8 >= 0) {
if (advance - 8 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_8.character());
n -= 8;
advance -= 8;
}
if (n - 4 >= 0) {
if (advance - 4 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_4.character());
n -= 4;
advance -= 4;
}
if (n - 2 >= 0) {
if (advance - 2 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_2.character());
n -= 2;
advance -= 2;
}
if (n - 1 >= 0) {
if (advance - 1 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_1.character());
}
@@ -171,50 +172,52 @@ public class BackgroundImpl implements Background {
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
return OffsetFont.shortestPosChars(rightMargin + right.advance());
}
@Override
public String createImage(float n, float leftMargin, float rightMargin) {
n = n + leftMargin + rightMargin + 2;
public String createImage(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
advance = advance + leftMargin + rightMargin + 2;
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(left.character());
while (n >= 128) {
while (advance >= 128) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_128.character());
n -= 128;
advance -= 128;
}
if (n - 64 >= 0) {
if (advance - 64 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_64.character());
n -= 64;
advance -= 64;
}
if (n - 32 >= 0) {
if (advance - 32 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_32.character());
n -= 32;
advance -= 32;
}
if (n - 16 >= 0) {
if (advance - 16 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_16.character());
n -= 16;
advance -= 16;
}
if (n - 8 >= 0) {
if (advance - 8 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_8.character());
n -= 8;
advance -= 8;
}
if (n - 4 >= 0) {
if (advance - 4 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_4.character());
n -= 4;
advance -= 4;
}
if (n - 2 >= 0) {
if (advance - 2 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_2.character());
n -= 2;
advance -= 2;
}
if (n - 1 >= 0) {
if (advance - 1 >= 0) {
stringBuilder.append(OffsetFont.NEG_1.character());
stringBuilder.append(width_1.character());
}
@@ -79,6 +79,7 @@ public class BubbleImpl implements Bubble {
@SuppressWarnings("DuplicatedCode")
@Override
public String createImagePrefix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
@@ -110,6 +111,7 @@ public class BubbleImpl implements Bubble {
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin - (tail.advance() - 1)) / (middle.advance() - 1));
float actualMiddleWidth = 0;
actualMiddleWidth += (tail.advance() - 1);
@@ -122,6 +124,7 @@ public class BubbleImpl implements Bubble {
@SuppressWarnings("DuplicatedCode")
@Override
public String createImage(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
@@ -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.api.feature.image;
public record Animation(int speed, int frames) {
}
@@ -18,6 +18,7 @@
package net.momirealms.customnameplates.api.feature.image;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import org.jetbrains.annotations.Nullable;
public interface Image {
@@ -33,15 +34,15 @@ public interface Image {
*
* @return true if the image has a shadow, false otherwise
*/
boolean hasShadow();
boolean removeShadow();
/**
* Returns the opacity level of the image.
* The value should typically range from 0 (fully transparent) to 255 (fully opaque).
* Get the animation of the image
*
* @return the opacity value of the image
* @return the animated image
*/
int opacity();
@Nullable
Animation animation();
/**
* Returns the configured character associated with this image.
@@ -73,20 +74,20 @@ public interface Image {
Builder id(String id);
/**
* Sets whether the image has a shadow effect.
* Sets whether to remove the shadow of the image
*
* @param has true if the image should have a shadow, false otherwise
* @param remove true if to remove the shadow
* @return the builder instance
*/
Builder hasShadow(boolean has);
Builder removeShadow(boolean remove);
/**
* Sets the opacity level of the image.
* Sets the animation of the image
*
* @param opacity the opacity value, typically from 0 to 255
* @param animation animation
* @return the builder instance
*/
Builder opacity(int opacity);
Builder animation(@Nullable Animation animation);
/**
* Sets the configured character associated with this image.
@@ -18,21 +18,22 @@
package net.momirealms.customnameplates.api.feature.image;
import net.momirealms.customnameplates.api.feature.ConfiguredCharacter;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class ImageImpl implements Image {
private final String id;
private final boolean hasShadow;
private final int opacity;
private final boolean removeShadow;
private final ConfiguredCharacter character;
private final Animation animation;
public ImageImpl(String id, boolean hasShadow, int opacity, ConfiguredCharacter character) {
public ImageImpl(String id, boolean removeShadow, Animation animation, ConfiguredCharacter character) {
this.id = id;
this.hasShadow = hasShadow;
this.opacity = opacity;
this.removeShadow = removeShadow;
this.character = character;
this.animation = animation;
}
@Override
@@ -41,13 +42,13 @@ public class ImageImpl implements Image {
}
@Override
public boolean hasShadow() {
return hasShadow;
public boolean removeShadow() {
return removeShadow;
}
@Override
public int opacity() {
return opacity;
public @Nullable Animation animation() {
return animation;
}
@Override
@@ -71,9 +72,9 @@ public class ImageImpl implements Image {
public static class BuilderImpl implements Builder {
private String id;
private boolean hasShadow;
private int opacity;
private boolean removeShadow;
private ConfiguredCharacter character;
private Animation animation;
@Override
public Builder id(String id) {
@@ -82,14 +83,14 @@ public class ImageImpl implements Image {
}
@Override
public Builder hasShadow(boolean has) {
this.hasShadow = has;
public Builder removeShadow(boolean removeShadow) {
this.removeShadow = removeShadow;
return this;
}
@Override
public Builder opacity(int opacity) {
this.opacity = opacity;
public Builder animation(@Nullable Animation animation) {
this.animation = animation;
return this;
}
@@ -101,7 +102,7 @@ public class ImageImpl implements Image {
@Override
public Image build() {
return new ImageImpl(id, hasShadow, opacity, character);
return new ImageImpl(id, removeShadow, animation, character);
}
}
}
@@ -57,6 +57,13 @@ public interface Nameplate extends AdaptiveImage {
*/
ConfiguredCharacter right();
/**
* Gets the min width of the nameplate
*
* @return the min width of the nameplate
*/
int minWidth();
/**
* Creates a new builder for constructing a Nameplate configuration.
*
@@ -87,6 +94,14 @@ public interface Nameplate extends AdaptiveImage {
*/
Builder displayName(String displayName);
/**
* Sets the min width for the nameplate
*
* @param minWidth min width
* @return the builder instance
*/
Builder minWidth(int minWidth);
/**
* Sets the configured character for the left side of the nameplate.
*
@@ -29,10 +29,12 @@ public class NameplateImpl implements Nameplate {
private final ConfiguredCharacter left;
private final ConfiguredCharacter middle;
private final ConfiguredCharacter right;
private final int minWidth;
public NameplateImpl(String id, String displayName, ConfiguredCharacter left, ConfiguredCharacter middle, ConfiguredCharacter right) {
public NameplateImpl(String id, String displayName, int minWidth, ConfiguredCharacter left, ConfiguredCharacter middle, ConfiguredCharacter right) {
this.id = id;
this.displayName = displayName;
this.minWidth = minWidth;
this.left = left;
this.middle = middle;
this.right = right;
@@ -63,6 +65,11 @@ public class NameplateImpl implements Nameplate {
return right;
}
@Override
public int minWidth() {
return minWidth;
}
@Override
public boolean equals(Object object) {
if (this == object) return true;
@@ -79,6 +86,8 @@ public class NameplateImpl implements Nameplate {
@SuppressWarnings("DuplicatedCode")
@Override
public String createImagePrefix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
advance = Math.max(minWidth, advance);
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
@@ -97,6 +106,8 @@ public class NameplateImpl implements Nameplate {
@Override
public String createImageSuffix(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
advance = Math.max(minWidth, advance);
int mid_amount = (int) Math.ceil((advance + leftMargin + rightMargin) / (middle.advance() - 1));
float exceed = mid_amount * (middle.advance() - 1) - advance - leftMargin - rightMargin;
return OffsetFont.shortestPosChars((float) Math.ceil(exceed / 2) + rightMargin + right.advance());
@@ -105,6 +116,8 @@ public class NameplateImpl implements Nameplate {
@SuppressWarnings("DuplicatedCode")
@Override
public String createImage(float advance, float leftMargin, float rightMargin) {
if (advance <= 0) return "";
advance = Math.max(minWidth, advance);
StringBuilder sb = new StringBuilder();
sb.append(left.character());
sb.append(OffsetFont.NEG_1.character());
@@ -128,6 +141,7 @@ public class NameplateImpl implements Nameplate {
private ConfiguredCharacter left;
private ConfiguredCharacter middle;
private ConfiguredCharacter right;
private int minWidth;
@Override
public Builder id(String id) {
@@ -141,6 +155,12 @@ public class NameplateImpl implements Nameplate {
return this;
}
@Override
public Builder minWidth(int minWidth) {
this.minWidth = minWidth;
return this;
}
@Override
public Builder left(ConfiguredCharacter left) {
this.left = left;
@@ -161,7 +181,7 @@ public class NameplateImpl implements Nameplate {
@Override
public Nameplate build() {
return new NameplateImpl(id, displayName, left, middle, right);
return new NameplateImpl(id, displayName, minWidth, left, middle, right);
}
}
}
@@ -76,6 +76,7 @@ public abstract class AbstractTag implements Tag {
@Override
public void show(CNPlayer viewer) {
if (!renderer.isValid()) return;
if (!isShown()) throw new IllegalStateException("This tag is currently hidden");
viewers.add(viewer);
resetViewerArray();
@@ -95,7 +96,8 @@ public abstract class AbstractTag implements Tag {
@Override
public void hide(CNPlayer viewer) {
if (!isShown()) return;
viewers.remove(viewer);
boolean removed = viewers.remove(viewer);
if (!removed) return;
resetViewerArray();
owner.untrackPassengers(viewer, entityID);
Object packet = CustomNameplates.getInstance().getPlatform().removeEntityPacket(entityID);
@@ -35,6 +35,13 @@ public interface TagRenderer {
*/
void hatOffset(double hatOffset);
/**
* Checks if the tag is valid
*
* @return valid or not
*/
boolean isValid();
/**
* Called every tick to update the state of the tags managed by this renderer.
*/
@@ -0,0 +1,51 @@
/*
* Copyright (C) <2024> <XiaoMoMi>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package net.momirealms.customnameplates.api.feature.tag;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
public class TeamView {
private final Map<String, Set<String>> teamMembers = new Object2ObjectOpenHashMap<>();
@Nullable
public Set<String> getTeamMembers(String team) {
return teamMembers.get(team);
}
public void addTeamMembers(String team, Collection<String> members) {
teamMembers.computeIfAbsent(team, k -> new ObjectOpenHashSet<>(members));
}
public void removeTeamMembers(String team, Collection<String> members) {
Set<String> membersSet = teamMembers.get(team);
if (membersSet != null) {
membersSet.removeAll(members);
}
}
public void removeTeam(String team) {
this.teamMembers.remove(team);
}
}
@@ -57,7 +57,15 @@ public interface UnlimitedTagManager extends Reloadable {
* @param player the player to update
* @param preview true to enable preview mode, false to disable
*/
void setPreviewing(CNPlayer player, boolean preview);
void setTempPreviewing(CNPlayer player, boolean preview);
/**
* Sets whether a player can always see their tags
*
* @param player the player to update
* @param preview true to enable preview mode, false to disable
*/
void togglePreviewing(CNPlayer player, boolean preview);
/**
* Returns the duration (in ticks) for which a tag preview is shown.
@@ -84,6 +84,14 @@ public class AdventureHelper {
.build();
}
public static void clearCache() {
AdventureHelper instance = getInstance();
instance.miniMessageToMinecraftComponentCache.cleanUp();
instance.miniMessageToComponentCache.cleanUp();
instance.jsonToMiniMessageCache.cleanUp();
instance.minecraftComponentToMiniMessageCache.cleanUp();
}
private static class SingletonHolder {
private static final AdventureHelper INSTANCE = new AdventureHelper();
}
@@ -291,7 +299,7 @@ public class AdventureHelper {
case 'd' -> stringBuilder.append("<light_purple>");
case 'e' -> stringBuilder.append("<yellow>");
case 'f' -> stringBuilder.append("<white>");
case 'r' -> stringBuilder.append("<r><!i>");
case 'r' -> stringBuilder.append("<reset>");
case 'l' -> stringBuilder.append("<b>");
case 'm' -> stringBuilder.append("<st>");
case 'o' -> stringBuilder.append("<i>");
@@ -61,12 +61,18 @@ public class VersionHelper {
private static float version;
private static boolean mojmap;
private static boolean folia;
private static boolean mohist;
private static boolean paper;
public static void init(String serverVersion) {
String[] split = serverVersion.split("\\.");
version = Float.parseFloat(split[1] + "." + (split.length == 3 ? split[2] : "0"));
checkMojMap();
checkFolia();
checkMohist();
checkPaper();
boolean isModdedServer = mohist;
paper = paper && !isModdedServer;
}
public static float version() {
@@ -90,6 +96,26 @@ public class VersionHelper {
}
}
private static void checkMohist() {
try {
Class.forName("com.mohistmc.api.ServerAPI");
mohist = true;
} catch (ClassNotFoundException ignored) {
}
}
private static void checkPaper() {
try {
Class.forName("com.destroystokyo.paper.Metrics");
paper = true;
} catch (ClassNotFoundException ignored) {
}
}
public static boolean isVersionNewerThan1_21_2() {
return version >= 21.19;
}
public static boolean isVersionNewerThan1_20_5() {
return version >= 20.49;
}
@@ -114,6 +140,14 @@ public class VersionHelper {
return folia;
}
public static boolean isMohist() {
return mohist;
}
public static boolean isPaperOrItsForks() {
return paper;
}
public static boolean isMojmap() {
return mojmap;
}
@@ -35,8 +35,6 @@ public interface PacketSender {
*/
void sendPacket(@NotNull CNPlayer player, Object packet);
void sendPacket(@NotNull CNPlayer player, Object packet, boolean immediately);
/**
* Sends a list of packets to the specified player.
*
@@ -44,6 +42,4 @@ public interface PacketSender {
* @param packet the list of packets to be sent
*/
void sendPacket(@NotNull CNPlayer player, List<Object> packet);
void sendPacket(@NotNull CNPlayer player, List<Object> packet, boolean immediately);
}
@@ -20,19 +20,33 @@ package net.momirealms.customnameplates.api.network;
import io.netty.channel.Channel;
import io.netty.channel.ChannelDuplexHandler;
import net.momirealms.customnameplates.api.CNPlayer;
import org.jetbrains.annotations.ApiStatus;
/**
* Interface for injecting and managing Netty pipeline handlers for players.
*/
public interface PipelineInjector {
@ApiStatus.Internal
void setUser(Channel channel, CNPlayer user);
@ApiStatus.Internal
CNPlayer getUser(Channel channel);
@ApiStatus.Internal
CNPlayer removeUser(Channel channel);
@ApiStatus.Internal
CNPlayer getUser(Object player);
/**
* Retrieves the Netty channel associated with the specified player.
*
* @param player the player whose channel is being retrieved
* @return the Netty channel associated with the player
*/
Channel getChannel(CNPlayer player);
@ApiStatus.Internal
Channel getChannel(Object player);
/**
* Creates a custom ChannelDuplexHandler for the specified player, used to intercept and modify network traffic.
@@ -40,19 +54,6 @@ public interface PipelineInjector {
* @param player the player for whom the handler is being created
* @return a ChannelDuplexHandler for the player
*/
@ApiStatus.Internal
ChannelDuplexHandler createHandler(CNPlayer player);
/**
* Injects a custom handler into the player's network channel, allowing interception and modification of packets.
*
* @param player the player whose channel will be injected
*/
void inject(CNPlayer player);
/**
* Removes the custom handler from the player's network channel.
*
* @param player the player whose channel will have the handler removed
*/
void uninject(CNPlayer player);
}
@@ -17,9 +17,9 @@
package net.momirealms.customnameplates.api.network;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -80,7 +80,7 @@ public class Tracker {
}
public Set<Integer> getPassengerIDs() {
return new HashSet<>(passengerIDs);
return new ObjectOpenHashSet<>(passengerIDs);
}
public boolean isEmpty() {
@@ -17,7 +17,9 @@
package net.momirealms.customnameplates.api.placeholder;
import java.util.HashSet;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CustomNameplates;
import java.util.Objects;
import java.util.Set;
@@ -27,8 +29,8 @@ public abstract class AbstractPlaceholder implements Placeholder {
protected int countId = PlaceholderCounter.getAndIncrease();
protected int refreshInterval;
protected PlaceholderManager manager;
protected Set<Placeholder> children = new HashSet<>();
protected Set<Placeholder> parents = new HashSet<>();
protected Set<Placeholder> children = new ObjectOpenHashSet<>();
protected Set<Placeholder> parents = new ObjectOpenHashSet<>();
protected AbstractPlaceholder(PlaceholderManager manager, String id, int refreshInterval) {
if (refreshInterval == 0) {
@@ -54,21 +56,37 @@ public abstract class AbstractPlaceholder implements Placeholder {
@Override
public void addChild(Placeholder placeholder) {
if (placeholder == this) {
CustomNameplates.getInstance().getPluginLogger().warn(String.format("There may be something wrong with your configuration. Placeholder relationship level loops. Placeholder %s becomes its own child Placeholder", id));
return;
}
children.add(placeholder);
}
@Override
public void addChildren(Set<Placeholder> placeholders) {
if (placeholders.contains(this)) {
CustomNameplates.getInstance().getPluginLogger().warn(String.format("There may be something wrong with your configuration. Placeholder relationship level loops. Placeholder %s becomes its own child Placeholder", id));
return;
}
children.addAll(placeholders);
}
@Override
public void addParent(Placeholder placeholder) {
if (placeholder == this) {
CustomNameplates.getInstance().getPluginLogger().warn(String.format("There may be something wrong with your configuration. Placeholder relationship level loops. Placeholder %s becomes its own parent Placeholder", id));
return;
}
parents.add(placeholder);
}
@Override
public void addParents(Set<Placeholder> placeholders) {
if (placeholders.contains(this)) {
CustomNameplates.getInstance().getPluginLogger().warn(String.format("There may be something wrong with your configuration. Placeholder relationship level loops. Placeholder %s becomes its own parent Placeholder", id));
return;
}
parents.addAll(placeholders);
}
@@ -18,10 +18,8 @@
package net.momirealms.customnameplates.api.placeholder.internal;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.OffsetFont;
import net.momirealms.customnameplates.api.CustomNameplatesAPI;
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
import net.momirealms.customnameplates.api.helper.AdventureHelper;
import org.jetbrains.annotations.ApiStatus;
@ApiStatus.Internal
@@ -62,23 +60,6 @@ public class StaticText {
}
public String create(String text) {
float parsedWidth = CustomNameplates.getInstance().getAdvanceManager().getLineAdvance(text);
switch (position) {
case LEFT -> {
return text + AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth));
}
case RIGHT -> {
return AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth)) + text;
}
case MIDDLE -> {
int half = (int) ((width - parsedWidth) / 2);
String left = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(half));
String right = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(width - parsedWidth - half));
return left + text + right;
}
default -> {
return "";
}
}
return CustomNameplatesAPI.getInstance().createStaticText(text, width, position);
}
}
@@ -18,6 +18,7 @@
package net.momirealms.customnameplates.api.requirement;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
/**
* A requirement that is always satisfied, representing an "empty" or default requirement.
@@ -57,6 +58,11 @@ public class EmptyRequirement implements Requirement {
return "empty";
}
@Override
public int countId() {
return CustomNameplates.getInstance().getRequirementManager().countId(this);
}
/**
* Returns -1, indicating that this requirement does not have a refresh interval.
*
@@ -40,6 +40,13 @@ public interface Requirement {
*/
String type();
/**
* Returns the int id for faster lookup
*
* @return count id
*/
int countId();
/**
* Returns the refresh interval of the requirement.
*
@@ -87,4 +87,12 @@ public interface RequirementManager extends Reloadable {
*/
@NotNull
Requirement parseSimpleRequirement(@NotNull String type, @NotNull Object value);
/**
* Gets the count id for the requirement for faster lookup
*
* @param requirement requirement
* @return the id
*/
int countId(Requirement requirement);
}
@@ -32,6 +32,9 @@ public class JsonData {
@SerializedName("bubble")
private String bubble;
@SerializedName("flags")
private int flags;
/**
* Constructs a new {@link JsonData} instance.
*
@@ -41,6 +44,27 @@ public class JsonData {
public JsonData(String nameplate, String bubble) {
this.nameplate = nameplate;
this.bubble = bubble;
this.flags = 0;
}
public JsonData(String nameplate, String bubble, int flags) {
this.nameplate = nameplate;
this.bubble = bubble;
this.flags = flags;
}
public JsonData(String nameplate, String bubble, boolean previewState) {
this.nameplate = nameplate;
this.bubble = bubble;
this.flags = encodeFlags(previewState);
}
public static int encodeFlags(boolean previewState) {
return previewState ? 1 : 0;
}
public static boolean decodePreviewState(int flags) {
return (flags & 1) == 1;
}
/**
@@ -61,6 +85,15 @@ public class JsonData {
return bubble;
}
/**
* Returns the flags
*
* @return flags
*/
public int getFlags() {
return flags;
}
/**
* Converts this JSON data back into a {@link PlayerData} instance.
*
@@ -72,6 +105,7 @@ public class JsonData {
.uuid(uuid)
.nameplate(nameplate)
.bubble(bubble)
.previewTags(decodePreviewState(flags))
.build();
}
}
@@ -41,6 +41,13 @@ public interface PlayerData {
*/
String bubble();
/**
* Returns if the tag should always be shown to the owner
*
* @return if the tag should always be shown to the owner
*/
boolean previewTags();
/**
* Returns the UUID of the player.
*
@@ -100,6 +107,14 @@ public interface PlayerData {
*/
Builder uuid(UUID uuid);
/**
* Sets whether to preview state for the {@link PlayerData}.
*
* @param previewTags the state
* @return the builder instance
*/
Builder previewTags(boolean previewTags);
/**
* Builds and returns the {@link PlayerData} instance.
*
@@ -114,6 +129,6 @@ public interface PlayerData {
* @return a {@link JsonData} object containing the nameplate and bubble data
*/
default JsonData toJsonData() {
return new JsonData(nameplate(), bubble());
return new JsonData(nameplate(), bubble(), previewTags());
}
}
@@ -28,11 +28,13 @@ public class PlayerDataImpl implements PlayerData {
protected UUID uuid;
protected String nameplate;
protected String bubble;
protected boolean previewTags;
public PlayerDataImpl(UUID uuid, String nameplate, String bubble) {
public PlayerDataImpl(UUID uuid, String nameplate, String bubble, boolean previewTags) {
this.uuid = uuid;
this.nameplate = nameplate;
this.bubble = bubble;
this.previewTags = previewTags;
}
@Override
@@ -45,6 +47,11 @@ public class PlayerDataImpl implements PlayerData {
return bubble;
}
@Override
public boolean previewTags() {
return previewTags;
}
@Override
public UUID uuid() {
return uuid;
@@ -57,6 +64,7 @@ public class PlayerDataImpl implements PlayerData {
private String nameplate;
private String bubble;
private boolean previewTags;
private UUID uuid;
@Override
@@ -77,9 +85,15 @@ public class PlayerDataImpl implements PlayerData {
return this;
}
@Override
public Builder previewTags(boolean previewTags) {
this.previewTags = previewTags;
return this;
}
@Override
public PlayerData build() {
return new PlayerDataImpl(uuid, nameplate, bubble);
return new PlayerDataImpl(uuid, nameplate, bubble, previewTags);
}
}
}
+6
View File
@@ -2,6 +2,10 @@ plugins {
id("io.github.goooler.shadow") version "8.1.8"
}
repositories {
maven("https://s01.oss.sonatype.org/content/repositories/snapshots/")
}
dependencies {
compileOnly(project(":common"))
compileOnly(project(":api"))
@@ -26,4 +30,6 @@ dependencies {
// TTF
compileOnly("org.lwjgl:lwjgl-freetype:${rootProject.properties["lwjgl_version"]}")
compileOnly("org.lwjgl:lwjgl:${rootProject.properties["lwjgl_version"]}")
// Fast util
compileOnly("it.unimi.dsi:fastutil:${rootProject.properties["fastutil_version"]}")
}
@@ -100,10 +100,18 @@ public class ActionBarManagerImpl implements ActionBarManager, JoinQuitListener
public void handleActionBarPacket(CNPlayer player, String miniMessage) {
ActionBarSender sender = senders.get(player.uuid());
if (sender != null) {
if (miniMessage.length() >= 1024) return;
sender.externalActionBar(miniMessage);
}
}
public void temporarilyHideCustomActionBar(CNPlayer player) {
ActionBarSender sender = senders.get(player.uuid());
if (sender != null) {
sender.temporarilyHide();
}
}
@Override
public @Nullable String getExternalActionBar(CNPlayer player) {
ActionBarSender sender = senders.get(player.uuid());
@@ -116,14 +124,19 @@ public class ActionBarManagerImpl implements ActionBarManager, JoinQuitListener
@Override
public void onPlayerJoin(CNPlayer player) {
if (!ConfigManager.actionbarModule()) return;
plugin.getScheduler().asyncLater(() -> {
Runnable r = () -> {
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);
};
if (ConfigManager.delaySend() < 0) {
r.run();
} else {
plugin.getScheduler().asyncLater(r, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
}
}
@Override
@@ -17,7 +17,9 @@
package net.momirealms.customnameplates.backend.feature.actionbar;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
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.DynamicText;
@@ -30,7 +32,6 @@ 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;
@@ -48,6 +49,7 @@ public class ActionBarSender implements Feature {
private String externalActionBar;
private long externalExpireTime;
private long temporaryHiddenTime;
private String latestContent;
@@ -78,7 +80,7 @@ public class ActionBarSender implements Feature {
this.onConditionTimerCheck();
}
public void onConditionTimerCheck() {
public synchronized void onConditionTimerCheck() {
ActionBarConfig[] configs = manager.actionBarConfigs();
outer: {
for (ActionBarConfig config : configs) {
@@ -146,14 +148,19 @@ public class ActionBarSender implements Feature {
}
public void refresh() {
if (currentActionBar == null) {
latestContent = "";
return;
}
latestContent = this.currentActionBar.render(owner);
textChangeFlag = false;
}
public void sendLatestActionBar() {
if (isTemporarilyHidden()) return;
if (latestContent != null) {
updateLastUpdateTime();
Object packet = CustomNameplates.getInstance().getPlatform().setActionBarTextPacket(AdventureHelper.miniMessageToMinecraftComponent(latestContent, "np", "ab"));
Object packet = CustomNameplates.getInstance().getPlatform().setActionBarTextPacket(AdventureHelper.miniMessageToMinecraftComponent(latestContent, "nameplates", "actionbar"));
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
}
@@ -170,7 +177,7 @@ public class ActionBarSender implements Feature {
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
Set<Placeholder> placeholders = new ObjectOpenHashSet<>();
for (ActionBarConfig config : manager.actionBarConfigs()) {
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
@@ -181,8 +188,11 @@ public class ActionBarSender implements Feature {
@Override
public void notifyPlaceholderUpdates(CNPlayer player, boolean force) {
refresh();
sendLatestActionBar();
// The actionbar should be visible before updating
if (currentActionBar != null) {
refresh();
sendLatestActionBar();
}
}
public void updateLastUpdateTime() {
@@ -204,6 +214,15 @@ public class ActionBarSender implements Feature {
public void externalActionBar(@NotNull String externalActionBar) {
requireNonNull(externalActionBar);
this.externalActionBar = externalActionBar;
this.externalExpireTime = System.currentTimeMillis() + 3000;
this.externalExpireTime = System.currentTimeMillis() + ConfigManager.otherActionBarStayTime();
}
public void temporarilyHide() {
this.temporaryHiddenTime = System.currentTimeMillis() + 3000;
}
public boolean isTemporarilyHidden() {
if (!ConfigManager.displaySystemChat()) return false;
return temporaryHiddenTime > System.currentTimeMillis();
}
}
@@ -25,6 +25,8 @@ import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
@@ -292,8 +294,8 @@ public class AdvanceManagerImpl implements AdvanceManager {
private final HashMap<String, BiConsumer<String, Section>> templateConfigConsumersMap = new HashMap<>();
private final CustomNameplates plugin;
private final HashMap<String, CharacterFontAdvanceData> charFontWidthDataMap = new HashMap<>();
private final HashMap<String, ConfigurableFontAdvanceData> configFontWidthDataMap = new HashMap<>();
private final Object2ObjectOpenHashMap<String, CharacterFontAdvanceData> charFontWidthDataMap = new Object2ObjectOpenHashMap<>();
private final Object2ObjectOpenHashMap<String, ConfigurableFontAdvanceData> configFontWidthDataMap = new Object2ObjectOpenHashMap<>();
private final Cache<String, Float> textWidthCache;
@@ -520,6 +522,8 @@ public class AdvanceManagerImpl implements AdvanceManager {
String codePoints = requireNonNull(section.getString("codepoints", ""), "codepoints should be NonNull");
int height = section.getInt("height", 8);
boolean custom = section.getBoolean("custom", false);
boolean copy = section.getBoolean("generate", false);
String namespace = section.getString("namespace", ConfigManager.namespace());
File bitmapCache = new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + id + ".tmp");
if (!bitmapCache.exists()) {
@@ -584,9 +588,25 @@ public class AdvanceManagerImpl implements AdvanceManager {
}
registerCharacterFontData(id, bitmapCache, (properties) -> {
int ascent = (int) properties.get("shift_y");
String filePath = custom ? ConfigManager.namespace() + ":font/" + file : "minecraft:font/" + codePoints + ".png";
String filePath = custom ? namespace + ":font/" + file : "minecraft:font/" + codePoints + ".png";
plugin.getConfigManager().saveResource("tmp/" + codePoints + ".json");
StringBuilder jsonContent = new StringBuilder();
if (copy) {
File pngFile = new File(plugin.getDataDirectory().toFile(), "font" + File.separator + file);
File destination = new File(plugin.getDataDirectory().toFile(),
"ResourcePack"
+ File.separator + "assets"
+ File.separator + (custom ? namespace : "minecraft")
+ File.separator + "textures"
+ File.separator + "font"
+ File.separator + (custom ? file : codePoints + ".png")
);
try {
FileUtils.copyFile(pngFile, destination);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
try (BufferedReader reader = new BufferedReader(new FileReader(new File(plugin.getDataDirectory().toFile(), "tmp" + File.separator + codePoints + ".json")))) {
String line;
while ((line = reader.readLine()) != null) {
@@ -787,11 +807,6 @@ public class AdvanceManagerImpl implements AdvanceManager {
plugin.getPluginLogger().warn(ttf.getAbsolutePath() + " is not a .ttf");
return;
}
if (true) {
plugin.getPluginLogger().info("We're currently unable to obtain TTF advance");
plugin.getPluginLogger().info("If you have Java knowledge and are interested in this, consider contributing if you have any progress.");
return;
}
try (InputStream inputStream = new FileInputStream(ttf)) {
ByteBuffer byteBuffer = null;
FT_Face fT_Face = null;
@@ -936,7 +951,7 @@ public class AdvanceManagerImpl implements AdvanceManager {
.advance(dataMap)
.fontProviderFunction(function)
.build();
charFontWidthDataMap.put(id, fontWidthData);
this.charFontWidthDataMap.put(id, fontWidthData);
}
@Override
@@ -1024,6 +1039,7 @@ public class AdvanceManagerImpl implements AdvanceManager {
@Override
public void unload() {
this.textWidthCache.invalidateAll();
this.configFontWidthDataMap.clear();
}
@@ -1220,15 +1236,17 @@ public class AdvanceManagerImpl implements AdvanceManager {
return totalAdvance;
}
@SuppressWarnings("UnstableApiUsage")
@Override
public List<Tuple<String, Key, Boolean>> miniMessageToIterable(String text) {
if (AdventureHelper.legacySupport) text = AdventureHelper.legacyToMiniMessage(text);
ElementNode node = (ElementNode) AdventureHelper.miniMessage().deserializeToTree(text);
ArrayList<Tuple<String, Key, Boolean>> iterableTexts = new ArrayList<>();
List<Tuple<String, Key, Boolean>> iterableTexts = new ObjectArrayList<>();
nodeToIterableTexts(node, iterableTexts, MINECRAFT_DEFAULT_FONT, false);
return iterableTexts;
}
@SuppressWarnings("UnstableApiUsage")
private void nodeToIterableTexts(ElementNode node, List<Tuple<String, Key, Boolean>> list, Key font, boolean bold) {
if (node instanceof ValueNode valueNode) {
String text = valueNode.value();
@@ -18,6 +18,8 @@
package net.momirealms.customnameplates.backend.feature.background;
import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
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.background.Background;
@@ -26,7 +28,10 @@ import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BackgroundManagerImpl implements BackgroundManager {
@@ -44,6 +49,7 @@ public class BackgroundManagerImpl implements BackgroundManager {
@Override
public void load() {
if (!ConfigManager.backgroundModule()) return;
this.loadConfigs();
}
@@ -55,7 +61,7 @@ public class BackgroundManagerImpl implements BackgroundManager {
@Override
public Collection<Background> getBackgrounds() {
return new HashSet<>(backgrounds.values());
return new ObjectArrayList<>(backgrounds.values());
}
private void loadConfigs() {
@@ -84,14 +84,19 @@ public class BossBarManagerImpl implements BossBarManager, JoinQuitListener {
@Override
public void onPlayerJoin(CNPlayer player) {
if (!ConfigManager.bossbarModule()) return;
plugin.getScheduler().asyncLater(() -> {
Runnable r = () -> {
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);
};
if (ConfigManager.delaySend() < 0) {
r.run();
} else {
plugin.getScheduler().asyncLater(r, ConfigManager.delaySend() * 50L, TimeUnit.MILLISECONDS);
}
}
@Override
@@ -17,6 +17,7 @@
package net.momirealms.customnameplates.backend.feature.bossbar;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
@@ -28,7 +29,6 @@ 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;
@@ -98,7 +98,7 @@ public class BossBarSender implements Feature, BossBar {
timeLeft = 0;
}
public void tick() {
public synchronized void tick() {
if (timeLeft > 0)
timeLeft--;
@@ -143,12 +143,12 @@ public class BossBarSender implements Feature, BossBar {
}
public void show() {
isShown = true;
if (latestContent == null) {
refresh();
if (this.latestContent == null) {
this.refresh();
}
Object packet = CustomNameplates.getInstance().getPlatform().createBossBarPacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent), progress(), overlay(), color());
Object packet = CustomNameplates.getInstance().getPlatform().createBossBarPacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(this.latestContent), progress(), overlay(), color());
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
isShown = true;
}
public boolean isShown() {
@@ -163,7 +163,7 @@ public class BossBarSender implements Feature, BossBar {
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
Set<Placeholder> placeholders = new ObjectOpenHashSet<>();
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
}
@@ -172,18 +172,22 @@ public class BossBarSender implements Feature, BossBar {
@Override
public void notifyPlaceholderUpdates(CNPlayer p1, boolean force) {
refresh();
if (isShown()) {
refresh();
sendLatestBossBarName();
}
}
public void refresh() {
latestContent = this.currentBossBar.render(owner);
if (this.currentBossBar == null) {
this.latestContent = "";
return;
}
this.latestContent = this.currentBossBar.render(owner);
}
public void sendLatestBossBarName() {
if (latestContent != null && isShown()) {
if (this.latestContent != null && isShown()) {
Object packet = CustomNameplates.getInstance().getPlatform().updateBossBarNamePacket(uuid, AdventureHelper.miniMessageToMinecraftComponent(latestContent));
CustomNameplates.getInstance().getPacketSender().sendPacket(owner, packet);
}
@@ -19,11 +19,22 @@ package net.momirealms.customnameplates.backend.feature.bubble;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag;
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
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.OffsetFont;
import net.momirealms.customnameplates.api.feature.bubble.Bubble;
import net.momirealms.customnameplates.api.feature.bubble.BubbleConfig;
import net.momirealms.customnameplates.api.feature.bubble.BubbleManager;
@@ -35,13 +46,14 @@ import net.momirealms.customnameplates.api.util.ConfigUtils;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
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 final Map<String, Bubble> bubbles = new Object2ObjectOpenHashMap<>();
private Requirement[] sendBubbleRequirements;
private Requirement[] viewBubbleRequirements;
private String defaultBubbleId;
@@ -52,7 +64,7 @@ public class BubbleManagerImpl implements BubbleManager, ChatListener {
private float viewRange;
private Set<String> blacklistChannels;
private ChannelMode channelMode;
private final HashMap<String, BubbleConfig> bubbleConfigs = new HashMap<>();
private final Map<String, BubbleConfig> bubbleConfigs = new Object2ObjectOpenHashMap<>();
public BubbleManagerImpl(CustomNameplates plugin) {
this.plugin = plugin;
@@ -84,12 +96,12 @@ public class BubbleManagerImpl implements BubbleManager, ChatListener {
@Override
public Collection<Bubble> bubbles() {
return new HashSet<>(bubbles.values());
return new ObjectArrayList<>(bubbles.values());
}
@Override
public Collection<BubbleConfig> bubbleConfigs() {
return new HashSet<>(bubbleConfigs.values());
return new ObjectArrayList<>(bubbleConfigs.values());
}
@Override
@@ -162,8 +174,38 @@ public class BubbleManagerImpl implements BubbleManager, ChatListener {
}
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"));
YamlDocument document = plugin.getConfigManager().loadConfig("configs" + File.separator + "bubble.yml",
GeneralSettings.builder()
.setRouteSeparator('.')
.setUseDefaults(false)
.build(),
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.builder()
.setEscapeUnprintable(false)
.setScalarFormatter((tag, value, role, def) -> {
if (role == NodeRole.KEY) {
return ScalarStyle.PLAIN;
} else {
return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
}
})
.build(),
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(ConfigManager.configVersion(), "sender-requirements", '.')
.addIgnoredRoute(ConfigManager.configVersion(), "viewer-requirements", '.')
.addIgnoredRoute(ConfigManager.configVersion(), "bubble-settings", '.')
.build());
try {
document.save(plugin.getConfigManager().resolveConfig("configs" + File.separator + "bubble.yml").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
sendBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("sender-requirements"));
viewBubbleRequirements = plugin.getRequirementManager().parseRequirements(document.getSection("viewer-requirements"));
defaultBubbleId = document.getString("default-bubble", "chat");
@@ -191,7 +233,7 @@ public class BubbleManagerImpl implements BubbleManager, ChatListener {
.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", ""))
.textPrefix(inner.getString("text-prefix", "").replace("{namespace}", ConfigManager.namespace()))
.textSuffix(inner.getString("text-suffix", ""))
.scale(ConfigUtils.vector3(inner.getString("scale", "1,1,1")))
.build());
@@ -265,6 +307,10 @@ public class BubbleManagerImpl implements BubbleManager, ChatListener {
String fullText = config.textPrefix() + AdventureHelper.stripTags(message.replace("\\", "\\\\")) + config.textSuffix();
int lines = plugin.getAdvanceManager().getLines(fullText, config.lineWidth());
if (lines > config.maxLines()) return;
if (lines <= 0) return;
int space = (int) plugin.getAdvanceManager().getLineAdvance(" ");
String fakeSpace = AdventureHelper.surroundWithNameplatesFont(OffsetFont.createOffsets(space));
fullText = fullText.replace(" ", fakeSpace);
TagRenderer renderer = plugin.getUnlimitedTagManager().getTagRender(player);
if (renderer == null) return;
@@ -231,7 +231,8 @@ public class BubbleTag extends AbstractTag {
@Override
public void hide(CNPlayer viewer) {
if (!isShown()) return;
viewers.remove(viewer);
boolean removed = viewers.remove(viewer);
if (!removed) return;
resetViewerArray();
if (background != null) {
owner.untrackPassengers(viewer, entityID, subEntityID);
@@ -110,6 +110,7 @@ public abstract class AbstractChatManager implements ChatManager {
@Override
public void onChat(CNPlayer player, String message, String channel) {
String text = message;
plugin.debug(() -> player.name() + " says [" + message + "]");
for (EmojiProvider provider : emojiProviders) {
text = provider.replace(player, text);
}
@@ -18,8 +18,11 @@
package net.momirealms.customnameplates.backend.feature.image;
import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
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.image.Animation;
import net.momirealms.customnameplates.api.feature.image.Image;
import net.momirealms.customnameplates.api.feature.image.ImageManager;
import net.momirealms.customnameplates.api.util.ConfigUtils;
@@ -28,7 +31,6 @@ 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 {
@@ -47,6 +49,7 @@ public class ImageManagerImpl implements ImageManager {
@Override
public void load() {
if (!ConfigManager.imageModule()) return;
this.loadConfigs();
}
@@ -57,7 +60,7 @@ public class ImageManagerImpl implements ImageManager {
@Override
public Collection<Image> images() {
return new HashSet<>(images.values());
return new ObjectArrayList<>(images.values());
}
private void loadConfigs() {
@@ -71,8 +74,11 @@ public class ImageManagerImpl implements ImageManager {
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))
.animation(config.contains("animation") ? new Animation(
config.getInt("animation.speed", 64),
config.getInt("animation.frames", 1)
) : null)
.removeShadow(!config.getBoolean("shadow", true))
.character(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("image") + ".png"),
config.getInt("ascent", 8),
@@ -18,7 +18,9 @@
package net.momirealms.customnameplates.backend.feature.nameplate;
import dev.dejvokep.boostedyaml.YamlDocument;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.momirealms.customnameplates.api.CNPlayer;
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.nameplate.Nameplate;
@@ -48,6 +50,7 @@ public class NameplateManagerImpl implements NameplateManager {
@Override
public void load() {
this.loadConfig();
if (!ConfigManager.nameplateModule()) return;
this.loadConfigs();
}
@@ -59,7 +62,7 @@ public class NameplateManagerImpl implements NameplateManager {
@Override
public Collection<Nameplate> nameplates() {
return new HashSet<>(nameplates.values());
return new ObjectArrayList<>(nameplates.values());
}
@Override
@@ -113,6 +116,7 @@ public class NameplateManagerImpl implements NameplateManager {
Nameplate nameplate = Nameplate.builder()
.id(id)
.displayName(config.getString("display-name", id))
.minWidth(config.getInt("min-width", 0))
.left(ConfiguredCharacter.create(
ConfigUtils.getFileInTheSameFolder(configFile, config.getString("left.image") + ".png"),
config.getInt("left.ascent", 12),
@@ -134,7 +138,7 @@ public class NameplateManagerImpl implements NameplateManager {
}
private void saveDefaultNameplates() {
String[] png_list = new String[]{"cat", "egg", "cheems", "wither", "xmas", "halloween", "hutao", "starsky", "trident", "rabbit"};
String[] png_list = new String[]{"egg", "xmas", "halloween", "hutao", "starsky", "trident", "rabbit", "sign", "game", "enter", "panda", "angel", "banner"};
String[] part_list = new String[]{"_left.png", "_middle.png", "_right.png", ".yml"};
for (String name : png_list) {
for (String part : part_list) {
@@ -29,6 +29,7 @@ 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.Animation;
import net.momirealms.customnameplates.api.feature.image.Image;
import net.momirealms.customnameplates.api.feature.nameplate.Nameplate;
import net.momirealms.customnameplates.api.feature.pack.ResourcePackManager;
@@ -40,6 +41,7 @@ import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.*;
public class ResourcePackManagerImpl implements ResourcePackManager {
@@ -69,19 +71,34 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
// 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);
if (ConfigManager.enableShader()) {
if (VersionHelper.isVersionNewerThan1_21_2()) {
this.generateShaders("ResourcePack" + File.separator + "overlay_1_21_2" + 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_21_2"),
new File(plugin.getDataFolder(), "ResourcePack")
);
FileUtils.deleteDirectory(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "overlay_1_21_2"));
} catch (IOException e) {
throw new RuntimeException(e);
}
} else if (VersionHelper.isVersionNewerThan1_20_5()) {
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);
}
this.generateShaders("ResourcePack" + File.separator + "overlay_1_21_2" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
} else {
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);
this.generateShaders("ResourcePack" + File.separator + "overlay_1_21_2" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "shaders" + File.separator + "core" + File.separator, true);
}
}
@@ -172,7 +189,11 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
@SuppressWarnings("ResultOfMethodCallIgnored")
private void setPackFormat() {
if (VersionHelper.isVersionNewerThan1_20_5()) {
if (VersionHelper.isVersionNewerThan1_21_2()) {
plugin.getConfigManager().saveResource("ResourcePack" + File.separator + "pack_1_21_2.mcmeta");
File file = new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack_1_21_2.mcmeta");
file.renameTo(new File(plugin.getDataFolder(), "ResourcePack" + File.separator + "pack.mcmeta"));
} else 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"));
@@ -216,6 +237,21 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
plugin.getPluginLogger().warn("Failed to copy files to Oraxen", e);
}
}
if (ConfigManager.packNexo()){
try {
FileUtils.deleteDirectory(new File(pluginsFolder, "Nexo" + File.separator + "pack" + File.separator + "external_packs" + File.separator + "CustomNameplates"));
FileUtils.copyDirectory(resourcePackFolder, new File(pluginsFolder, "Nexo" + File.separator + "pack" + File.separator + "external_packs" + File.separator + "CustomNameplates"));
} catch (IOException e){
plugin.getPluginLogger().warn("Failed to copy files to Nexo", e);
}
}
if(ConfigManager.packCreativeCentral()) {
try {
FileUtils.copyDirectory(new File(resourcePackFolder, "assets"), new File(pluginsFolder, "creative-central" + File.separator + "resources" + File.separator + "assets"));
} catch (IOException e){
plugin.getPluginLogger().warn("Failed to copy files to Creative-Central", e);
}
}
}
private List<JsonObject> getBubbles(File texturesFolder) {
@@ -295,11 +331,53 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
jo.add("chars", ja);
list.add(jo);
try {
File targetFile = new File(texturesFolder,
ConfigManager.imagePath().replace("\\", File.separator) + character.imageFile().getName());
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()));
"contents" + File.separator + "images" + File.separator + character.imageFile().getName()), targetFile);
if (image.removeShadow() || image.animation() != null) {
BufferedImage bufferedImage = ImageIO.read(targetFile);
if (image.removeShadow()) {
for (int y = 0; y < bufferedImage.getHeight(); y++) {
for (int x = 0; x < bufferedImage.getWidth(); x++) {
int argb = bufferedImage.getRGB(x, y);
int alpha = (argb >> 24) & 0xff;
if (alpha != 0) {
int rgb = argb & 0x00ffffff;
int newArgb = (254 << 24) | rgb;
bufferedImage.setRGB(x, y, newArgb);
}
}
}
}
Animation animation = image.animation();
if (animation != null) {
int height = bufferedImage.getHeight();
int extra = height % animation.frames();
if (extra > 0) {
plugin.getPluginLogger().warn("Image height is not a multiple of frame rate: " + image.id());
continue;
}
int eachFrameHeight = height / animation.frames();
int width = bufferedImage.getWidth();
int speed = Math.min(Math.max(animation.speed(), 1), 255);
int alpha = 1;
int red = speed;
int green = width;
int blue = eachFrameHeight;
int argb = (alpha << 24) | (red << 16) | (green << 8) | blue;
for (int i = 0; i < animation.frames(); i++) {
int y = i * eachFrameHeight;
bufferedImage.setRGB(0, y, argb);
}
}
ImageIO.write(bufferedImage, "png", targetFile);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -418,14 +496,14 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
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";
var destination = "ResourcePack" + File.separator + "assets" + File.separator + "minecraft" + File.separator + "textures" + File.separator + "font";
File imageFile = new File(plugin.getDataFolder(), path);
File destinationFile = new File(plugin.getDataFolder(), destination);
File destinationFolder = new File(plugin.getDataFolder(), destination);
if (imageFile.exists()) {
try {
FileUtils.copyFile(imageFile, destinationFile);
FileUtils.copyFileToDirectory(imageFile, destinationFolder);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
@@ -433,7 +511,6 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
}
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");
@@ -486,7 +563,7 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
public static class ShaderConstants {
public static final String Nameplates_Shader_1_20_5 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
"if (Color.xyz == vec3(255., 254., 253.) / 255. && (Position.z == 50.03 || Position.z == 200.03 || Position.z == 400.03 || Position.z == 1000.03 || Position.z == 2000 || Position.z == 2200.03 || Position.z == 2400.06 || Position.z == 2400.12 || Position.z == 2650.03 || Position.z == 2800.03)) {\n" +
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
" vertex.x += 1;\n" +
@@ -505,7 +582,7 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
" }";
public static final String Nameplates_Shader_1_20_4 =
"if (Color.xyz == vec3(255., 254., 253.) / 255.) {\n" +
"if (Color.xyz == vec3(255., 254., 253.) / 255. && (Position.z == 0.03 || Position.z == 0.06 || Position.z == 0.12 || Position.z == 100.03 || Position.z == 200.03 || Position.z == 400.03)) {\n" +
" vertexColor = Color * texelFetch(Sampler2, UV2 / 16, 0);\n" +
" vertex.y += 1;\n" +
" vertex.x += 1;\n" +
@@ -558,6 +635,7 @@ public class ResourcePackManagerImpl implements ResourcePackManager {
" 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" +
@@ -17,6 +17,7 @@
package net.momirealms.customnameplates.backend.feature.tag;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.CarouselText;
@@ -31,7 +32,6 @@ 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;
@@ -131,16 +131,19 @@ public class NameTag extends AbstractTag implements RelationalFeature {
}
public void refresh() {
if (!isShown()) return;
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);
if (isShown(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
@@ -192,7 +195,7 @@ public class NameTag extends AbstractTag implements RelationalFeature {
@Override
public Set<Placeholder> allPlaceholders() {
HashSet<Placeholder> placeholders = new HashSet<>();
Set<Placeholder> placeholders = new ObjectOpenHashSet<>();
for (CarouselText text : config.carouselTexts()) {
placeholders.addAll(text.preParsedDynamicText().placeholders());
}
@@ -17,6 +17,7 @@
package net.momirealms.customnameplates.backend.feature.tag;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.Feature;
@@ -24,6 +25,7 @@ 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.helper.VersionHelper;
import net.momirealms.customnameplates.api.network.Tracker;
import java.util.*;
@@ -37,6 +39,7 @@ public class TagRendererImpl implements TagRenderer {
private Tag[] tagArray;
private Tag[] rTagsArray;
private double hatOffset;
private boolean valid = true;
public TagRendererImpl(UnlimitedTagManager manager, CNPlayer owner) {
this.owner = owner;
@@ -68,15 +71,23 @@ public class TagRendererImpl implements TagRenderer {
}
@Override
public void onTick() {
HashSet<CNPlayer> playersToUpdatePassengers = new HashSet<>();
HashSet<CNPlayer> tagTranslationUpdates = new HashSet<>();
public boolean isValid() {
return valid;
}
@Override
public synchronized void onTick() {
if (!isValid()) return;
Set<CNPlayer> playersToUpdatePassengers = new ObjectOpenHashSet<>();
Set<CNPlayer> tagTranslationUpdates = new ObjectOpenHashSet<>();
Collection<CNPlayer> nearbyPlayers = owner.nearbyPlayers();
for (Tag tag : tagArray) {
boolean canShow = tag.canShow();
if (canShow) {
if (tag.isShown()) {
for (CNPlayer nearby : owner.nearbyPlayers()) {
for (CNPlayer nearby : nearbyPlayers) {
if (tag.isShown(nearby)) {
if (!tag.canShow(nearby)) {
tag.hide(nearby);
@@ -97,7 +108,7 @@ public class TagRendererImpl implements TagRenderer {
tag.init();
tag.tick();
tag.show();
for (CNPlayer nearby : owner.nearbyPlayers()) {
for (CNPlayer nearby : nearbyPlayers) {
if (tag.canShow(nearby) && !tag.isShown(nearby)) {
tag.show(nearby);
playersToUpdatePassengers.add(nearby);
@@ -110,7 +121,7 @@ public class TagRendererImpl implements TagRenderer {
if (tag.isShown()) {
tag.hide();
if (!tag.relativeTranslation())
tagTranslationUpdates.addAll(owner.nearbyPlayers());
tagTranslationUpdates.addAll(nearbyPlayers);
}
}
}
@@ -133,6 +144,7 @@ public class TagRendererImpl implements TagRenderer {
@Override
public void destroy() {
this.valid = false;
for (Tag tag : this.tagArray) {
tag.hide();
if (tag instanceof Feature feature) {
@@ -290,7 +302,13 @@ public class TagRendererImpl implements TagRenderer {
passengers[index++] = passenger;
}
Object packet = CustomNameplates.getInstance().getPlatform().setPassengersPacket(owner.entityID(), passengers);
CustomNameplates.getInstance().getPacketSender().sendPacket(another, packet);
if (VersionHelper.isPaperOrItsForks()) {
CustomNameplates.getInstance().getPacketSender().sendPacket(another, packet);
} else {
CustomNameplates.getInstance().getScheduler().sync().runLater(() -> {
CustomNameplates.getInstance().getPacketSender().sendPacket(another, packet);
}, 0, null);
}
}
public void handleEntityDataChange(CNPlayer another, boolean isCrouching) {
@@ -19,12 +19,21 @@ package net.momirealms.customnameplates.backend.feature.tag;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.common.ScalarStyle;
import dev.dejvokep.boostedyaml.libs.org.snakeyaml.engine.v2.nodes.Tag;
import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings;
import dev.dejvokep.boostedyaml.settings.general.GeneralSettings;
import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings;
import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings;
import dev.dejvokep.boostedyaml.utils.format.NodeRole;
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.PlayerListener;
import net.momirealms.customnameplates.api.feature.tag.NameTagConfig;
import net.momirealms.customnameplates.api.feature.tag.TagRenderer;
import net.momirealms.customnameplates.api.feature.tag.UnlimitedTagManager;
@@ -36,12 +45,14 @@ import net.momirealms.customnameplates.api.util.ConfigUtils;
import net.momirealms.customnameplates.api.util.Vector3;
import java.io.File;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitListener {
public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitListener, PlayerListener {
private final CustomNameplates plugin;
private final LinkedHashMap<String, NameTagConfig> configs = new LinkedHashMap<>();
@@ -55,24 +66,73 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitLis
}
@Override
public void setPreviewing(CNPlayer player, boolean preview) {
boolean isPreviewing = player.isPreviewing();
public void setTempPreviewing(CNPlayer player, boolean preview) {
boolean isPreviewing = player.isTempPreviewing();
if (isPreviewing) {
if (preview) return;
plugin.getUnlimitedTagManager().onRemovePlayer(player, player);
onRemovePlayer(player, player);
player.removePlayerFromTracker(player);
((AbstractCNPlayer) player).setPreviewing(false);
((AbstractCNPlayer) player).setTempPreviewing(false);
} else {
if (!preview) return;
Tracker tracker = player.addPlayerToTracker(player);
tracker.setScale(player.scale());
tracker.setCrouching(player.isCrouching());
tracker.setSpectator(player.isSpectator());
plugin.getUnlimitedTagManager().onAddPlayer(player, player);
((AbstractCNPlayer) player).setPreviewing(true);
onAddPlayer(player, player);
((AbstractCNPlayer) player).setTempPreviewing(true);
}
}
@Override
public void togglePreviewing(CNPlayer player, boolean preview) {
boolean isPreviewing = player.isToggleablePreviewing();
if (isPreviewing) {
if (preview) return;
onRemovePlayer(player, player);
player.removePlayerFromTracker(player);
((AbstractCNPlayer) player).setToggleablePreviewing(false);
} else {
if (!preview) return;
Tracker tracker = player.addPlayerToTracker(player);
tracker.setScale(player.scale());
tracker.setCrouching(player.isCrouching());
tracker.setSpectator(player.isSpectator());
onAddPlayer(player, player);
((AbstractCNPlayer) player).setToggleablePreviewing(true);
}
}
@Override
public void onChangeWorld(CNPlayer player) {
plugin.getScheduler().asyncLater(() -> {
if (player.isOnline() && (player.isTempPreviewing() || player.isToggleablePreviewing())) {
onRemovePlayer(player, player);
onAddPlayer(player, player);
}
}, VersionHelper.isFolia() ? 500 : 50, TimeUnit.MILLISECONDS);
}
@Override
public void onRespawn(CNPlayer player) {
plugin.getScheduler().asyncLater(() -> {
if (player.isOnline() && (player.isTempPreviewing() || player.isToggleablePreviewing())) {
onRemovePlayer(player, player);
onAddPlayer(player, player);
}
}, VersionHelper.isFolia() ? 500 : 50, TimeUnit.MILLISECONDS);
}
@Override
public void onTeleport(CNPlayer player) {
plugin.getScheduler().asyncLater(() -> {
if (player.isOnline() && (player.isTempPreviewing() || player.isToggleablePreviewing())) {
onRemovePlayer(player, player);
onAddPlayer(player, player);
}
}, VersionHelper.isFolia() ? 500 : 50, TimeUnit.MILLISECONDS);
}
@Override
public int previewDuration() {
return previewDuration;
@@ -80,13 +140,17 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitLis
@Override
public void onPlayerJoin(CNPlayer player) {
plugin.debug(() -> player.name() + " joined the server");
TagRendererImpl sender = new TagRendererImpl(this, player);
sender.onTick();
TagRendererImpl previous = tagRenderers.put(player.uuid(), sender);
if (previous != null) {
previous.destroy();
}
setPreviewing(player, isAlwaysShow());
if (isAlwaysShow()) {
setTempPreviewing(player, isAlwaysShow());
} else if (player.isLoaded() && player.isTempPreviewing()) {
setTempPreviewing(player, false);
}
}
@Override
@@ -99,7 +163,6 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitLis
@Override
public void load() {
if (!ConfigManager.nameplateModule()) return;
this.loadConfig();
this.resetArray();
for (CNPlayer online : plugin.getOnlinePlayers()) {
@@ -189,12 +252,39 @@ public class UnlimitedTagManagerImpl implements UnlimitedTagManager, JoinQuitLis
}
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"));
YamlDocument document = plugin.getConfigManager().loadConfig("configs" + File.separator + "nameplate.yml",
GeneralSettings.builder()
.setRouteSeparator('.')
.setUseDefaults(false)
.build(),
LoaderSettings
.builder()
.setAutoUpdate(true)
.build(),
DumperSettings.builder()
.setEscapeUnprintable(false)
.setScalarFormatter((tag, value, role, def) -> {
if (role == NodeRole.KEY) {
return ScalarStyle.PLAIN;
} else {
return tag == Tag.STR ? ScalarStyle.DOUBLE_QUOTED : ScalarStyle.PLAIN;
}
})
.build(),
UpdaterSettings
.builder()
.setVersioning(new BasicVersioning("config-version"))
.addIgnoredRoute(ConfigManager.configVersion(), "unlimited", '.')
.build());
try {
document.save(plugin.getConfigManager().resolveConfig("configs" + File.separator + "nameplate.yml").toFile());
} catch (IOException e) {
throw new RuntimeException(e);
}
previewDuration = document.getInt("preview-duration", 5);
alwaysShow = document.getBoolean("always-show", false);
Section unlimitedSection = document.getSection("unlimited");
if (unlimitedSection == null) return;
if (!ConfigManager.nametagModule() || unlimitedSection == null) return;
for (Map.Entry<String, Object> entry : unlimitedSection.getStringRouteMappedValues(false).entrySet()) {
if (!(entry.getValue() instanceof Section section))
return;
@@ -19,6 +19,10 @@ package net.momirealms.customnameplates.backend.placeholder;
import dev.dejvokep.boostedyaml.YamlDocument;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
@@ -44,8 +48,6 @@ 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 {
@@ -53,7 +55,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
private final CustomNameplates plugin;
private final HashMap<String, Integer> refreshIntervals = new HashMap<>();
private final HashMap<Integer, Integer> fasterRefreshIntervals = new HashMap<>();
private final Int2IntOpenHashMap fasterRefreshIntervals = new Int2IntOpenHashMap();
private final Map<String, Placeholder> registeredPlaceholders = new HashMap<>();
private final HashMap<Placeholder, List<PreParsedDynamicText>> childrenText = new HashMap<>();
private final HashMap<String, Placeholder> nestedPlaceholders = new HashMap<>();
@@ -112,6 +114,15 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
}
private void loadNestNameplatePlaceholders() {
if (!ConfigManager.nameplateModule()) {
this.registerPlayerPlaceholder("%np_tag-image%", (player -> "Please remove %np_tag-image% if you have disabled nameplates module"));
this.registerPlayerPlaceholder("%rel_np_tag-image%", (player -> "Please remove %rel_np_tag-image% if you have disabled nameplates module"));
this.registerPlayerPlaceholder("%np_tag-text%", (player -> "Please remove %np_tag-text% if you have disabled nameplates module"));
this.registerPlayerPlaceholder("%rel_np_tag-text%", (player -> "Please remove %rel_np_tag-text% if you have disabled nameplates module"));
this.registerPlayerPlaceholder("%np_tag%", (player -> "Please remove %np_tag% if you have disabled nameplates module"));
this.registerPlayerPlaceholder("%rel_np_tag%", (player -> "Please remove %rel_np_tag% if you have disabled nameplates module"));
return;
}
PreParsedDynamicText nameTag = new PreParsedDynamicText(plugin.getNameplateManager().playerNameTag());
Placeholder placeholder1 = this.registerPlayerPlaceholder("%np_tag-image%", (player -> {
String equippedNameplate = player.equippedNameplate();
@@ -166,22 +177,35 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
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("%np_biome%", (player -> {
Vector3 vector3 = player.position();
return plugin.getPlatform().getBiome(player.world(), (int) Math.floor(vector3.x()), (int) Math.floor(vector3.y()), (int) Math.floor(vector3.z()));
}));
this.registerRelationalPlaceholder("%rel_np_biome%", (p1,p2) -> {
Vector3 vector3 = p1.position();
return plugin.getPlatform().getBiome(p1.world(), (int) Math.floor(vector3.x()), (int) Math.floor(vector3.y()), (int) Math.floor(vector3.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("");
});
if (ConfigManager.nameplateModule()) {
this.registerPlayerPlaceholder("%np_equipped_nameplate%", CNPlayer::equippedNameplate);
this.registerPlayerPlaceholder("%np_equipped_nameplate-name%", (player) -> {
Nameplate nameplate = plugin.getNameplateManager().nameplateById(player.equippedNameplate());
return Optional.ofNullable(nameplate).map(Nameplate::displayName).orElse("");
});
}
if (ConfigManager.bubbleModule()) {
this.registerPlayerPlaceholder("%np_equipped_bubble%", CNPlayer::equippedBubble);
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()));
for (int i = 1; i <= 20; i++) {
@@ -189,7 +213,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
this.registerPlayerPlaceholder("%np_gradient_" + i + "%", (player) -> {
int currentTicks = MainTask.getTicks();
double progress = currentTicks * 0.01 * speed;
return String.format("%.2f", -1 + (progress % 2.0001));
return String.format(Locale.US, "%.2f", -1 + (progress % 2.0001));
});
}
for (int i = 1; i <= 20; i++) {
@@ -197,9 +221,11 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
this.registerSharedPlaceholder("%shared_np_gradient_" + i + "%", () -> {
int currentTicks = MainTask.getTicks();
double progress = currentTicks * 0.01 * speed;
return String.format("%.2f", -1 + (progress % 2.0001));
return String.format(Locale.US, "%.2f", -1 + (progress % 2.0001));
});
}
this.registerPlayerPlaceholder("%np_is_showing%", (player) -> String.valueOf(player.isToggleablePreviewing()));
this.registerPlayerPlaceholder("%np_is_previewing%", (player) -> String.valueOf(player.isTempPreviewing()));
this.registerPlayerPlaceholder("%np_time%", (player) -> {
long time = player.playerTime() % 24_000;
String ap = time >= 6000 && time < 18000 ? " PM" : " AM";
@@ -315,13 +341,18 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
}
private void loadVanillaHud(Section section) {
if (!ConfigManager.imageModule()) return;
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");
Image empty = plugin.getImageManager().imageById(inner.getString("images.empty"));
Image half = plugin.getImageManager().imageById(inner.getString("images.half"));
Image full = plugin.getImageManager().imageById(inner.getString("images.full"));
if (empty == null || half == null || full == null) {
plugin.getPluginLogger().warn("Empty/Half/Full image not found in vanilla hud");
continue;
}
String currentValue = inner.getString("placeholder.value", "1");
String maxValue = inner.getString("placeholder.max-value", currentValue);
VanillaHud vanillaHud = new VanillaHud(empty, half, full, reverse, currentValue, maxValue);
@@ -381,6 +412,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
}
private void loadBubbleTextSection(Section section) {
if (!ConfigManager.bubbleModule()) return;
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
String id = entry.getKey();
if (entry.getValue() instanceof Section inner) {
@@ -412,13 +444,14 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
childrenText.put(placeholder8, list);
childrenText.put(placeholder9, list);
} else {
plugin.getPluginLogger().warn("Nameplate [" + bbID + "] not exists");
plugin.getPluginLogger().warn("Bubble [" + bbID + "] not exists");
}
}
}
}
private void loadNameplateTextSection(Section section) {
if (!ConfigManager.nameplateModule()) return;
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
String id = entry.getKey();
if (entry.getValue() instanceof Section inner) {
@@ -457,6 +490,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
}
private void loadBackgroundTextSection(Section section) {
if (!ConfigManager.backgroundModule()) return;
for (Map.Entry<String, Object> entry : section.getStringRouteMappedValues(false).entrySet()) {
String id = entry.getKey();
if (entry.getValue() instanceof Section inner) {
@@ -553,15 +587,15 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
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<RelationalPlaceholder> delayedPlaceholdersToUpdate = new ArrayList<>();
Set<Feature> featuresToNotifyUpdates = new ObjectOpenHashSet<>();
Map<Feature, List<CNPlayer>> relationalFeaturesToNotifyUpdates = new Object2ObjectOpenHashMap<>();
List<RelationalPlaceholder> delayedPlaceholdersToUpdate = new ObjectArrayList<>();
for (Placeholder placeholder : player.activePlaceholdersToRefresh()) {
if (placeholder instanceof PlayerPlaceholder playerPlaceholder) {
TimeStampData<String> previous = player.getValue(placeholder);
TimeStampData<String> previous = player.getRawPlayerValue(playerPlaceholder);
if (previous == null) {
String value = playerPlaceholder.request(player);
player.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true));
player.setPlayerValue(playerPlaceholder, new TimeStampData<>(value, MainTask.getTicks(), true));
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
} else {
if (previous.ticks() > MainTask.getTicks() - getRefreshInterval(placeholder.countId())) {
@@ -583,7 +617,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
} else if (placeholder instanceof RelationalPlaceholder relationalPlaceholder) {
delayedPlaceholdersToUpdate.add(relationalPlaceholder);
} else if (placeholder instanceof SharedPlaceholder sharedPlaceholder) {
TimeStampData<String> previous = player.getValue(placeholder);
TimeStampData<String> previous = player.getRawSharedValue(sharedPlaceholder);
if (previous == null) {
String value;
// if the shared placeholder has been updated by other players
@@ -592,7 +626,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
} else {
value = sharedPlaceholder.request();
}
player.setValue(placeholder, new TimeStampData<>(value, MainTask.getTicks(), true));
player.setSharedValue(sharedPlaceholder, new TimeStampData<>(value, MainTask.getTicks(), true));
featuresToNotifyUpdates.addAll(player.activeFeatures(placeholder));
} else {
// The placeholder has been refreshed by other codes
@@ -623,14 +657,14 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
for (RelationalPlaceholder placeholder : delayedPlaceholdersToUpdate) {
for (CNPlayer nearby : player.nearbyPlayers()) {
TimeStampData<String> previous = player.getRelationalValue(placeholder, nearby);
TimeStampData<String> previous = player.getRawRelationalValue(placeholder, nearby);
if (previous == null) {
String value = placeholder.request(player, nearby);
player.setRelationalValue(placeholder, nearby, new TimeStampData<>(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<>());
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ObjectArrayList<>());
players.add(nearby);
}
}
@@ -641,7 +675,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
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<>());
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ObjectArrayList<>());
players.add(nearby);
}
}
@@ -655,7 +689,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
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<>());
List<CNPlayer> players = relationalFeaturesToNotifyUpdates.computeIfAbsent(feature, k -> new ObjectArrayList<>());
players.add(nearby);
}
}
@@ -705,7 +739,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
}
}
registeredPlaceholders.put(placeholder.id(), placeholder);
fasterRefreshIntervals.put(placeholder.countId(), getRefreshInterval(placeholder.id()));
fasterRefreshIntervals.put(placeholder.countId(), placeholder.refreshInterval());
return placeholder;
}
@@ -751,7 +785,7 @@ public class PlaceholderManagerImpl implements PlaceholderManager {
if (firstPercent == 0 && text.charAt(text.length() - 1) == '%' && text.indexOf('%', 1) == text.length() - 1) {
return Collections.singletonList(text);
}
List<String> placeholders = new ArrayList<>();
List<String> placeholders = new ObjectArrayList<>();
Matcher m = PATTERN.matcher(text);
while (m.find()) {
placeholders.add(m.group());
@@ -17,16 +17,29 @@
package net.momirealms.customnameplates.backend.requirement;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.requirement.Requirement;
import org.jetbrains.annotations.Nullable;
public abstract class AbstractRequirement implements Requirement {
protected final int refreshInterval;
@Nullable
protected Integer countId;
public AbstractRequirement(int refreshInterval) {
this.refreshInterval = refreshInterval;
}
@Override
public int countId() {
// lazy init
if (countId == null) {
countId = CustomNameplates.getInstance().getRequirementManager().countId(this);
}
return countId;
}
@Override
public int refreshInterval() {
return refreshInterval;
@@ -18,6 +18,8 @@
package net.momirealms.customnameplates.backend.requirement;
import dev.dejvokep.boostedyaml.block.implementation.Section;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.api.feature.PreParsedDynamicText;
@@ -26,15 +28,21 @@ 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 net.momirealms.customnameplates.common.util.ListUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public abstract class AbstractRequirementManager implements RequirementManager {
protected final CustomNameplates plugin;
private final HashMap<String, RequirementFactory> requirementFactoryMap = new HashMap<>();
private final Object2ObjectOpenHashMap<String, RequirementFactory> requirementFactoryMap = new Object2ObjectOpenHashMap<>(128);
private int countId = 0;
private final Map<Requirement, Integer> registeredRequirements = new Object2IntOpenHashMap<>(128);
public AbstractRequirementManager(CustomNameplates plugin) {
this.plugin = plugin;
@@ -42,7 +50,23 @@ public abstract class AbstractRequirementManager implements RequirementManager {
this.registerPlatformRequirements();
}
@Override
public void reload() {
this.countId = 0;
this.registeredRequirements.clear();
}
@Override
public int countId(Requirement requirement) {
if (requirement == null) return -1;
if (this.registeredRequirements.containsKey(requirement)) return this.registeredRequirements.get(requirement);
this.countId++;
this.registeredRequirements.put(requirement, countId);
return this.countId;
}
private void registerInternalRequirements() {
this.registerRequirement((args, interval) -> new LaggyRequirement(interval, (int) args), "laggy");
this.registerRequirement((args, interval) -> {
Section section = ConfigUtils.safeCast(args, Section.class);
if (section == null) return Requirement.empty();
@@ -100,10 +124,17 @@ public abstract class AbstractRequirementManager implements RequirementManager {
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", "");
PreParsedDynamicText dynamicText1 = new PreParsedDynamicText(section.getString("papi", ""), true);
String regex = section.getString("regex", "");
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("papi", ""), true);
String regex = section.getString("regex", "");
return new NotRegexRequirement(interval, dynamicText1, regex);
}, "!regex");
this.registerRequirement((args, interval) -> {
Section section = ConfigUtils.safeCast(args, Section.class);
if (section == null) return Requirement.empty();
@@ -178,6 +209,10 @@ public abstract class AbstractRequirementManager implements RequirementManager {
boolean has = (boolean) args;
return new HasBubbleRequirement(interval, has);
}, "has-bubble");
this.registerRequirement((args, interval) -> new NameplateRequirement(interval, new HashSet<>(ListUtils.toList(args))), "nameplate");
this.registerRequirement((args, interval) -> new NotNameplateRequirement(interval, new HashSet<>(ListUtils.toList(args))), "!nameplate");
this.registerRequirement((args, interval) -> new BubbleRequirement(interval, new HashSet<>(ListUtils.toList(args))), "bubble");
this.registerRequirement((args, interval) -> new NotBubbleRequirement(interval, new HashSet<>(ListUtils.toList(args))), "!bubble");
}
protected abstract void registerPlatformRequirements();
@@ -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.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
import java.util.Objects;
import java.util.Set;
public class BubbleRequirement extends AbstractRequirement {
private final Set<String> bubbles;
public BubbleRequirement(int refreshInterval, Set<String> bubbles) {
super(refreshInterval);
this.bubbles = bubbles;
}
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.bubbleModule()) return false;
String bubble = p1.equippedBubble();
if (bubble.equals("none")) bubble = CustomNameplates.getInstance().getBubbleManager().defaultBubbleId();
return bubbles.contains(bubble);
}
@Override
public String type() {
return "bubble";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof BubbleRequirement that)) return false;
return bubbles.equals(that.bubbles);
}
@Override
public int hashCode() {
return Objects.hashCode(bubbles);
}
}
@@ -18,6 +18,7 @@
package net.momirealms.customnameplates.backend.requirement.builtin;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
@@ -32,6 +33,7 @@ public class HasBubbleRequirement extends AbstractRequirement {
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.bubbleModule()) return !has;
String bubble = p1.equippedBubble();
if (bubble.equals("none")) bubble = CustomNameplates.getInstance().getBubbleManager().defaultBubbleId();
if (has) {
@@ -18,6 +18,7 @@
package net.momirealms.customnameplates.backend.requirement.builtin;
import net.momirealms.customnameplates.api.CNPlayer;
import net.momirealms.customnameplates.api.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
@@ -32,6 +33,7 @@ public class HasNameplateRequirement extends AbstractRequirement {
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.nameplateModule()) return !has;
String nameplate = p1.equippedNameplate();
if (nameplate.equals("none")) nameplate = CustomNameplates.getInstance().getNameplateManager().defaultNameplateId();
if (has) {
@@ -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.ConfigManager;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
public class LaggyRequirement extends AbstractRequirement {
private final int time;
public LaggyRequirement(int refreshInterval, int time) {
super(refreshInterval);
this.time = time;
}
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.debug()) return true;
long time1 = System.currentTimeMillis();
while (true) {
if (System.currentTimeMillis() - time1 > time) {
break;
}
}
return true;
}
@Override
public String type() {
return "laggy";
}
@Override
public int hashCode() {
return type().hashCode() + time * 17;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof LaggyRequirement that)) return false;
return time == that.time;
}
}
@@ -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.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
import java.util.Objects;
import java.util.Set;
public class NameplateRequirement extends AbstractRequirement {
private final Set<String> nameplates;
public NameplateRequirement(int refreshInterval, Set<String> nameplates) {
super(refreshInterval);
this.nameplates = nameplates;
}
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.nameplateModule()) return false;
String nameplate = p1.equippedNameplate();
if (nameplate.equals("none")) nameplate = CustomNameplates.getInstance().getNameplateManager().defaultNameplateId();
return nameplates.contains(nameplate);
}
@Override
public String type() {
return "nameplate";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NameplateRequirement that)) return false;
return nameplates.equals(that.nameplates);
}
@Override
public int hashCode() {
return Objects.hashCode(nameplates);
}
}
@@ -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.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
import java.util.Objects;
import java.util.Set;
public class NotBubbleRequirement extends AbstractRequirement {
private final Set<String> bubbles;
public NotBubbleRequirement(int refreshInterval, Set<String> bubbles) {
super(refreshInterval);
this.bubbles = bubbles;
}
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.bubbleModule()) return false;
String bubble = p1.equippedBubble();
if (bubble.equals("none")) bubble = CustomNameplates.getInstance().getBubbleManager().defaultBubbleId();
return !bubbles.contains(bubble);
}
@Override
public String type() {
return "!bubble";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NotBubbleRequirement that)) return false;
return bubbles.equals(that.bubbles);
}
@Override
public int hashCode() {
return Objects.hashCode(bubbles);
}
}
@@ -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.ConfigManager;
import net.momirealms.customnameplates.api.CustomNameplates;
import net.momirealms.customnameplates.backend.requirement.AbstractRequirement;
import java.util.Objects;
import java.util.Set;
public class NotNameplateRequirement extends AbstractRequirement {
private final Set<String> nameplates;
public NotNameplateRequirement(int refreshInterval, Set<String> nameplates) {
super(refreshInterval);
this.nameplates = nameplates;
}
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
if (!ConfigManager.nameplateModule()) return false;
String nameplate = p1.equippedNameplate();
if (nameplate.equals("none")) nameplate = CustomNameplates.getInstance().getNameplateManager().defaultNameplateId();
return !nameplates.contains(nameplate);
}
@Override
public String type() {
return "!nameplate";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof NotNameplateRequirement that)) return false;
return nameplates.equals(that.nameplates);
}
@Override
public int hashCode() {
return Objects.hashCode(nameplates);
}
}
@@ -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 NotRegexRequirement extends PlaceholderRequirement<String> {
public NotRegexRequirement(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";
}
}
@@ -37,8 +37,9 @@ public abstract class PlaceholdersRequirement extends AbstractRequirement {
@Override
public boolean isSatisfied(CNPlayer p1, CNPlayer p2) {
p1.forceUpdatePlaceholders(t1.placeholders(), Set.of(p2));
p1.forceUpdatePlaceholders(t2.placeholders(), Set.of(p2));
Set<CNPlayer> set = Set.of(p2);
p1.forceUpdatePlaceholders(t1.placeholders(), set);
p1.forceUpdatePlaceholders(t2.placeholders(), set);
String a1 = t1.fastCreate(p1).render(p2);
String a2 = t2.fastCreate(p1).render(p2);
return checkArgument(a1, a2);
@@ -95,6 +95,7 @@ public class StorageManagerImpl implements StorageManager, JoinQuitListener {
private void handleDataLoad(CNPlayer player, PlayerData data) {
player.equippedBubble(data.bubble());
player.equippedNameplate(data.nameplate());
plugin.getUnlimitedTagManager().togglePreviewing(player, data.previewTags());
}
@Override
@@ -1,5 +1,5 @@
#version 150
#moj_import<fog.glsl>
#moj_import <fog.glsl>
uniform sampler2D Sampler0;
uniform vec4 ColorModulator;
@@ -1,5 +1,5 @@
#version 150
#moj_import<fog.glsl>
#moj_import <fog.glsl>
uniform sampler2D Sampler0;
uniform vec4 ColorModulator;
@@ -1,9 +1,4 @@
{
"blend": {
"func": "add",
"srcrgb": "srcalpha",
"dstrgb": "1-srcalpha"
},
"vertex": "rendertype_text",
"fragment": "rendertype_text",
"attributes": [
@@ -0,0 +1,42 @@
#version 150
#moj_import <minecraft:fog.glsl>
uniform sampler2D Sampler0;
uniform vec4 ColorModulator;
uniform float FogStart,FogEnd;
uniform vec4 FogColor;
uniform float GameTime;
in float vertexDistance;
in vec4 vertexColor;
in vec2 texCoord0;
in float depthLevel;
%SHADER_0%
out vec4 fragColor;
void main() {
vec4 texColor = texture(Sampler0, texCoord0);
vec4 color = texColor * vertexColor * ColorModulator;%SHADER_1%
if (color.a < 0.1) {
discard;
}
if (depthLevel == 114514.0) {
discard;
}
if (texColor.a == 254.0/255.0) {
if (depthLevel == 1000.0
|| depthLevel == 2200
|| depthLevel == 50
|| depthLevel == 2650
|| depthLevel == 200
|| depthLevel == 400
|| depthLevel == 2800
|| depthLevel == 2400
) {
discard;
} else {
color = vec4(texColor.rgb, 1.0) * vertexColor * ColorModulator;
}
}
fragColor = linear_fog(color, vertexDistance, FogStart, FogEnd, FogColor);
}
@@ -0,0 +1,23 @@
{
"vertex": "minecraft:core/rendertype_text",
"fragment": "minecraft:core/rendertype_text",
"attributes": [
"Position",
"Color",
"UV0",
"UV2"
],
"samplers": [
{ "name": "Sampler0" },
{ "name": "Sampler2" }
],
"uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
{ "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
{ "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },
{ "name": "GameTime", "type": "float", "count": 1, "values": [ 1.0 ] },
{ "name": "FogColor", "type": "float", "count": 4, "values": [ 0.0, 0.0, 0.0, 0.0 ] }
]
}
@@ -0,0 +1,27 @@
#version 150
#moj_import <minecraft:fog.glsl>
in vec3 Position;
in vec4 Color;
in vec2 UV0;
in ivec2 UV2;
uniform sampler2D Sampler2;
uniform mat4 ModelViewMat;
uniform mat4 ProjMat;
uniform float GameTime;
uniform int FogShape;
uniform vec2 ScreenSize;
out float vertexDistance;
out vec4 vertexColor;
out vec2 texCoord0;
out float depthLevel;
%SHADER_0%
void main() {
vec4 vertex = vec4(Position, 1.0);
vertexDistance = fog_distance(Position, FogShape);
depthLevel = Position.z;
texCoord0 = UV0;
%SHADER_1%%SHADER_2%%SHADER_3%
}
@@ -1,21 +1,28 @@
{
"pack":{
"pack_format": 8,
"description":"CustomNameplates",
"supported_formats": {
"min_inclusive": 8,
"max_inclusive": 34
"pack": {
"pack_format": 13,
"description": "CustomNameplates",
"supported_formats": {
"min_inclusive": 13,
"max_inclusive": 46
}
},
"overlays": {
"entries": [
{
"formats": {
"min_inclusive": 32,
"max_inclusive": 34
},
"directory": "overlay_1_20_5"
},
{
"formats": {
"min_inclusive": 42,
"max_inclusive": 46
},
"directory": "overlay_1_21_2"
}
]
}
},
"overlays": {
"entries": [
{
"formats": {
"min_inclusive": 32,
"max_inclusive": 34
},
"directory": "overlay_1_20_5"
}
]
}
}
@@ -1,10 +1,28 @@
{
"pack":{
"pack_format": 32,
"description":"CustomNameplates",
"supported_formats": {
"min_inclusive": 32,
"max_inclusive": 34
"pack": {
"pack_format": 32,
"description": "CustomNameplates",
"supported_formats": {
"min_inclusive": 32,
"max_inclusive": 46
}
},
"overlays": {
"entries": [
{
"formats": {
"min_inclusive": 32,
"max_inclusive": 34
},
"directory": "overlay_1_20_5"
},
{
"formats": {
"min_inclusive": 42,
"max_inclusive": 46
},
"directory": "overlay_1_21_2"
}
]
}
}
}
@@ -0,0 +1,10 @@
{
"pack": {
"pack_format": 42,
"description": "CustomNameplates",
"supported_formats": {
"min_inclusive": 42,
"max_inclusive": 46
}
}
}
@@ -87,6 +87,16 @@ nameplates_preview:
usage:
- /nameplates preview
# A command to toggle the nameplate previewing
# This command conflicts with `/nameplates preview`
# You should never enable both of them at the same time
# Usage: [COMMAND]
nameplates_toggle:
enable: false
permission: nameplates.command.toggle
usage:
- /nameplates toggle
# A command to view the available nameplates
# Usage: [COMMAND]
nameplates_list:
+28 -16
View File
@@ -11,7 +11,8 @@ force-locale: ''
# Module Settings: Enable or disable specific features.
modules:
nameplates: true # Enable nameplate display
nametags: true # Enable name tags
nameplates: true # Enable nameplates
backgrounds: true # Enable background images for nameplates
bubbles: true # Enable bubble text display
bossbars: true # Enable bossbars
@@ -22,8 +23,10 @@ modules:
integrations:
resource-pack:
ItemsAdder: false # Integration with ItemsAdder
ItemsAdder-old-method: false # Old method for ItemsAdder
ItemsAdder-old-method: false # Legacy integration with ItemsAdder
Oraxen: false # Integration with Oraxen
Nexo: false # Integration with Nexo
Creative-Central: false # Integration with Creative-Central
chat:
TrChat: false # Integration with TrChat
VentureChat: false # Integration with VentureChat
@@ -31,6 +34,8 @@ integrations:
CarbonChat: false # Integration with CarbonChat
AdvancedChat: false # Integration with AdvancedChat
Essentials: false # Integration with Essentials chat
ChatControlRed: false # Integration with ChatControlRed
Chatty: false # Integration with Chatty
# Resource Pack Generation Settings: Configure resource pack generation behavior.
resource-pack:
@@ -58,15 +63,19 @@ resource-pack:
# Additional Settings: Extra options to control behavior and performance.
other-settings:
legacy-color-code-support: true # Enable support for legacy color codes using &
send-delay: 0 # Set actionbar/bossbar send delay
send-delay: 20 # Set actionbar/bossbar send delay (ticks) (0 = send immediately on async threads, -1 = send immediately on main thread)
catch-other-plugin-actionbar: true # Capture actionbars from other plugins
other-actionbar-stay-time: 3000 # How long does other actionsbars stay (ms)
display-system-actionbar: true # Should the plugin display system actionbar and temporarily hide the custom actionbar?
hide-team-names: true # Hide the team name sent by other plugins or vanilla team system
unsafe-chat-event: false # Listen for canceled(unsafe) chat events from unknown plugins
default-condition-refresh-interval: 1 # Set default condition refresh interval
default-placeholder-refresh-interval: 1 # Set default placeholder refresh interval
default-condition-refresh-interval: 10 # Set default condition refresh interval in ticks
default-placeholder-refresh-interval: 1 # Set default placeholder refresh interval in ticks
placeholder-refresh-interval: # Custom placeholder refresh intervals for performance optimization
"%player_name%": 100
"%vault_prefix%": 20
# Font Templates: Define different fonts and their settings.
# Requires a restart to apply because it can be laggy to read thousands of character
font-templates:
space:
space:
@@ -75,7 +84,7 @@ other-settings:
unihex:
unifont:
file: unifont.zip # Load font from unifont.zip
generate: false # Don't include font in the generated resource pack
generate: false # Should this unihex file be included in the generated pack
size_overrides: # Override size for specific Unicode ranges
- from: "\\u3001"
to: "\\u30FF"
@@ -115,7 +124,7 @@ other-settings:
right: 15
unifont_jp:
file: unifont_jp.zip # Load Japanese unifont
generate: false # Don't generate in the resource pack
generate: false # Should this unihex file be included in the generated pack
filter:
"jp": true # Apply to Japanese characters only
size_overrides:
@@ -132,7 +141,9 @@ other-settings:
codepoints: ascii # Define ASCII bitmap font
file: ascii.png # Use ascii.png file for the font
height: 8 # Font height is 8 units
custom: false # Not a custom font (default Minecraft)
custom: false # Does this png path point to a non-default font path??
generate: false # Should this png file be included in the generated pack
# namespace: custom # Optional namespace if you don't want to use the global namespace set in config.yml
ascii_sga:
codepoints: ascii_sga
file: ascii_sga.png
@@ -162,20 +173,21 @@ other-settings:
unicode:
file: unicode_page_%02x.png # Default Unicode characters
sizes: glyph_sizes.bin # Glyph size configuration
ttf:
example:
generate: false # Should this TTF file be included in the generated pack
file: example.ttf # Define the TTF file to load the font from
size: 10.0 # Set font size
oversample: 8.0 # Oversample rate for TTF rendering
skip: [] # Skipped characters in list format
ttf: {}
# ttf:
# example:
# generate: false # Should this TTF file be included in the generated pack
# file: example.ttf # Define the TTF file to load the font from
# size: 10.0 # Set font size
# oversample: 8.0 # Oversample rate for TTF rendering
# skip: [] # Skipped characters in list format
shift-fonts:
shift_0:
- space
- nonlatin_european:7
- accented:9
- ascii:7
- unifont_jp
#- unifont_jp
- unifont
shift_1:
- space
@@ -1,3 +1,5 @@
# Do not modify this value
config-version: '${config_version}'
# https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/config/bubble.yml
sender-requirements:
permission: bubbles.send
@@ -24,5 +26,5 @@ bubble-settings:
3: chat_3
line-width: 150
background-color: 0,0,0,0
text-prefix: "<black><font:nameplates:shift_0>"
text-prefix: "<black><font:{namespace}:shift_0>" # {namespace} would be replaced by the namespace set in config.yml
text-suffix: "</font></black>"
@@ -2,21 +2,37 @@
conditional-text:
actionbar:
priority_1:
text: '%np_background_other_actionbar%'
text: '%np_background_other_actionbar%' # hides the money hud when a player rides something or dives in water
conditions:
'||':
'!=':
value1: '%player_remaining_air%'
value2: "300"
'!gamemode': survival
condition_or:
type: "||"
refresh-interval: 1
value:
condition_passenger:
type: is-passenger
refresh-interval: 1
value: true
condition_air:
type: "!="
refresh-interval: 1
value:
value1: '%player_remaining_air%'
value2: "300"
condition_gamemode:
type: "!gamemode"
refresh-interval: 1
value: survival
priority_2:
text: '%np_static_money_hud%%np_offset_-179%%np_static_other_actionbar%'
text: '%np_static_money_hud%%np_offset_-179%%np_static_other_actionbar%' # displays the money hud + other plugin's actionbar message
conditions:
'!equals':
value1: '%np_actionbar%'
value2: ""
condition_other_actionbar:
type: "!equals"
refresh-interval: 1
value:
value1: '%np_actionbar%'
value2: ""
priority_3:
text: '%np_static_money_hud%'
text: '%np_static_money_hud%' # displays the money hud only
weather:
priority_1:
text: 'Sunny'
@@ -130,30 +146,32 @@ switch-text:
switch: "%np_equipped_nameplate%"
default: ""
case:
'wither': "<gradient:#FF0000:#000000:%np_gradient_3%>"
'halloween': "<gradient:#FF0000:#000000:%np_gradient_3%>"
'hutao': "<gradient:#FFFFFF:#FF4500:%np_gradient_3%>"
'rabbit': "<gradient:#FFFFFF:#DB7093:%np_gradient_3%>"
'starsky': "<gradient:#FFFFF0:#696969:%np_gradient_3%>"
'trident': "<gradient:#E1FFFF:#008B8B:%np_gradient_10%>"
'cheems': "<gradient:#C0C0C0:#F5F5F5:%np_gradient_5%>"
'egg': "<gradient:#FFFFFF:#FFA500:%np_gradient_5%>"
'cat': "<gradient:#FFFFFF:#1E90FF:%np_gradient_5%>"
'xmas': "<gradient:#FFFFFF:#FF4500:%np_gradient_5%>"
'xmas': "<WHITE>"
'angel': "<#8B4513>"
'sign': "<BLACK>"
'banner': "<WHITE>"
'enter': "<WHITE>"
nameplate_color_suffix:
switch: "%np_equipped_nameplate%"
default: ""
case:
'wither': "</gradient>"
'halloween': "</gradient>"
'hutao': "</gradient>"
'rabbit': "</gradient>"
'starsky': "</gradient>"
'trident': "</gradient>"
'cheems': "</gradient>"
'egg': "</gradient>"
'cat': "</gradient>"
'xmas': "</gradient>"
'xmas': "</WHITE>"
'angel': "</#8B4513>"
'sign': "</BLACK>"
'banner': "</WHITE>"
'enter': "</WHITE>"
world:
switch: '%player_world%'
case:
@@ -1,3 +1,5 @@
# Do not modify this value
config-version: '${config_version}'
# https://mo-mi.gitbook.io/xiaomomi-plugins/plugin-wiki/customnameplates/config/nameplate.yml
preview-duration: 5
default-nameplate: 'none'
@@ -7,7 +9,20 @@ nameplate:
player-name: '%np_shift_player_name%'
suffix: '%np_switch_nameplate_color_suffix%'
unlimited:
tag_1:
tag_normal:
text: '%np_tag-text%'
translation: 0,0.2,0
viewer-conditions: { }
owner-conditions:
has-nameplate: false
potion-effect: "INVISIBILITY<0"
self-disguised: false
affected-by-crouching: true
affected-by-scale-attribute: true
affected-by-spectator: true
line-width: 1024
background-color: 64,0,0,0
tag_image_part:
text: '%np_tag-image%'
translation: 0,0.2,0
viewer-conditions: { }
@@ -20,7 +35,7 @@ unlimited:
affected-by-spectator: true
line-width: 1024
background-color: 0,0,0,0
tag_2:
tag_text_part:
text: '%np_tag-text%'
translation: 0.01,0.2,0.01
viewer-conditions: { }
@@ -32,17 +47,4 @@ unlimited:
affected-by-scale-attribute: true
affected-by-spectator: true
line-width: 1024
background-color: 0,0,0,0
tag_3:
text: '%np_tag-text%'
translation: 0,0.2,0
viewer-conditions: { }
owner-conditions:
has-nameplate: false
potion-effect: "INVISIBILITY<0"
self-disguised: false
affected-by-crouching: true
affected-by-scale-attribute: true
affected-by-spectator: true
line-width: 1024
background-color: 64,0,0,0
background-color: 0,0,0,0
@@ -1,3 +1,4 @@
image: bell
height: 10
ascent: 4
ascent: 4
shadow: false
@@ -1,3 +1,4 @@
image: bubble
height: 10
ascent: 4
ascent: 4
shadow: false
@@ -1,3 +1,4 @@
image: clock
height: 10
ascent: 4
ascent: 4
shadow: false
@@ -1,3 +1,4 @@
image: coin
height: 10
ascent: -14
ascent: -14
shadow: false
@@ -1,3 +1,4 @@
image: compass
height: 10
ascent: 4
ascent: 4
shadow: false
@@ -1,3 +1,4 @@
image: stamina_0
height: 9
ascent: -16
ascent: -16
shadow: false
@@ -1,3 +1,4 @@
image: stamina_1
height: 9
ascent: -16
ascent: -16
shadow: false
@@ -1,3 +1,4 @@
image: stamina_2
height: 9
ascent: -16
ascent: -16
shadow: false
@@ -1,3 +1,4 @@
image: weather
height: 10
ascent: 4
ascent: 4
shadow: false
@@ -0,0 +1,13 @@
display-name: Angel
left:
image: angel_left
height: 16
ascent: 14
middle:
image: angel_middle
height: 16
ascent: 14
right:
image: angel_right
height: 16
ascent: 14
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1006 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -0,0 +1,13 @@
display-name: Banner
left:
image: banner_left
height: 16
ascent: 13
middle:
image: banner_middle
height: 16
ascent: 13
right:
image: banner_right
height: 16
ascent: 13
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1000 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 396 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

Loaded 100 of 243 files, more files were not shown because too many files have changed in this diff. Show more