Closed iimrudy closed 5 months ago
You're currently supposed to create a different Sidebar instance for every player. I plan on making this easier in the next major version
Alright tysm
On Tue, May 21, 2024, 7:03 AM vytskalt @.***> wrote:
You're currently supposed to create a different Sidebar instance for every player. I plan on making this easier in the next major version
— Reply to this email directly, view it on GitHub https://github.com/MegavexNetwork/scoreboard-library/issues/42#issuecomment-2121745677, or unsubscribe https://github.com/notifications/unsubscribe-auth/ANA27SBM55RZS6YUWBHLDLLZDLIRHAVCNFSM6AAAAABIALXEM6VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCMRRG42DKNRXG4 . You are receiving this because you authored the thread.Message ID: @.***>
Creating a scoreboard for each player is going to impact a lot on performance or ram memory?
i'm trying to keep resources as low as possible :)
Server performance wouldnt change because most work is being done off the main thread.
Memory I guess would be larger than a single sidebar, but most likely nowhere near where it would actually matter. You can create the sidebar like this to make the situation better: .createSidebar(Sidebar.MAX_LINES, Locale.US);
Would be interesting if you profiled it though
i'am going to try that, i'll post profilings asap
thanks for the help
Hello sorry to bother again,
i wrote a bunch of code where the personal scoreboard seems to work fine but the animated title it is not working anymore and i can't find why, may i ask you for a help?
PerPlayerSidebar.java
public class PerPlayerSidebar implements Listener, Runnable {
private final Map<Player, Tuple<Sidebar, ComponentSidebarLayout>> playersSidebarMap = new ConcurrentHashMap<>();
private final ScoreboardLibrary scoreboardLibrary;
private final BukkitTask task;
//@Getter
//@Setter
private PlayerSidebarComponentGenerator generator;
public PerPlayerSidebar(Plugin plugin, ScoreboardLibrary library, PlayerSidebarComponentGenerator generator) {
this.scoreboardLibrary = library;
this.generator = generator;
plugin.getServer().getPluginManager().registerEvents(this, plugin);
plugin.getServer().getOnlinePlayers().forEach(this::addPlayer);
this.task = plugin.getServer().getScheduler().runTaskTimer(plugin, this, 0, 1L);
}
@Override
public void run() {
for(var entry : playersSidebarMap.entrySet()) {
// per player apply
var tuple = entry.getValue();
tuple.getRight().apply(tuple.getLeft());
}
}
@EventHandler
private void onPlayerJoin(PlayerJoinEvent event) {
this.addPlayer(event.getPlayer());
}
private void addPlayer(Player p) {
var sidebar = this.scoreboardLibrary.createSidebar();
sidebar.addPlayer(p);
this.playersSidebarMap.put(p, new Tuple<>(sidebar, generator.createSidebarComponent(p)));
task.getOwner().getLogger().info("init scoreboard for" + p.getName());
}
@EventHandler
private void onPlayerQuitEvent(PlayerQuitEvent event) {
var tuple = this.playersSidebarMap.get(event.getPlayer());
if (!Objects.isNull(tuple)) {
tuple.getLeft().removePlayer(event.getPlayer());
this.playersSidebarMap.remove(event.getPlayer());
}
task.getOwner().getLogger().info("deinit scoreboard for" + event.getPlayer().getName());
}
public void destroy() {
this.task.cancel();
for(var entry : playersSidebarMap.entrySet()) {
var player = entry.getKey();
var tuple = entry.getValue();
tuple.getLeft().removePlayer(player);
}
this.playersSidebarMap.clear();
}
}
PlayerSidebarComponentGenerator.java
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
import net.kyori.adventure.text.minimessage.tag.resolver.TagResolver;
import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout;
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation;
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.List;
@FunctionalInterface
public interface PlayerSidebarComponentGenerator {
ComponentSidebarLayout createSidebarComponent(Player player);
default SidebarAnimation<Component> createGradientAnimation(String startColor, String endColor, Component text) {
float step = 1f / 8f;
TagResolver.Single textPlaceholder = Placeholder.component("text", text);
List<Component> frames = new ArrayList<>((int) (2f / step));
float phase = -1f;
while (phase < 1) {
frames.add(MiniMessage.miniMessage().deserialize("<gradient:" + startColor + ":" + endColor + ":" + phase + "><text>", textPlaceholder));
phase += step;
}
return new CollectionSidebarAnimation<>(frames);
}
}
LobbyScoreboardV2
public class LobbyScoreboardV2 implements PlayerSidebarComponentGenerator {
private static final PaperRanks ranks = new PaperRanks();
@Override
public ComponentSidebarLayout createSidebarComponent(Player player) {
// per player animation otherwise there will be frame skipping
var titleAnimation = createGradientAnimation("#23E7C6", "#690AC7", Component.text("TESTTTTT", Style.style(TextDecoration.BOLD)));
var title = SidebarComponent.animatedLine(titleAnimation);
// Custom SidebarComponent, see below for how an implementation might look like
SidebarComponent onlinePlayers = new KeyValueSidebarComponent(
Component.text("Online players"),
() -> Component.text(Bukkit.getServer().getOnlinePlayers().size())
);
//var serverName = new KeyValueSidebarComponent(Component.text("SERVER"), () -> Component.text(Core.getInstance().getServerName()));
//var serverCategory = new KeyValueSidebarComponent(Component.text("CAT"), () -> Component.text(Core.getInstance().getServerCategory().name()));
SidebarComponent lines = SidebarComponent.builder()
.addDynamicLine(() -> {
var rank = ranks.getRank(player);
return Component.text(rank.getPrefix(), NamedTextColor.GRAY);
})
.addDynamicLine(() -> {
var ping = player.getPing();
return Component.text("PING: " + ping);
})
.addBlankLine()
.addStaticLine(Component.text("TEST-server"))
.addStaticLine(Component.text("TEST"))
.addBlankLine()
.addComponent(onlinePlayers)
.addBlankLine()
.addStaticLine(Component.text("TEst", NamedTextColor.AQUA))
.build();
return new ComponentSidebarLayout(title, lines);
}
}
Tuple.java
import java.util.Objects;
public class Tuple <A, B> {
private final A left;
private final B right;
public Tuple(A a, B b) {
this.left = a;
this.right = b;
}
public A getLeft() {
return left;
}
public B getRight() {
return right;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tuple<?, ?> tuple = (Tuple<?, ?>) o;
return Objects.equals(left, tuple.left) && Objects.equals(right, tuple.right);
}
@Override
public int hashCode() {
return Objects.hash(left, right);
}
}
to initialize this add to the onEnable of the plugin
new PerPlayerSidebar(this, getScoreboardLibrary(), new LobbyScoreboardV2());
You need to be advancing the title animation by calling titleAnimation.nextFrame();
every tick or so
oh that make sense tysm, i tought it was automatic 😳
Hello i've made some tests with 100 players online (bots), the scoreboard ticker is async so no performance impact overall, very low memory usage (less then 700kb)
the only things that has a little bit of impact is the ScoreboardLibrary.createSidebar() but's has no relevance overall...
ill leave the spark reports
server was having 1gb of ram, using aikar flags, paper version: git-Paper-496 (MC: 1.20.4) (Implementing API version 1.20.4-R0.1-SNAPSHOT) (Git: 7ac24a1 on ver/1.20.4)
for my use-case i'm never going to have an paper instance with more then 100 players so i'm very happy with that results :)
That's great to hear
Hello how can i build a scoreboard like the one in the example with self player data like adding to example the player's HEALTH, or maybe adding rank etc..
thanks...