mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Scoreboard improvements (#2078)
* Fixed some Scoreboard bugs and ScoreboardUpdater is now global
* Begin implementing below name support and better name display
* Use final for classes
* Revert "Begin implementing below name support and better name display"
This reverts commit 01babd636a
.
* Don't remove objective if we're showing it
* Prevent concurrency exceptions when switching servers
* Properly fix the concurrency issue
* Fix inconsistencies in update cycle
* Few minor changes
* Port over this fix
* Fixed a problem that was introduced yesterday
* Cleanup
* Scores don't have to be removed before removing the objective itself
* Moved away from the general thread pool and some more changes
* Small changes
* Converted switch statements
Co-authored-by: Camotoy <20743703+Camotoy@users.noreply.github.com>
This commit is contained in:
parent
7f4d3def58
commit
3323e5732c
11 changed files with 384 additions and 284 deletions
|
@ -53,6 +53,7 @@ import org.geysermc.connector.network.translators.PacketTranslatorRegistry;
|
||||||
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
import org.geysermc.connector.network.translators.item.ItemTranslator;
|
||||||
import org.geysermc.connector.network.translators.world.WorldManager;
|
import org.geysermc.connector.network.translators.world.WorldManager;
|
||||||
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
||||||
|
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||||
import org.geysermc.connector.skin.FloodgateSkinUploader;
|
import org.geysermc.connector.skin.FloodgateSkinUploader;
|
||||||
import org.geysermc.connector.utils.*;
|
import org.geysermc.connector.utils.*;
|
||||||
import org.geysermc.floodgate.crypto.AesCipher;
|
import org.geysermc.floodgate.crypto.AesCipher;
|
||||||
|
@ -155,6 +156,7 @@ public class GeyserConnector {
|
||||||
ItemTranslator.init();
|
ItemTranslator.init();
|
||||||
MessageTranslator.init();
|
MessageTranslator.init();
|
||||||
LocaleUtils.init();
|
LocaleUtils.init();
|
||||||
|
ScoreboardUpdater.init();
|
||||||
|
|
||||||
ResourcePack.loadPacks();
|
ResourcePack.loadPacks();
|
||||||
|
|
||||||
|
|
|
@ -29,38 +29,33 @@ import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.scoreboard.Objective;
|
|
||||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
import org.geysermc.connector.scoreboard.ScoreboardUpdater.ScoreboardSession;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public class WorldCache {
|
public class WorldCache {
|
||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
|
private final ScoreboardSession scoreboardSession;
|
||||||
|
private Scoreboard scoreboard;
|
||||||
@Setter
|
@Setter
|
||||||
private Difficulty difficulty = Difficulty.EASY;
|
private Difficulty difficulty = Difficulty.EASY;
|
||||||
|
|
||||||
private Scoreboard scoreboard;
|
|
||||||
private final ScoreboardUpdater scoreboardUpdater;
|
|
||||||
|
|
||||||
public WorldCache(GeyserSession session) {
|
public WorldCache(GeyserSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.scoreboard = new Scoreboard(session);
|
this.scoreboard = new Scoreboard(session);
|
||||||
scoreboardUpdater = new ScoreboardUpdater(this);
|
scoreboardSession = new ScoreboardSession(session);
|
||||||
scoreboardUpdater.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeScoreboard() {
|
public void removeScoreboard() {
|
||||||
if (scoreboard != null) {
|
if (scoreboard != null) {
|
||||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
scoreboard.removeScoreboard();
|
||||||
scoreboard.despawnObjective(objective);
|
|
||||||
}
|
|
||||||
scoreboard = new Scoreboard(session);
|
scoreboard = new Scoreboard(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int increaseAndGetScoreboardPacketsPerSecond() {
|
public int increaseAndGetScoreboardPacketsPerSecond() {
|
||||||
int pendingPps = scoreboardUpdater.incrementAndGetPacketsPerSecond();
|
int pendingPps = scoreboardSession.getPendingPacketsPerSecond().incrementAndGet();
|
||||||
int pps = scoreboardUpdater.getPacketsPerSecond();
|
int pps = scoreboardSession.getPacketsPerSecond();
|
||||||
return Math.max(pps, pendingPps);
|
return Math.max(pps, pendingPps);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,12 +25,11 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
|
|
||||||
|
|
||||||
@Translator(packet = ServerDisplayScoreboardPacket.class)
|
@Translator(packet = ServerDisplayScoreboardPacket.class)
|
||||||
public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisplayScoreboardPacket> {
|
public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisplayScoreboardPacket> {
|
||||||
|
|
||||||
|
|
|
@ -25,30 +25,39 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.scoreboard.ObjectiveAction;
|
||||||
|
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
|
||||||
|
import org.geysermc.connector.GeyserConnector;
|
||||||
|
import org.geysermc.connector.GeyserLogger;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.WorldCache;
|
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
import org.geysermc.connector.scoreboard.Objective;
|
import org.geysermc.connector.scoreboard.Objective;
|
||||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
import org.geysermc.connector.scoreboard.UpdateType;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ObjectiveAction;
|
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerScoreboardObjectivePacket;
|
|
||||||
|
|
||||||
@Translator(packet = ServerScoreboardObjectivePacket.class)
|
@Translator(packet = ServerScoreboardObjectivePacket.class)
|
||||||
public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerScoreboardObjectivePacket> {
|
public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerScoreboardObjectivePacket> {
|
||||||
|
private final GeyserLogger logger = GeyserConnector.getInstance().getLogger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ServerScoreboardObjectivePacket packet) {
|
public void translate(GeyserSession session, ServerScoreboardObjectivePacket packet) {
|
||||||
WorldCache worldCache = session.getWorldCache();
|
WorldCache worldCache = session.getWorldCache();
|
||||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||||
Objective objective = scoreboard.getObjective(packet.getName());
|
|
||||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||||
|
|
||||||
if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) {
|
Objective objective = scoreboard.getObjective(packet.getName());
|
||||||
objective = scoreboard.registerNewObjective(packet.getName(), false);
|
if (objective != null && objective.getUpdateType() != UpdateType.REMOVE && packet.getAction() == ObjectiveAction.ADD) {
|
||||||
|
// matches vanilla behaviour
|
||||||
|
logger.warning("An objective with the same name '" + packet.getName() + "' already exists! Ignoring packet");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((objective == null || objective.getUpdateType() == UpdateType.REMOVE) && packet.getAction() != ObjectiveAction.REMOVE) {
|
||||||
|
objective = scoreboard.registerNewObjective(packet.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
|
|
|
@ -25,26 +25,25 @@
|
||||||
|
|
||||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||||
|
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamAction;
|
||||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
|
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerTeamPacket;
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.GeyserLogger;
|
import org.geysermc.connector.GeyserLogger;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||||
import org.geysermc.connector.network.translators.Translator;
|
import org.geysermc.connector.network.translators.Translator;
|
||||||
|
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||||
import org.geysermc.connector.scoreboard.Team;
|
import org.geysermc.connector.scoreboard.Team;
|
||||||
import org.geysermc.connector.scoreboard.UpdateType;
|
import org.geysermc.connector.scoreboard.UpdateType;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Translator(packet = ServerTeamPacket.class)
|
@Translator(packet = ServerTeamPacket.class)
|
||||||
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||||
private static final GeyserLogger LOGGER = GeyserConnector.getInstance().getLogger();
|
private final GeyserLogger LOGGER = GeyserConnector.getInstance().getLogger();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void translate(GeyserSession session, ServerTeamPacket packet) {
|
public void translate(GeyserSession session, ServerTeamPacket packet) {
|
||||||
|
@ -52,12 +51,16 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||||
LOGGER.debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
LOGGER.debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((packet.getAction() == TeamAction.ADD_PLAYER || packet.getAction() == TeamAction.REMOVE_PLAYER) && packet.getPlayers().length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond();
|
int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond();
|
||||||
|
|
||||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||||
Team team = scoreboard.getTeam(packet.getTeamName());
|
Team team = scoreboard.getTeam(packet.getTeamName());
|
||||||
switch (packet.getAction()) {
|
switch (packet.getAction()) {
|
||||||
case CREATE -> scoreboard.registerNewTeam(packet.getTeamName(), toPlayerSet(packet.getPlayers()))
|
case CREATE -> scoreboard.registerNewTeam(packet.getTeamName(), packet.getPlayers())
|
||||||
.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
.setName(MessageTranslator.convertMessage(packet.getDisplayName()))
|
||||||
.setColor(packet.getColor())
|
.setColor(packet.getColor())
|
||||||
.setNameTagVisibility(packet.getNameTagVisibility())
|
.setNameTagVisibility(packet.getNameTagVisibility())
|
||||||
|
@ -108,8 +111,4 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||||
scoreboard.onUpdate();
|
scoreboard.onUpdate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<String> toPlayerSet(String[] players) {
|
|
||||||
return new ObjectOpenHashSet<>(players);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,7 +66,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScor
|
||||||
if (objective != null) {
|
if (objective != null) {
|
||||||
objective.removeScore(packet.getEntry());
|
objective.removeScore(packet.getEntry());
|
||||||
} else {
|
} else {
|
||||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
for (Objective objective1 : scoreboard.getObjectives()) {
|
||||||
objective1.removeScore(packet.getEntry());
|
objective1.removeScore(packet.getEntry());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
package org.geysermc.connector.scoreboard;
|
package org.geysermc.connector.scoreboard;
|
||||||
|
|
||||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
|
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||||
|
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamColor;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ public final class Objective {
|
||||||
public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) {
|
public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) {
|
||||||
this(scoreboard);
|
this(scoreboard);
|
||||||
this.objectiveName = objectiveName;
|
this.objectiveName = objectiveName;
|
||||||
this.displaySlot = correctDisplaySlot(displaySlot);
|
this.displaySlot = displaySlot;
|
||||||
this.displaySlotName = translateDisplaySlot(displaySlot);
|
this.displaySlotName = translateDisplaySlot(displaySlot);
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
@ -83,14 +84,6 @@ public final class Objective {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ScoreboardPosition correctDisplaySlot(ScoreboardPosition displaySlot) {
|
|
||||||
return switch (displaySlot) {
|
|
||||||
case BELOW_NAME -> ScoreboardPosition.BELOW_NAME;
|
|
||||||
case PLAYER_LIST -> ScoreboardPosition.PLAYER_LIST;
|
|
||||||
default -> ScoreboardPosition.SIDEBAR;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void registerScore(String id, int score) {
|
public void registerScore(String id, int score) {
|
||||||
if (!scores.containsKey(id)) {
|
if (!scores.containsKey(id)) {
|
||||||
long scoreId = scoreboard.getNextId().getAndIncrement();
|
long scoreId = scoreboard.getNextId().getAndIncrement();
|
||||||
|
@ -145,12 +138,57 @@ public final class Objective {
|
||||||
public void setActive(ScoreboardPosition displaySlot) {
|
public void setActive(ScoreboardPosition displaySlot) {
|
||||||
if (!active) {
|
if (!active) {
|
||||||
active = true;
|
active = true;
|
||||||
this.displaySlot = correctDisplaySlot(displaySlot);
|
this.displaySlot = displaySlot;
|
||||||
displaySlotName = translateDisplaySlot(displaySlot);
|
displaySlotName = translateDisplaySlot(displaySlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The objective will be removed on the next update
|
||||||
|
*/
|
||||||
|
public void pendingRemove() {
|
||||||
|
updateType = UpdateType.REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScoreboardPosition getPositionCategory() {
|
||||||
|
return switch (displaySlot) {
|
||||||
|
case PLAYER_LIST -> ScoreboardPosition.PLAYER_LIST;
|
||||||
|
case BELOW_NAME -> ScoreboardPosition.BELOW_NAME;
|
||||||
|
default -> ScoreboardPosition.SIDEBAR;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasTeamColor() {
|
||||||
|
return displaySlot != ScoreboardPosition.PLAYER_LIST &&
|
||||||
|
displaySlot != ScoreboardPosition.BELOW_NAME &&
|
||||||
|
displaySlot != ScoreboardPosition.SIDEBAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TeamColor getTeamColor() {
|
||||||
|
return switch (displaySlot) {
|
||||||
|
case SIDEBAR_TEAM_RED -> TeamColor.RED;
|
||||||
|
case SIDEBAR_TEAM_AQUA -> TeamColor.AQUA;
|
||||||
|
case SIDEBAR_TEAM_BLUE -> TeamColor.BLUE;
|
||||||
|
case SIDEBAR_TEAM_GOLD -> TeamColor.GOLD;
|
||||||
|
case SIDEBAR_TEAM_GRAY -> TeamColor.GRAY;
|
||||||
|
case SIDEBAR_TEAM_BLACK -> TeamColor.BLACK;
|
||||||
|
case SIDEBAR_TEAM_GREEN -> TeamColor.GREEN;
|
||||||
|
case SIDEBAR_TEAM_WHITE -> TeamColor.WHITE;
|
||||||
|
case SIDEBAR_TEAM_YELLOW -> TeamColor.YELLOW;
|
||||||
|
case SIDEBAR_TEAM_DARK_RED -> TeamColor.DARK_RED;
|
||||||
|
case SIDEBAR_TEAM_DARK_AQUA -> TeamColor.DARK_AQUA;
|
||||||
|
case SIDEBAR_TEAM_DARK_BLUE -> TeamColor.DARK_BLUE;
|
||||||
|
case SIDEBAR_TEAM_DARK_GRAY -> TeamColor.DARK_GRAY;
|
||||||
|
case SIDEBAR_TEAM_DARK_GREEN -> TeamColor.DARK_GREEN;
|
||||||
|
case SIDEBAR_TEAM_DARK_PURPLE -> TeamColor.DARK_PURPLE;
|
||||||
|
case SIDEBAR_TEAM_LIGHT_PURPLE -> TeamColor.LIGHT_PURPLE;
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public void removed() {
|
public void removed() {
|
||||||
|
active = false;
|
||||||
|
updateType = UpdateType.REMOVE;
|
||||||
scores = null;
|
scores = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ public final class Score {
|
||||||
/**
|
/**
|
||||||
* Changes that have been made since the last cached data.
|
* Changes that have been made since the last cached data.
|
||||||
*/
|
*/
|
||||||
private Score.ScoreData currentData;
|
private final Score.ScoreData currentData;
|
||||||
/**
|
/**
|
||||||
* The data that is currently displayed to the Bedrock client.
|
* The data that is currently displayed to the Bedrock client.
|
||||||
*/
|
*/
|
||||||
|
@ -72,14 +72,14 @@ public final class Score {
|
||||||
if (currentData.team != null && team != null) {
|
if (currentData.team != null && team != null) {
|
||||||
if (!currentData.team.equals(team)) {
|
if (!currentData.team.equals(team)) {
|
||||||
currentData.team = team;
|
currentData.team = team;
|
||||||
currentData.updateType = UpdateType.UPDATE;
|
setUpdateType(UpdateType.UPDATE);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
// simplified from (this.team != null && team == null) || (this.team == null && team != null)
|
// simplified from (this.team != null && team == null) || (this.team == null && team != null)
|
||||||
if (currentData.team != null || team != null) {
|
if (currentData.team != null || team != null) {
|
||||||
currentData.team = team;
|
currentData.team = team;
|
||||||
currentData.updateType = UpdateType.UPDATE;
|
setUpdateType(UpdateType.UPDATE);
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -126,13 +126,13 @@ public final class Score {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public static final class ScoreData {
|
public static final class ScoreData {
|
||||||
protected UpdateType updateType;
|
private UpdateType updateType;
|
||||||
protected long updateTime;
|
private long updateTime;
|
||||||
|
|
||||||
private Team team;
|
private Team team;
|
||||||
private int score;
|
private int score;
|
||||||
|
|
||||||
protected ScoreData() {
|
private ScoreData() {
|
||||||
updateType = UpdateType.ADD;
|
updateType = UpdateType.ADD;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,14 +42,15 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||||
|
|
||||||
import static org.geysermc.connector.scoreboard.UpdateType.*;
|
import static org.geysermc.connector.scoreboard.UpdateType.*;
|
||||||
|
|
||||||
@Getter
|
|
||||||
public final class Scoreboard {
|
public final class Scoreboard {
|
||||||
private final GeyserSession session;
|
private final GeyserSession session;
|
||||||
private final GeyserLogger logger;
|
private final GeyserLogger logger;
|
||||||
|
@Getter
|
||||||
private final AtomicLong nextId = new AtomicLong(0);
|
private final AtomicLong nextId = new AtomicLong(0);
|
||||||
|
|
||||||
private final Map<String, Objective> objectives = new ConcurrentHashMap<>();
|
private final Map<String, Objective> objectives = new ConcurrentHashMap<>();
|
||||||
private final Map<String, Team> teams = new HashMap<>();
|
private final Map<ScoreboardPosition, Objective> objectiveSlots = new HashMap<>();
|
||||||
|
private final Map<String, Team> teams = new ConcurrentHashMap<>(); // updated on multiple threads
|
||||||
|
|
||||||
private int lastAddScoreCount = 0;
|
private int lastAddScoreCount = 0;
|
||||||
private int lastRemoveScoreCount = 0;
|
private int lastRemoveScoreCount = 0;
|
||||||
|
@ -59,45 +60,52 @@ public final class Scoreboard {
|
||||||
this.logger = GeyserConnector.getInstance().getLogger();
|
this.logger = GeyserConnector.getInstance().getLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Objective registerNewObjective(String objectiveId, boolean active) {
|
public void removeScoreboard() {
|
||||||
Objective objective = objectives.get(objectiveId);
|
Iterator<Objective> iterator = objectives.values().iterator();
|
||||||
if (active || objective != null) {
|
while (iterator.hasNext()) {
|
||||||
return objective;
|
Objective objective = iterator.next();
|
||||||
|
iterator.remove();
|
||||||
|
|
||||||
|
deleteObjective(objective, false);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Objective registerNewObjective(String objectiveId) {
|
||||||
|
Objective objective = objectives.get(objectiveId);
|
||||||
|
if (objective != null) {
|
||||||
|
// we have no other choice, or we have to make a new map?
|
||||||
|
// if the objective hasn't been deleted, we have to force it
|
||||||
|
if (objective.getUpdateType() != REMOVE) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
deleteObjective(objective, true);
|
||||||
|
}
|
||||||
|
|
||||||
objective = new Objective(this, objectiveId);
|
objective = new Objective(this, objectiveId);
|
||||||
objectives.put(objectiveId, objective);
|
objectives.put(objectiveId, objective);
|
||||||
return objective;
|
return objective;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Objective displayObjective(String objectiveId, ScoreboardPosition displaySlot) {
|
public void displayObjective(String objectiveId, ScoreboardPosition displaySlot) {
|
||||||
Objective objective = objectives.get(objectiveId);
|
Objective objective = objectives.get(objectiveId);
|
||||||
if (objective != null) {
|
if (objective == null) {
|
||||||
if (!objective.isActive()) {
|
return;
|
||||||
objective.setActive(displaySlot);
|
|
||||||
removeOldObjectives(objective);
|
|
||||||
return objective;
|
|
||||||
}
|
|
||||||
despawnObjective(objective);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
objective = new Objective(this, objectiveId, displaySlot, "unknown", 0);
|
if (!objective.isActive()) {
|
||||||
objectives.put(objectiveId, objective);
|
objective.setActive(displaySlot);
|
||||||
removeOldObjectives(objective);
|
// for reactivated objectives
|
||||||
return objective;
|
objective.setUpdateType(ADD);
|
||||||
}
|
|
||||||
|
|
||||||
private void removeOldObjectives(Objective newObjective) {
|
|
||||||
for (Objective next : objectives.values()) {
|
|
||||||
if (next.getId() == newObjective.getId()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (next.getDisplaySlot() == newObjective.getDisplaySlot()) {
|
|
||||||
next.setUpdateType(REMOVE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Objective storedObjective = objectiveSlots.get(displaySlot);
|
||||||
|
if (storedObjective != null && storedObjective != objective) {
|
||||||
|
objective.pendingRemove();
|
||||||
|
}
|
||||||
|
objectiveSlots.put(displaySlot, objective);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Team registerNewTeam(String teamName, Set<String> players) {
|
public Team registerNewTeam(String teamName, String[] players) {
|
||||||
Team team = teams.get(teamName);
|
Team team = teams.get(teamName);
|
||||||
if (team != null) {
|
if (team != null) {
|
||||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName));
|
logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName));
|
||||||
|
@ -113,6 +121,10 @@ public final class Scoreboard {
|
||||||
return objectives.get(objectiveName);
|
return objectives.get(objectiveName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Collection<Objective> getObjectives() {
|
||||||
|
return objectives.values();
|
||||||
|
}
|
||||||
|
|
||||||
public Team getTeam(String teamName) {
|
public Team getTeam(String teamName) {
|
||||||
return teams.get(teamName);
|
return teams.get(teamName);
|
||||||
}
|
}
|
||||||
|
@ -120,7 +132,7 @@ public final class Scoreboard {
|
||||||
public void unregisterObjective(String objectiveName) {
|
public void unregisterObjective(String objectiveName) {
|
||||||
Objective objective = getObjective(objectiveName);
|
Objective objective = getObjective(objectiveName);
|
||||||
if (objective != null) {
|
if (objective != null) {
|
||||||
objective.setUpdateType(REMOVE);
|
objective.pendingRemove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,114 +144,38 @@ public final class Scoreboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpdate() {
|
public void onUpdate() {
|
||||||
List<ScoreInfo> addScores = new ArrayList<>(getLastAddScoreCount());
|
List<ScoreInfo> addScores = new ArrayList<>(lastAddScoreCount);
|
||||||
List<ScoreInfo> removeScores = new ArrayList<>(getLastRemoveScoreCount());
|
List<ScoreInfo> removeScores = new ArrayList<>(lastRemoveScoreCount);
|
||||||
List<Objective> removedObjectives = new ArrayList<>();
|
List<Objective> removedObjectives = new ArrayList<>();
|
||||||
|
|
||||||
|
Team playerTeam = getTeamFor(session.getPlayerEntity().getUsername());
|
||||||
|
Objective correctSidebar = null;
|
||||||
|
|
||||||
for (Objective objective : objectives.values()) {
|
for (Objective objective : objectives.values()) {
|
||||||
if (!objective.isActive()) {
|
// objective has been deleted
|
||||||
logger.debug("Ignoring non-active Scoreboard Objective '" + objective.getObjectiveName() + '\'');
|
if (objective.getUpdateType() == REMOVE) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hearts can't hold teams, so we treat them differently
|
|
||||||
if (objective.getType() == 1) {
|
|
||||||
for (Score score : objective.getScores().values()) {
|
|
||||||
boolean update = score.shouldUpdate();
|
|
||||||
|
|
||||||
if (update) {
|
|
||||||
score.update(objective.getObjectiveName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score.getUpdateType() != REMOVE && update) {
|
|
||||||
addScores.add(score.getCachedInfo());
|
|
||||||
}
|
|
||||||
if (score.getUpdateType() != ADD && update) {
|
|
||||||
removeScores.add(score.getCachedInfo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean objectiveUpdate = objective.getUpdateType() == UPDATE;
|
|
||||||
boolean objectiveAdd = objective.getUpdateType() == ADD;
|
|
||||||
boolean objectiveRemove = objective.getUpdateType() == REMOVE;
|
|
||||||
|
|
||||||
for (Score score : objective.getScores().values()) {
|
|
||||||
Team team = score.getTeam();
|
|
||||||
|
|
||||||
boolean add = objectiveAdd || objectiveUpdate;
|
|
||||||
boolean remove = false;
|
|
||||||
if (team != null) {
|
|
||||||
if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) {
|
|
||||||
score.setTeam(null);
|
|
||||||
add = true;
|
|
||||||
remove = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
add |= score.shouldUpdate();
|
|
||||||
remove |= score.shouldUpdate();
|
|
||||||
|
|
||||||
if (score.getUpdateType() == REMOVE || objectiveRemove) {
|
|
||||||
add = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (score.getUpdateType() == ADD || objectiveRemove) {
|
|
||||||
remove = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectiveRemove && score.getCachedData() != null) {
|
|
||||||
// This score has been sent to the client and needs to be removed since the objective is being removed
|
|
||||||
remove = true;
|
|
||||||
} else if (score.shouldUpdate()) {
|
|
||||||
score.update(objective.getObjectiveName());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (add) {
|
|
||||||
addScores.add(score.getCachedInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (remove) {
|
|
||||||
removeScores.add(score.getCachedInfo());
|
|
||||||
}
|
|
||||||
|
|
||||||
// score is pending to be removed, so we can remove it from the objective
|
|
||||||
if (score.getUpdateType() == REMOVE) {
|
|
||||||
objective.removeScore0(score.getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
score.setUpdateType(NOTHING);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectiveRemove) {
|
|
||||||
removedObjectives.add(objective);
|
removedObjectives.add(objective);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (objectiveUpdate) {
|
if (playerTeam != null && playerTeam.getColor() == objective.getTeamColor()) {
|
||||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
correctSidebar = objective;
|
||||||
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
|
||||||
session.sendUpstreamPacket(removeObjectivePacket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((objectiveAdd || objectiveUpdate) && !objectiveRemove) {
|
|
||||||
SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket();
|
|
||||||
displayObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
|
||||||
displayObjectivePacket.setDisplayName(objective.getDisplayName());
|
|
||||||
displayObjectivePacket.setCriteria("dummy");
|
|
||||||
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName());
|
|
||||||
displayObjectivePacket.setSortOrder(1); // ??
|
|
||||||
session.sendUpstreamPacket(displayObjectivePacket);
|
|
||||||
}
|
|
||||||
|
|
||||||
objective.setUpdateType(NOTHING);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (correctSidebar == null) {
|
||||||
|
correctSidebar = objectiveSlots.get(ScoreboardPosition.SIDEBAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
handleObjective(objectiveSlots.get(ScoreboardPosition.PLAYER_LIST), addScores, removeScores);
|
||||||
|
handleObjective(correctSidebar, addScores, removeScores);
|
||||||
|
handleObjective(objectiveSlots.get(ScoreboardPosition.BELOW_NAME), addScores, removeScores);
|
||||||
|
|
||||||
Iterator<Team> teamIterator = teams.values().iterator();
|
Iterator<Team> teamIterator = teams.values().iterator();
|
||||||
while (teamIterator.hasNext()) {
|
while (teamIterator.hasNext()) {
|
||||||
Team current = teamIterator.next();
|
Team current = teamIterator.next();
|
||||||
|
|
||||||
switch (current.getUpdateType()) {
|
switch (current.getCachedUpdateType()) {
|
||||||
case ADD, UPDATE -> current.markUpdated();
|
case ADD, UPDATE -> current.markUpdated();
|
||||||
case REMOVE -> teamIterator.remove();
|
case REMOVE -> teamIterator.remove();
|
||||||
}
|
}
|
||||||
|
@ -259,17 +195,100 @@ public final class Scoreboard {
|
||||||
session.sendUpstreamPacket(setScorePacket);
|
session.sendUpstreamPacket(setScorePacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevents crashes in some cases
|
|
||||||
for (Objective objective : removedObjectives) {
|
for (Objective objective : removedObjectives) {
|
||||||
despawnObjective(objective);
|
deleteObjective(objective, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
lastAddScoreCount = addScores.size();
|
lastAddScoreCount = addScores.size();
|
||||||
lastRemoveScoreCount = removeScores.size();
|
lastRemoveScoreCount = removeScores.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void despawnObjective(Objective objective) {
|
private void handleObjective(Objective objective, List<ScoreInfo> addScores, List<ScoreInfo> removeScores) {
|
||||||
objectives.remove(objective.getObjectiveName());
|
if (objective == null || objective.getUpdateType() == REMOVE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hearts can't hold teams, so we treat them differently
|
||||||
|
if (objective.getType() == 1) {
|
||||||
|
for (Score score : objective.getScores().values()) {
|
||||||
|
boolean update = score.shouldUpdate();
|
||||||
|
|
||||||
|
if (update) {
|
||||||
|
score.update(objective.getObjectiveName());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score.getUpdateType() != REMOVE && update) {
|
||||||
|
addScores.add(score.getCachedInfo());
|
||||||
|
}
|
||||||
|
if (score.getUpdateType() != ADD && update) {
|
||||||
|
removeScores.add(score.getCachedInfo());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean objectiveAdd = objective.getUpdateType() == ADD;
|
||||||
|
boolean objectiveUpdate = objective.getUpdateType() == UPDATE;
|
||||||
|
|
||||||
|
for (Score score : objective.getScores().values()) {
|
||||||
|
if (score.getUpdateType() == REMOVE) {
|
||||||
|
removeScores.add(score.getCachedInfo());
|
||||||
|
// score is pending to be removed, so we can remove it from the objective
|
||||||
|
objective.removeScore0(score.getName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Team team = score.getTeam();
|
||||||
|
|
||||||
|
boolean add = objectiveAdd || objectiveUpdate;
|
||||||
|
|
||||||
|
if (team != null) {
|
||||||
|
if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) {
|
||||||
|
score.setTeam(null);
|
||||||
|
add = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (score.shouldUpdate()) {
|
||||||
|
score.update(objective.getObjectiveName());
|
||||||
|
add = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (add) {
|
||||||
|
addScores.add(score.getCachedInfo());
|
||||||
|
}
|
||||||
|
|
||||||
|
score.setUpdateType(NOTHING);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectiveUpdate) {
|
||||||
|
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||||
|
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||||
|
session.sendUpstreamPacket(removeObjectivePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objectiveAdd || objectiveUpdate) {
|
||||||
|
SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket();
|
||||||
|
displayObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||||
|
displayObjectivePacket.setDisplayName(objective.getDisplayName());
|
||||||
|
displayObjectivePacket.setCriteria("dummy");
|
||||||
|
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName());
|
||||||
|
displayObjectivePacket.setSortOrder(1); // 0 = ascending, 1 = descending
|
||||||
|
session.sendUpstreamPacket(displayObjectivePacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
objective.setUpdateType(NOTHING);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param remove if we should remove the objective from the objectives map.
|
||||||
|
*/
|
||||||
|
public void deleteObjective(Objective objective, boolean remove) {
|
||||||
|
if (remove) {
|
||||||
|
objectives.remove(objective.getObjectiveName());
|
||||||
|
}
|
||||||
|
objectiveSlots.remove(objective.getDisplaySlot(), objective);
|
||||||
|
|
||||||
objective.removed();
|
objective.removed();
|
||||||
|
|
||||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||||
|
|
|
@ -25,100 +25,149 @@
|
||||||
|
|
||||||
package org.geysermc.connector.scoreboard;
|
package org.geysermc.connector.scoreboard;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.geysermc.connector.GeyserConnector;
|
import org.geysermc.connector.GeyserConnector;
|
||||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||||
import org.geysermc.connector.network.session.GeyserSession;
|
import org.geysermc.connector.network.session.GeyserSession;
|
||||||
import org.geysermc.connector.network.session.cache.WorldCache;
|
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||||
import org.geysermc.connector.utils.LanguageUtils;
|
import org.geysermc.connector.utils.LanguageUtils;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
public class ScoreboardUpdater extends Thread {
|
public final class ScoreboardUpdater extends Thread {
|
||||||
public static final int FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
public static final int FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
||||||
public static final int SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD = 250;
|
public static final int SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD = 250;
|
||||||
|
|
||||||
private static final int FIRST_MILLIS_BETWEEN_UPDATES = 250; // 4 updates per second
|
private static final int FIRST_MILLIS_BETWEEN_UPDATES = 250; // 4 updates per second
|
||||||
private static final int SECOND_MILLIS_BETWEEN_UPDATES = 1000 * 3; // 1 update per 3 seconds
|
private static final int SECOND_MILLIS_BETWEEN_UPDATES = 1000; // 1 update per second
|
||||||
|
|
||||||
private static final boolean DEBUG_ENABLED;
|
private static final boolean DEBUG_ENABLED;
|
||||||
|
|
||||||
private final WorldCache worldCache;
|
|
||||||
private final GeyserSession session;
|
|
||||||
|
|
||||||
private int millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES;
|
|
||||||
private long lastUpdate = System.currentTimeMillis();
|
|
||||||
private long lastLog = -1;
|
|
||||||
|
|
||||||
private long lastPacketsPerSecondUpdate = System.currentTimeMillis();
|
|
||||||
private final AtomicInteger packetsPerSecond = new AtomicInteger(0);
|
|
||||||
private final AtomicInteger pendingPacketsPerSecond = new AtomicInteger(0);
|
|
||||||
|
|
||||||
public ScoreboardUpdater(WorldCache worldCache) {
|
|
||||||
super("Scoreboard Updater");
|
|
||||||
this.worldCache = worldCache;
|
|
||||||
session = worldCache.getSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (!session.isClosed()) {
|
|
||||||
long currentTime = System.currentTimeMillis();
|
|
||||||
|
|
||||||
// reset score-packets per second every second
|
|
||||||
if (currentTime - lastPacketsPerSecondUpdate > 1000) {
|
|
||||||
lastPacketsPerSecondUpdate = currentTime;
|
|
||||||
packetsPerSecond.set(pendingPacketsPerSecond.get());
|
|
||||||
pendingPacketsPerSecond.set(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentTime - lastUpdate > millisBetweenUpdates) {
|
|
||||||
lastUpdate = currentTime;
|
|
||||||
|
|
||||||
int pps = packetsPerSecond.get();
|
|
||||||
if (pps >= FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
|
||||||
boolean reachedSecondThreshold = pps >= SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
|
||||||
if (reachedSecondThreshold) {
|
|
||||||
millisBetweenUpdates = SECOND_MILLIS_BETWEEN_UPDATES;
|
|
||||||
} else {
|
|
||||||
millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES;
|
|
||||||
}
|
|
||||||
|
|
||||||
worldCache.getScoreboard().onUpdate();
|
|
||||||
|
|
||||||
if (DEBUG_ENABLED && (currentTime - lastLog > 60000)) { // one minute
|
|
||||||
int threshold = reachedSecondThreshold ?
|
|
||||||
SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD :
|
|
||||||
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
|
||||||
|
|
||||||
GeyserConnector.getInstance().getLogger().info(
|
|
||||||
LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.getName(), threshold, pps) +
|
|
||||||
LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached", (millisBetweenUpdates / 1000.0))
|
|
||||||
);
|
|
||||||
|
|
||||||
lastLog = currentTime;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
session.getConnector().getGeneralThreadPool().schedule(this, 50, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPacketsPerSecond() {
|
|
||||||
return packetsPerSecond.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Increase the Scoreboard Packets Per Second and return the updated value
|
|
||||||
*/
|
|
||||||
public int incrementAndGetPacketsPerSecond() {
|
|
||||||
return pendingPacketsPerSecond.incrementAndGet();
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
GeyserConfiguration config = GeyserConnector.getInstance().getConfig();
|
GeyserConfiguration config = GeyserConnector.getInstance().getConfig();
|
||||||
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
|
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
|
||||||
DEBUG_ENABLED = config.isDebugMode();
|
DEBUG_ENABLED = config.isDebugMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final GeyserConnector connector = GeyserConnector.getInstance();
|
||||||
|
|
||||||
|
private long lastUpdate = System.currentTimeMillis();
|
||||||
|
private long lastPacketsPerSecondUpdate = System.currentTimeMillis();
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
new ScoreboardUpdater().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!connector.isShuttingDown()) {
|
||||||
|
long timeTillAction = getTimeTillNextAction();
|
||||||
|
if (timeTillAction > 0) {
|
||||||
|
sleepFor(timeTillAction);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
// reset score-packets per second every second
|
||||||
|
if (currentTime - lastPacketsPerSecondUpdate >= 1000) {
|
||||||
|
lastPacketsPerSecondUpdate = currentTime;
|
||||||
|
for (GeyserSession session : connector.getPlayers()) {
|
||||||
|
ScoreboardSession scoreboardSession = session.getWorldCache().getScoreboardSession();
|
||||||
|
|
||||||
|
int oldPps = scoreboardSession.getPacketsPerSecond();
|
||||||
|
int newPps = scoreboardSession.getPendingPacketsPerSecond().get();
|
||||||
|
|
||||||
|
scoreboardSession.packetsPerSecond = newPps;
|
||||||
|
scoreboardSession.pendingPacketsPerSecond.set(0);
|
||||||
|
|
||||||
|
// just making sure that all updates are pushed before giving up control
|
||||||
|
if (oldPps >= FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD &&
|
||||||
|
newPps < FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||||
|
session.getWorldCache().getScoreboard().onUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentTime - lastUpdate >= FIRST_MILLIS_BETWEEN_UPDATES) {
|
||||||
|
lastUpdate = currentTime;
|
||||||
|
|
||||||
|
for (GeyserSession session : connector.getPlayers()) {
|
||||||
|
WorldCache worldCache = session.getWorldCache();
|
||||||
|
ScoreboardSession scoreboardSession = worldCache.getScoreboardSession();
|
||||||
|
|
||||||
|
int pps = scoreboardSession.getPacketsPerSecond();
|
||||||
|
if (pps >= FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||||
|
boolean reachedSecondThreshold = pps >= SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
||||||
|
|
||||||
|
int millisBetweenUpdates = reachedSecondThreshold ?
|
||||||
|
SECOND_MILLIS_BETWEEN_UPDATES :
|
||||||
|
FIRST_MILLIS_BETWEEN_UPDATES;
|
||||||
|
|
||||||
|
if (currentTime - scoreboardSession.lastUpdate >= millisBetweenUpdates) {
|
||||||
|
worldCache.getScoreboard().onUpdate();
|
||||||
|
scoreboardSession.lastUpdate = currentTime;
|
||||||
|
|
||||||
|
if (DEBUG_ENABLED && (currentTime - scoreboardSession.lastLog >= 60000)) { // one minute
|
||||||
|
int threshold = reachedSecondThreshold ?
|
||||||
|
SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD :
|
||||||
|
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
||||||
|
|
||||||
|
connector.getLogger().info(
|
||||||
|
LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached.log", session.getName(), threshold, pps) +
|
||||||
|
LanguageUtils.getLocaleStringLog("geyser.scoreboard.updater.threshold_reached", (millisBetweenUpdates / 1000.0))
|
||||||
|
);
|
||||||
|
|
||||||
|
scoreboardSession.lastLog = currentTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_ENABLED) {
|
||||||
|
long timeSpent = System.currentTimeMillis() - currentTime;
|
||||||
|
if (timeSpent > 0) {
|
||||||
|
connector.getLogger().info(String.format(
|
||||||
|
"Scoreboard updater: took %s ms. Updated %s players",
|
||||||
|
timeSpent, connector.getPlayers().size()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
long timeTillNextAction = getTimeTillNextAction();
|
||||||
|
sleepFor(timeTillNextAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getTimeTillNextAction() {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
long timeUntilNextUpdate = FIRST_MILLIS_BETWEEN_UPDATES - (currentTime - lastUpdate);
|
||||||
|
long timeUntilPacketReset = 1000 - (currentTime - lastPacketsPerSecondUpdate);
|
||||||
|
|
||||||
|
return Math.min(timeUntilNextUpdate, timeUntilPacketReset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleepFor(long millis) {
|
||||||
|
if (millis <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Thread.sleep(millis);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Getter
|
||||||
|
public static final class ScoreboardSession {
|
||||||
|
private final GeyserSession session;
|
||||||
|
private final AtomicInteger pendingPacketsPerSecond = new AtomicInteger(0);
|
||||||
|
private int packetsPerSecond;
|
||||||
|
private long lastUpdate;
|
||||||
|
private long lastLog;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ public final class Team {
|
||||||
@Setter private NameTagVisibility nameTagVisibility;
|
@Setter private NameTagVisibility nameTagVisibility;
|
||||||
@Setter private TeamColor color;
|
@Setter private TeamColor color;
|
||||||
|
|
||||||
private TeamData currentData;
|
private final TeamData currentData;
|
||||||
private TeamData cachedData;
|
private TeamData cachedData;
|
||||||
|
|
||||||
private boolean updating;
|
private boolean updating;
|
||||||
|
@ -60,22 +60,6 @@ public final class Team {
|
||||||
entities = new ObjectOpenHashSet<>();
|
entities = new ObjectOpenHashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAddedEntities(List<String> added) {
|
|
||||||
if (added.size() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// we don't have to change the updateType,
|
|
||||||
// because the scores itself need updating, not the team
|
|
||||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
|
||||||
for (String addedEntity : added) {
|
|
||||||
Score score = objective.getScores().get(addedEntity);
|
|
||||||
if (score != null) {
|
|
||||||
score.setTeam(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Team addEntities(String... names) {
|
public Team addEntities(String... names) {
|
||||||
List<String> added = new ArrayList<>();
|
List<String> added = new ArrayList<>();
|
||||||
for (String name : names) {
|
for (String name : names) {
|
||||||
|
@ -83,18 +67,20 @@ public final class Team {
|
||||||
added.add(name);
|
added.add(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkAddedEntities(added);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Team addEntities(Set<String> names) {
|
if (added.size() == 0) {
|
||||||
List<String> added = new ArrayList<>();
|
return this;
|
||||||
for (String name : names) {
|
}
|
||||||
if (entities.add(name)) {
|
// we don't have to change the updateType,
|
||||||
added.add(name);
|
// because the scores itself need updating, not the team
|
||||||
|
for (Objective objective : scoreboard.getObjectives()) {
|
||||||
|
for (String addedEntity : added) {
|
||||||
|
Score score = objective.getScores().get(addedEntity);
|
||||||
|
if (score != null) {
|
||||||
|
score.setTeam(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
checkAddedEntities(added);
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +155,10 @@ public final class Team {
|
||||||
}
|
}
|
||||||
|
|
||||||
public UpdateType getUpdateType() {
|
public UpdateType getUpdateType() {
|
||||||
|
return currentData.updateType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UpdateType getCachedUpdateType() {
|
||||||
return cachedData != null ? cachedData.updateType : currentData.updateType;
|
return cachedData != null ? cachedData.updateType : currentData.updateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,14 +186,14 @@ public final class Team {
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
public static final class TeamData {
|
public static final class TeamData {
|
||||||
protected UpdateType updateType;
|
private UpdateType updateType;
|
||||||
protected long updateTime;
|
private long updateTime;
|
||||||
|
|
||||||
protected String name;
|
private String name;
|
||||||
protected String prefix;
|
private String prefix;
|
||||||
protected String suffix;
|
private String suffix;
|
||||||
|
|
||||||
protected TeamData() {
|
private TeamData() {
|
||||||
updateType = UpdateType.ADD;
|
updateType = UpdateType.ADD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue