Mirror von
https://github.com/GeyserMC/Geyser.git
synchronisiert 2024-12-28 09:00:11 +01:00
Fixed some Scoreboard bugs and ScoreboardUpdater is now global
Dieser Commit ist enthalten in:
Ursprung
988fd66a85
Commit
36c41b8dc7
@ -56,6 +56,7 @@ import org.geysermc.connector.network.translators.world.block.BlockTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.BlockEntityTranslator;
|
||||
import org.geysermc.connector.network.translators.collision.CollisionTranslator;
|
||||
import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.utils.DimensionUtils;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.utils.LocaleUtils;
|
||||
@ -147,6 +148,7 @@ public class GeyserConnector {
|
||||
RecipeRegistry.init();
|
||||
SoundRegistry.init();
|
||||
SoundHandlerRegistry.init();
|
||||
ScoreboardUpdater.init();
|
||||
|
||||
ResourcePack.loadPacks();
|
||||
|
||||
|
@ -31,37 +31,35 @@ import lombok.Setter;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater.ScoreboardSession;
|
||||
|
||||
@Getter
|
||||
public class WorldCache {
|
||||
private final GeyserSession session;
|
||||
private final ScoreboardSession scoreboardSession;
|
||||
@Setter
|
||||
private Difficulty difficulty = Difficulty.EASY;
|
||||
private boolean showCoordinates = true;
|
||||
|
||||
private Scoreboard scoreboard;
|
||||
private final ScoreboardUpdater scoreboardUpdater;
|
||||
|
||||
public WorldCache(GeyserSession session) {
|
||||
this.session = session;
|
||||
this.scoreboard = new Scoreboard(session);
|
||||
scoreboardUpdater = new ScoreboardUpdater(this);
|
||||
scoreboardUpdater.start();
|
||||
scoreboardSession = new ScoreboardSession(session);
|
||||
}
|
||||
|
||||
public void removeScoreboard() {
|
||||
if (scoreboard != null) {
|
||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
||||
scoreboard.despawnObjective(objective);
|
||||
for (Objective objective : scoreboard.getObjectives()) {
|
||||
scoreboard.deleteObjective(objective);
|
||||
}
|
||||
scoreboard = new Scoreboard(session);
|
||||
}
|
||||
}
|
||||
|
||||
public int increaseAndGetScoreboardPacketsPerSecond() {
|
||||
int pendingPps = scoreboardUpdater.incrementAndGetPacketsPerSecond();
|
||||
int pps = scoreboardUpdater.getPacketsPerSecond();
|
||||
int pendingPps = scoreboardSession.getPendingPacketsPerSecond().incrementAndGet();
|
||||
int pps = scoreboardSession.getPacketsPerSecond();
|
||||
return Math.max(pps, pendingPps);
|
||||
}
|
||||
|
||||
|
@ -25,12 +25,11 @@
|
||||
|
||||
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.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerDisplayScoreboardPacket;
|
||||
|
||||
@Translator(packet = ServerDisplayScoreboardPacket.class)
|
||||
public class JavaDisplayScoreboardTranslator extends PacketTranslator<ServerDisplayScoreboardPacket> {
|
||||
|
||||
|
@ -25,30 +25,38 @@
|
||||
|
||||
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.cache.WorldCache;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
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.Scoreboard;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.network.translators.chat.MessageTranslator;
|
||||
|
||||
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.scoreboard.UpdateType;
|
||||
|
||||
@Translator(packet = ServerScoreboardObjectivePacket.class)
|
||||
public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerScoreboardObjectivePacket> {
|
||||
private final GeyserLogger logger = GeyserConnector.getInstance().getLogger();
|
||||
|
||||
@Override
|
||||
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
|
||||
WorldCache worldCache = session.getWorldCache();
|
||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||
Objective objective = scoreboard.getObjective(packet.getName());
|
||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
||||
if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) {
|
||||
objective = scoreboard.registerNewObjective(packet.getName(), false);
|
||||
Objective objective = scoreboard.getObjective(packet.getName());
|
||||
if (objective != null && objective.getUpdateType() != UpdateType.REMOVE && packet.getAction() == ObjectiveAction.ADD) {
|
||||
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()) {
|
||||
|
@ -66,7 +66,7 @@ public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScor
|
||||
if (objective != null) {
|
||||
objective.removeScore(packet.getEntry());
|
||||
} else {
|
||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
||||
for (Objective objective1 : scoreboard.getObjectives()) {
|
||||
objective1.removeScore(packet.getEntry());
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
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.Setter;
|
||||
|
||||
@ -48,6 +49,7 @@ public final class Objective {
|
||||
private int type = 0; // 0 = integer, 1 = heart
|
||||
|
||||
private Map<String, Score> scores = new ConcurrentHashMap<>();
|
||||
// todo add a 'to add' map so that we don't have to use a concurrentHashMap
|
||||
|
||||
private Objective(Scoreboard scoreboard) {
|
||||
this.id = scoreboard.getNextId().getAndIncrement();
|
||||
@ -69,7 +71,7 @@ public final class Objective {
|
||||
public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) {
|
||||
this(scoreboard);
|
||||
this.objectiveName = objectiveName;
|
||||
this.displaySlot = correctDisplaySlot(displaySlot);
|
||||
this.displaySlot = displaySlot;
|
||||
this.displaySlotName = translateDisplaySlot(displaySlot);
|
||||
this.displayName = displayName;
|
||||
this.type = type;
|
||||
@ -86,17 +88,6 @@ public final class Objective {
|
||||
}
|
||||
}
|
||||
|
||||
private static ScoreboardPosition correctDisplaySlot(ScoreboardPosition displaySlot) {
|
||||
switch (displaySlot) {
|
||||
case BELOW_NAME:
|
||||
return ScoreboardPosition.BELOW_NAME;
|
||||
case PLAYER_LIST:
|
||||
return ScoreboardPosition.PLAYER_LIST;
|
||||
default:
|
||||
return ScoreboardPosition.SIDEBAR;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerScore(String id, int score) {
|
||||
if (!scores.containsKey(id)) {
|
||||
long scoreId = scoreboard.getNextId().getAndIncrement();
|
||||
@ -151,12 +142,74 @@ public final class Objective {
|
||||
public void setActive(ScoreboardPosition displaySlot) {
|
||||
if (!active) {
|
||||
active = true;
|
||||
this.displaySlot = correctDisplaySlot(displaySlot);
|
||||
this.displaySlot = displaySlot;
|
||||
displaySlotName = translateDisplaySlot(displaySlot);
|
||||
}
|
||||
}
|
||||
|
||||
public void deactivate() {
|
||||
active = false;
|
||||
}
|
||||
|
||||
public ScoreboardPosition getPositionCategory() {
|
||||
switch (displaySlot) {
|
||||
case PLAYER_LIST:
|
||||
return ScoreboardPosition.PLAYER_LIST;
|
||||
case BELOW_NAME:
|
||||
return ScoreboardPosition.BELOW_NAME;
|
||||
default:
|
||||
return ScoreboardPosition.SIDEBAR;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasTeamColor() {
|
||||
return displaySlot != ScoreboardPosition.PLAYER_LIST &&
|
||||
displaySlot != ScoreboardPosition.BELOW_NAME &&
|
||||
displaySlot != ScoreboardPosition.SIDEBAR;
|
||||
}
|
||||
|
||||
public TeamColor getTeamColor() {
|
||||
switch (displaySlot) {
|
||||
case SIDEBAR_TEAM_RED:
|
||||
return TeamColor.RED;
|
||||
case SIDEBAR_TEAM_AQUA:
|
||||
return TeamColor.AQUA;
|
||||
case SIDEBAR_TEAM_BLUE:
|
||||
return TeamColor.BLUE;
|
||||
case SIDEBAR_TEAM_GOLD:
|
||||
return TeamColor.GOLD;
|
||||
case SIDEBAR_TEAM_GRAY:
|
||||
return TeamColor.GRAY;
|
||||
case SIDEBAR_TEAM_BLACK:
|
||||
return TeamColor.BLACK;
|
||||
case SIDEBAR_TEAM_GREEN:
|
||||
return TeamColor.GREEN;
|
||||
case SIDEBAR_TEAM_WHITE:
|
||||
return TeamColor.WHITE;
|
||||
case SIDEBAR_TEAM_YELLOW:
|
||||
return TeamColor.YELLOW;
|
||||
case SIDEBAR_TEAM_DARK_RED:
|
||||
return TeamColor.DARK_RED;
|
||||
case SIDEBAR_TEAM_DARK_AQUA:
|
||||
return TeamColor.DARK_AQUA;
|
||||
case SIDEBAR_TEAM_DARK_BLUE:
|
||||
return TeamColor.DARK_BLUE;
|
||||
case SIDEBAR_TEAM_DARK_GRAY:
|
||||
return TeamColor.DARK_GRAY;
|
||||
case SIDEBAR_TEAM_DARK_GREEN:
|
||||
return TeamColor.DARK_GREEN;
|
||||
case SIDEBAR_TEAM_DARK_PURPLE:
|
||||
return TeamColor.DARK_PURPLE;
|
||||
case SIDEBAR_TEAM_LIGHT_PURPLE:
|
||||
return TeamColor.LIGHT_PURPLE;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void removed() {
|
||||
active = false;
|
||||
updateType = UpdateType.REMOVE;
|
||||
scores = null;
|
||||
}
|
||||
}
|
||||
|
@ -42,14 +42,16 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.geysermc.connector.scoreboard.UpdateType.*;
|
||||
|
||||
@Getter
|
||||
public final class Scoreboard {
|
||||
private final GeyserSession session;
|
||||
private final GeyserLogger logger;
|
||||
@Getter
|
||||
private final AtomicLong nextId = new AtomicLong(0);
|
||||
|
||||
private final Map<String, Objective> objectives = new ConcurrentHashMap<>();
|
||||
private final Map<String, Team> teams = new HashMap<>();
|
||||
private final Map<String, Objective> objectives = new HashMap<>();
|
||||
private final Map<ScoreboardPosition, Objective> objectiveSlots = new HashMap<>();
|
||||
private final Map<String, Team> teams = new ConcurrentHashMap<>(); // updated on multiple threads
|
||||
// todo add a 'to add' map so that we don't have to use a concurrentHashMap
|
||||
|
||||
private int lastAddScoreCount = 0;
|
||||
private int lastRemoveScoreCount = 0;
|
||||
@ -59,11 +61,17 @@ public final class Scoreboard {
|
||||
this.logger = GeyserConnector.getInstance().getLogger();
|
||||
}
|
||||
|
||||
public Objective registerNewObjective(String objectiveId, boolean active) {
|
||||
public Objective registerNewObjective(String objectiveId) {
|
||||
Objective objective = objectives.get(objectiveId);
|
||||
if (active || objective != null) {
|
||||
return objective;
|
||||
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);
|
||||
}
|
||||
|
||||
objective = new Objective(this, objectiveId);
|
||||
objectives.put(objectiveId, objective);
|
||||
return objective;
|
||||
@ -71,32 +79,24 @@ public final class Scoreboard {
|
||||
|
||||
public Objective displayObjective(String objectiveId, ScoreboardPosition displaySlot) {
|
||||
Objective objective = objectives.get(objectiveId);
|
||||
if (objective != null) {
|
||||
if (!objective.isActive()) {
|
||||
objective.setActive(displaySlot);
|
||||
removeOldObjectives(objective);
|
||||
return objective;
|
||||
}
|
||||
despawnObjective(objective);
|
||||
if (objective == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
objective = new Objective(this, objectiveId, displaySlot, "unknown", 0);
|
||||
objectives.put(objectiveId, objective);
|
||||
removeOldObjectives(objective);
|
||||
if (!objective.isActive()) {
|
||||
objective.setActive(displaySlot);
|
||||
// for reactivated objectives
|
||||
objective.setUpdateType(ADD);
|
||||
}
|
||||
|
||||
Objective storedObjective = objectiveSlots.get(displaySlot);
|
||||
if (storedObjective != null) {
|
||||
deactivateObjective(storedObjective);
|
||||
}
|
||||
objectiveSlots.put(displaySlot, objective);
|
||||
return objective;
|
||||
}
|
||||
|
||||
private void removeOldObjectives(Objective newObjective) {
|
||||
for (Objective next : objectives.values()) {
|
||||
if (next.getId() == newObjective.getId()) {
|
||||
continue;
|
||||
}
|
||||
if (next.getDisplaySlot() == newObjective.getDisplaySlot()) {
|
||||
next.setUpdateType(REMOVE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Team registerNewTeam(String teamName, Set<String> players) {
|
||||
Team team = teams.get(teamName);
|
||||
if (team != null) {
|
||||
@ -113,6 +113,10 @@ public final class Scoreboard {
|
||||
return objectives.get(objectiveName);
|
||||
}
|
||||
|
||||
public Collection<Objective> getObjectives() {
|
||||
return objectives.values();
|
||||
}
|
||||
|
||||
public Team getTeam(String teamName) {
|
||||
return teams.get(teamName);
|
||||
}
|
||||
@ -121,6 +125,7 @@ public final class Scoreboard {
|
||||
Objective objective = getObjective(objectiveName);
|
||||
if (objective != null) {
|
||||
objective.setUpdateType(REMOVE);
|
||||
objective.deactivate();
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,114 +137,38 @@ public final class Scoreboard {
|
||||
}
|
||||
|
||||
public void onUpdate() {
|
||||
List<ScoreInfo> addScores = new ArrayList<>(getLastAddScoreCount());
|
||||
List<ScoreInfo> removeScores = new ArrayList<>(getLastRemoveScoreCount());
|
||||
List<ScoreInfo> addScores = new ArrayList<>(lastAddScoreCount);
|
||||
List<ScoreInfo> removeScores = new ArrayList<>(lastRemoveScoreCount);
|
||||
List<Objective> removedObjectives = new ArrayList<>();
|
||||
|
||||
Team playerTeam = getTeamFor(session.getPlayerEntity().getUsername());
|
||||
Objective correctSidebar = null;
|
||||
|
||||
for (Objective objective : objectives.values()) {
|
||||
if (!objective.isActive()) {
|
||||
logger.debug("Ignoring non-active Scoreboard Objective '" + objective.getObjectiveName() + '\'');
|
||||
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) {
|
||||
// objective has been deleted
|
||||
if (objective.getUpdateType() == REMOVE) {
|
||||
removedObjectives.add(objective);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (objectiveUpdate) {
|
||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||
session.sendUpstreamPacket(removeObjectivePacket);
|
||||
if (playerTeam != null && playerTeam.getColor() == objective.getTeamColor()) {
|
||||
correctSidebar = objective;
|
||||
}
|
||||
|
||||
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, removedObjectives);
|
||||
handleObjective(correctSidebar, addScores, removeScores, removedObjectives);
|
||||
handleObjective(objectiveSlots.get(ScoreboardPosition.BELOW_NAME), addScores, removeScores, removedObjectives);
|
||||
|
||||
Iterator<Team> teamIterator = teams.values().iterator();
|
||||
while (teamIterator.hasNext()) {
|
||||
Team current = teamIterator.next();
|
||||
|
||||
switch (current.getUpdateType()) {
|
||||
switch (current.getCachedUpdateType()) {
|
||||
case ADD:
|
||||
case UPDATE:
|
||||
current.markUpdated();
|
||||
@ -265,15 +194,156 @@ public final class Scoreboard {
|
||||
|
||||
// prevents crashes in some cases
|
||||
for (Objective objective : removedObjectives) {
|
||||
despawnObjective(objective);
|
||||
deleteObjective(objective);
|
||||
}
|
||||
|
||||
lastAddScoreCount = addScores.size();
|
||||
lastRemoveScoreCount = removeScores.size();
|
||||
}
|
||||
|
||||
public void despawnObjective(Objective objective) {
|
||||
private void handleObjective(Objective objective, List<ScoreInfo> addScores, List<ScoreInfo> removeScores, List<Objective> removedObjectives) {
|
||||
if (objective == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// objective has been removed when scores is null
|
||||
if (objective.getScores() == null) {
|
||||
objectiveSlots.remove(objective.getDisplaySlot());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!objective.isActive()) {
|
||||
deactivateObjective(objective);
|
||||
objectiveSlots.remove(objective.getDisplaySlot());
|
||||
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 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);
|
||||
}
|
||||
|
||||
if (objectiveUpdate) {
|
||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||
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);
|
||||
}
|
||||
|
||||
private void deactivateObjective(Objective objective) {
|
||||
// Scoreboard has been removed already
|
||||
if (objective.getScores() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ScoreInfo> removedScores = new ArrayList<>(objective.getScores().size());
|
||||
for (Score score : objective.getScores().values()) {
|
||||
removedScores.add(score.getCachedInfo());
|
||||
}
|
||||
|
||||
objective.deactivate();
|
||||
|
||||
SetScorePacket scorePacket = new SetScorePacket();
|
||||
scorePacket.setAction(SetScorePacket.Action.REMOVE);
|
||||
scorePacket.setInfos(removedScores);
|
||||
session.sendUpstreamPacket(scorePacket);
|
||||
|
||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||
session.sendUpstreamPacket(removeObjectivePacket);
|
||||
}
|
||||
|
||||
public void deleteObjective(Objective objective) {
|
||||
objectives.remove(objective.getObjectiveName());
|
||||
|
||||
Objective storedSlot = objectiveSlots.get(objective.getDisplaySlot());
|
||||
if (storedSlot != null && storedSlot.getId() == objective.getId()) {
|
||||
deactivateObjective(objective);
|
||||
objective.removed();
|
||||
return;
|
||||
}
|
||||
|
||||
objective.removed();
|
||||
|
||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
@ -34,91 +36,107 @@ import org.geysermc.connector.utils.LanguageUtils;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ScoreboardUpdater extends Thread {
|
||||
public class ScoreboardUpdater implements Runnable {
|
||||
public static final int FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD;
|
||||
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 SECOND_MILLIS_BETWEEN_UPDATES = 1000 * 3; // 1 update per 3 seconds
|
||||
private static final int SECOND_MILLIS_BETWEEN_UPDATES = 1000 * 3; // 1 update per seconds
|
||||
|
||||
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();
|
||||
}
|
||||
private static GeyserConnector connector;
|
||||
|
||||
static {
|
||||
GeyserConfiguration config = GeyserConnector.getInstance().getConfig();
|
||||
FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD = Math.min(config.getScoreboardPacketThreshold(), SECOND_SCORE_PACKETS_PER_SECOND_THRESHOLD);
|
||||
DEBUG_ENABLED = config.isDebugMode();
|
||||
}
|
||||
|
||||
private long lastUpdate = System.currentTimeMillis();
|
||||
private long lastPacketsPerSecondUpdate = System.currentTimeMillis();
|
||||
|
||||
public static void init() {
|
||||
connector = GeyserConnector.getInstance();
|
||||
ScoreboardUpdater updater = new ScoreboardUpdater();
|
||||
updater.run();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
long timeTillAction = getTimeTillNextAction();
|
||||
if (timeTillAction > 0) {
|
||||
connector.getGeneralThreadPool().schedule(this, timeTillAction, TimeUnit.MILLISECONDS);
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
scoreboardSession.packetsPerSecond = scoreboardSession.getPendingPacketsPerSecond().get();
|
||||
scoreboardSession.pendingPacketsPerSecond.set(0);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (reachedSecondThreshold) {
|
||||
scoreboardSession.millisBetweenUpdates = SECOND_MILLIS_BETWEEN_UPDATES;
|
||||
} else {
|
||||
scoreboardSession.millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES;
|
||||
}
|
||||
|
||||
if (currentTime - scoreboardSession.lastUpdate > scoreboardSession.millisBetweenUpdates) {
|
||||
worldCache.getScoreboard().onUpdate();
|
||||
|
||||
if (DEBUG_ENABLED && (currentTime - scoreboardSession.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", (scoreboardSession.millisBetweenUpdates / 1000.0))
|
||||
);
|
||||
|
||||
scoreboardSession.lastLog = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
connector.getGeneralThreadPool().schedule(this, getTimeTillNextAction(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@RequiredArgsConstructor
|
||||
@Getter
|
||||
public static class ScoreboardSession {
|
||||
private final GeyserSession session;
|
||||
private final AtomicInteger pendingPacketsPerSecond = new AtomicInteger(0);
|
||||
private int packetsPerSecond;
|
||||
private int millisBetweenUpdates = FIRST_MILLIS_BETWEEN_UPDATES;
|
||||
private long lastUpdate;
|
||||
private long lastLog;
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ public final class Team {
|
||||
}
|
||||
// we don't have to change the updateType,
|
||||
// because the scores itself need updating, not the team
|
||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
||||
for (Objective objective : scoreboard.getObjectives()) {
|
||||
for (String addedEntity : added) {
|
||||
Score score = objective.getScores().get(addedEntity);
|
||||
if (score != null) {
|
||||
@ -169,6 +169,10 @@ public final class Team {
|
||||
}
|
||||
|
||||
public UpdateType getUpdateType() {
|
||||
return currentData.updateType;
|
||||
}
|
||||
|
||||
public UpdateType getCachedUpdateType() {
|
||||
return cachedData != null ? cachedData.updateType : currentData.updateType;
|
||||
}
|
||||
|
||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren