forked from GeyserMC/Geyser
Scoreboard improvements (#1166)
* Added a way to check if debug logging is enabled * Improved scoreboard performance * Include Teams in pps and return pending pps instead when higher then pps Some servers have a huge amount of score packets when the player logs in, but before this commit, only after the first high pps (packets per second) the ScoreboardUpdater will be used (after pending packets per second have been moved to packets per second). But this commit fixes that the ScoreboardUpdater can be used on the second that the pps is getting high. * Fixed team pre + suffix "null" issue and added threshold config option Fixed team pre + suffix "null" issue. When the prefix and/or suffix of a Team is null, "null" will be returned instead of null (Due to the way that MCProtocolLib is made and designed). This is fixed by simply checking if the prefix and/or suffix equal "null" and if that is the case, replace it with "". Added threshold option. Gave the person who is running Geyser an option to specify the first Scoreboard packets per second threshold to further improve performance by lowering the setting or decrease performance by relaxing the setting a bit. The value can't be higher then 250 (the second threshold), because it'll always choose the lowest threshold. * Forgot to bump config version * Small changes * Reverted version bump, changed Sponge config, changed FloodgateKeyLoader Reverted version bump Camotoy said that you only need to bump the config version if the change is breaking, the config version bump has been reverted. Changed Sponge config The Sponge config has been modified to look like the other platform configurations. Changed FloodgateKeyLoader * Changed default-locale and (remote) address as requested by Camotoy * Reduce bandwidth and a few final tweaks * Made the scoreboard-packet-threshold a bit higher due to improvements
This commit is contained in:
parent
7cbfdcf521
commit
1c84993853
29 changed files with 549 additions and 616 deletions
|
@ -29,27 +29,22 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
|||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import lombok.Getter;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import org.geysermc.connector.FloodgateKeyLoader;
|
||||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserBungeeConfiguration extends GeyserJacksonConfiguration {
|
||||
|
||||
public final class GeyserBungeeConfiguration extends GeyserJacksonConfiguration {
|
||||
@JsonIgnore
|
||||
private Path floodgateKey;
|
||||
private Path floodgateKeyPath;
|
||||
|
||||
public void loadFloodgate(GeyserBungeePlugin plugin, Configuration configuration) {
|
||||
public void loadFloodgate(GeyserBungeePlugin plugin) {
|
||||
Plugin floodgate = plugin.getProxy().getPluginManager().getPlugin("floodgate-bungee");
|
||||
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), configuration.getString("floodgate-key-file"), "public-key.pem"), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
|
||||
}
|
||||
Path geyserDataFolder = plugin.getDataFolder().toPath();
|
||||
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return floodgateKey;
|
||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,19 +25,21 @@
|
|||
|
||||
package org.geysermc.platform.bungeecord;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class GeyserBungeeLogger implements GeyserLogger {
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
private Logger logger;
|
||||
private boolean debugMode;
|
||||
|
||||
public GeyserBungeeLogger(Logger logger, boolean debugMode) {
|
||||
public GeyserBungeeLogger(Logger logger, boolean debug) {
|
||||
this.logger = logger;
|
||||
this.debugMode = debugMode;
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -72,12 +74,8 @@ public class GeyserBungeeLogger implements GeyserLogger {
|
|||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (debugMode)
|
||||
if (debug) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
debugMode = debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,9 +27,6 @@ package org.geysermc.platform.bungeecord;
|
|||
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.config.Configuration;
|
||||
import net.md_5.bungee.config.ConfigurationProvider;
|
||||
import net.md_5.bungee.config.YamlConfiguration;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.command.CommandManager;
|
||||
|
@ -64,13 +61,11 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
if (!getDataFolder().exists())
|
||||
getDataFolder().mkdir();
|
||||
|
||||
Configuration configuration = null;
|
||||
try {
|
||||
if (!getDataFolder().exists())
|
||||
getDataFolder().mkdir();
|
||||
File configFile = FileUtils.fileOrCopiedFromResource(new File(getDataFolder(), "config.yml"), "config.yml", (x) -> x.replaceAll("generateduuid", UUID.randomUUID().toString()));
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserBungeeConfiguration.class);
|
||||
configuration = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml"));
|
||||
} catch (IOException ex) {
|
||||
getLogger().log(Level.WARNING, LanguageUtils.getLocaleStringLog("geyser.config.failed"), ex);
|
||||
ex.printStackTrace();
|
||||
|
@ -108,7 +103,7 @@ public class GeyserBungeePlugin extends Plugin implements GeyserBootstrap {
|
|||
geyserConfig.getRemote().setAuthType("floodgate");
|
||||
}
|
||||
|
||||
geyserConfig.loadFloodgate(this, configuration);
|
||||
geyserConfig.loadFloodgate(this);
|
||||
|
||||
this.connector = GeyserConnector.start(PlatformType.BUNGEECORD, this);
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.platform.spigot;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
@ -35,26 +34,19 @@ import org.geysermc.connector.FloodgateKeyLoader;
|
|||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserSpigotConfiguration extends GeyserJacksonConfiguration {
|
||||
|
||||
@JsonProperty("floodgate-key-file")
|
||||
private String floodgateKeyFile;
|
||||
|
||||
public final class GeyserSpigotConfiguration extends GeyserJacksonConfiguration {
|
||||
@JsonIgnore
|
||||
private Path floodgateKey;
|
||||
private Path floodgateKeyPath;
|
||||
|
||||
public void loadFloodgate(GeyserSpigotPlugin plugin) {
|
||||
Plugin floodgate = Bukkit.getPluginManager().getPlugin("floodgate-bukkit");
|
||||
floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(plugin.getDataFolder().toString(), plugin.getConfig().getString("floodgate-key-file", "public-key.pem")), floodgate, floodgate != null ? floodgate.getDataFolder().toPath() : null);
|
||||
}
|
||||
Path geyserDataFolder = plugin.getDataFolder().toPath();
|
||||
Path floodgateDataFolder = floodgate != null ? floodgate.getDataFolder().toPath() : null;
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return floodgateKey;
|
||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, floodgateDataFolder, geyserDataFolder, plugin.getGeyserLogger());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -26,7 +26,8 @@
|
|||
package org.geysermc.platform.spigot;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
@ -34,9 +35,9 @@ import java.util.logging.Logger;
|
|||
|
||||
@AllArgsConstructor
|
||||
public class GeyserSpigotLogger implements GeyserLogger {
|
||||
|
||||
private Logger logger;
|
||||
private boolean debugMode;
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
|
@ -70,12 +71,8 @@ public class GeyserSpigotLogger implements GeyserLogger {
|
|||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (debugMode)
|
||||
if (debug) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debug) {
|
||||
debugMode = debug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,268 +25,13 @@
|
|||
|
||||
package org.geysermc.platform.sponge;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
|
||||
public class GeyserSpongeConfiguration implements GeyserConfiguration {
|
||||
|
||||
private File dataFolder;
|
||||
private ConfigurationNode node;
|
||||
|
||||
/**
|
||||
* If the config was originally 'auto' before the values changed
|
||||
*/
|
||||
private boolean autoconfiguredRemote = false;
|
||||
|
||||
private SpongeBedrockConfiguration bedrockConfig;
|
||||
private SpongeRemoteConfiguration remoteConfig;
|
||||
private SpongeMetricsInfo metricsInfo;
|
||||
|
||||
private Map<String, SpongeUserAuthenticationInfo> userAuthInfo = new HashMap<>();
|
||||
|
||||
public GeyserSpongeConfiguration(File dataFolder, ConfigurationNode node) {
|
||||
this.dataFolder = dataFolder;
|
||||
this.node = node;
|
||||
|
||||
this.bedrockConfig = new SpongeBedrockConfiguration(node.getNode("bedrock"));
|
||||
this.remoteConfig = new SpongeRemoteConfiguration(node.getNode("remote"));
|
||||
this.metricsInfo = new SpongeMetricsInfo();
|
||||
|
||||
if (node.getNode("userAuths").getValue() == null)
|
||||
return;
|
||||
|
||||
List<String> userAuths = new ArrayList<String>(((LinkedHashMap)node.getNode("userAuths").getValue()).keySet());
|
||||
for (String key : userAuths) {
|
||||
userAuthInfo.put(key, new SpongeUserAuthenticationInfo(key));
|
||||
}
|
||||
}
|
||||
|
||||
public void setAutoconfiguredRemote(boolean autoconfiguredRemote) {
|
||||
this.autoconfiguredRemote = autoconfiguredRemote;
|
||||
}
|
||||
|
||||
public final class GeyserSpongeConfiguration extends GeyserJacksonConfiguration {
|
||||
@Override
|
||||
public SpongeBedrockConfiguration getBedrock() {
|
||||
return bedrockConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeRemoteConfiguration getRemote() {
|
||||
return remoteConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, SpongeUserAuthenticationInfo> getUserAuths() {
|
||||
return userAuthInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCommandSuggestions() {
|
||||
return node.getNode("command-suggestions").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPassthroughMotd() {
|
||||
return node.getNode("passthrough-motd").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPassthroughProtocolName() {
|
||||
return node.getNode("passthrough-protocol-name").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPassthroughPlayerCounts() {
|
||||
return node.getNode("passthrough-player-counts").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacyPingPassthrough() {
|
||||
return node.getNode("legacy-ping-passthrough").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPingPassthroughInterval() {
|
||||
return node.getNode("ping-passthrough-interval").getInt(3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPlayers() {
|
||||
return node.getNode("max-players").getInt(100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugMode() {
|
||||
return node.getNode("debug-mode").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGeneralThreadPool() {
|
||||
return node.getNode("genereal-thread-pool").getInt(32);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowThirdPartyCapes() {
|
||||
return node.getNode("allow-third-party-capes").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowThirdPartyEars() {
|
||||
return node.getNode("allow-third-party-ears").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowCooldown() {
|
||||
return node.getNode("show-cooldown").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDefaultLocale() {
|
||||
return node.getNode("default-locale").getString("en_us");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return Paths.get(dataFolder.toString(), node.getNode("floodgate-key-file").getString("public-key.pem"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCacheChunks() {
|
||||
return node.getNode("cache-chunks").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCacheImages() {
|
||||
return node.getNode("cache-skins").getInt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAboveBedrockNetherBuilding() {
|
||||
return node.getNode("above-bedrock-nether-building").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpongeMetricsInfo getMetrics() {
|
||||
return metricsInfo;
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SpongeBedrockConfiguration implements IBedrockConfiguration {
|
||||
|
||||
private ConfigurationNode node;
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return node.getNode("address").getString("0.0.0.0");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return node.getNode("port").getInt(19132);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCloneRemotePort() {
|
||||
return node.getNode("clone-remote-port").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMotd1() {
|
||||
return node.getNode("motd1").getString("GeyserMC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMotd2() {
|
||||
return node.getNode("motd2").getString("GeyserMC");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServerName() {
|
||||
return node.getNode("server-name").getString("Geyser");
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
public class SpongeRemoteConfiguration implements IRemoteConfiguration {
|
||||
|
||||
private ConfigurationNode node;
|
||||
|
||||
@Override
|
||||
public String getAddress() {
|
||||
return node.getNode("address").getString("127.0.0.1");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAddress(String address) {
|
||||
node.getNode("address").setValue(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
return node.getNode("port").getInt(25565);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPort(int port) {
|
||||
node.getNode("port").setValue(port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAuthType() {
|
||||
return node.getNode("auth-type").getString("online");
|
||||
}
|
||||
}
|
||||
|
||||
public class SpongeUserAuthenticationInfo implements IUserAuthenticationInfo {
|
||||
|
||||
private String key;
|
||||
|
||||
public SpongeUserAuthenticationInfo(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEmail() {
|
||||
return node.getNode("userAuths").getNode(key).getNode("email").getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPassword() {
|
||||
return node.getNode("userAuths").getNode(key).getNode("password").getString();
|
||||
}
|
||||
}
|
||||
|
||||
public class SpongeMetricsInfo implements IMetricsInfo {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return node.getNode("metrics").getNode("enabled").getBoolean(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUniqueId() {
|
||||
return node.getNode("metrics").getNode("uuid").getString("generateduuid");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnableProxyConnections() {
|
||||
return node.getNode("enable-proxy-connections").getBoolean(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMtu() {
|
||||
return node.getNode("mtu").getInt(1400);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getConfigVersion() {
|
||||
return node.getNode("config-version").getInt(0);
|
||||
public Path getFloodgateKeyPath() {
|
||||
return null; //floodgate isn't available for Sponge
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,15 +26,16 @@
|
|||
package org.geysermc.platform.sponge;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GeyserSpongeLogger implements GeyserLogger {
|
||||
|
||||
private Logger logger;
|
||||
private boolean debugMode;
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
|
@ -68,12 +69,8 @@ public class GeyserSpongeLogger implements GeyserLogger {
|
|||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (debugMode)
|
||||
if (debug) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debugMode) {
|
||||
this.debugMode = debugMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,6 @@
|
|||
package org.geysermc.platform.sponge;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import ninja.leaping.configurate.ConfigurationNode;
|
||||
import ninja.leaping.configurate.loader.ConfigurationLoader;
|
||||
import ninja.leaping.configurate.yaml.YAMLConfigurationLoader;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.bootstrap.GeyserBootstrap;
|
||||
import org.geysermc.connector.command.CommandManager;
|
||||
|
@ -85,20 +82,14 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
|
|||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
ConfigurationLoader loader = YAMLConfigurationLoader.builder().setPath(configFile.toPath()).build();
|
||||
ConfigurationNode config;
|
||||
try {
|
||||
config = loader.load();
|
||||
this.geyserConfig = new GeyserSpongeConfiguration(configDir, config);
|
||||
this.geyserConfig = FileUtils.loadConfig(configFile, GeyserSpongeConfiguration.class);
|
||||
} catch (IOException ex) {
|
||||
logger.warn(LanguageUtils.getLocaleStringLog("geyser.config.failed"));
|
||||
ex.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
ConfigurationNode serverIP = config.getNode("remote").getNode("address");
|
||||
ConfigurationNode serverPort = config.getNode("remote").getNode("port");
|
||||
|
||||
if (Sponge.getServer().getBoundAddress().isPresent()) {
|
||||
InetSocketAddress javaAddr = Sponge.getServer().getBoundAddress().get();
|
||||
|
||||
|
@ -106,13 +97,12 @@ public class GeyserSpongePlugin implements GeyserBootstrap {
|
|||
// By default this should be 127.0.0.1 but may need to be changed in some circumstances
|
||||
if (this.geyserConfig.getRemote().getAddress().equalsIgnoreCase("auto")) {
|
||||
this.geyserConfig.setAutoconfiguredRemote(true);
|
||||
serverPort.setValue(javaAddr.getPort());
|
||||
geyserConfig.getRemote().setPort(javaAddr.getPort());
|
||||
}
|
||||
}
|
||||
|
||||
ConfigurationNode bedrockPort = config.getNode("bedrock").getNode("port");
|
||||
if (geyserConfig.getBedrock().isCloneRemotePort()){
|
||||
bedrockPort.setValue(serverPort.getValue());
|
||||
geyserConfig.getBedrock().setPort(geyserConfig.getRemote().getPort());
|
||||
}
|
||||
|
||||
this.geyserLogger = new GeyserSpongeLogger(logger, geyserConfig.isDebugMode());
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
package org.geysermc.platform.standalone;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import lombok.Getter;
|
||||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
|
||||
|
@ -35,13 +34,9 @@ import java.nio.file.Paths;
|
|||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration {
|
||||
|
||||
@JsonProperty("floodgate-key-file")
|
||||
private String floodgateKeyFile;
|
||||
|
||||
public final class GeyserStandaloneConfiguration extends GeyserJacksonConfiguration {
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return Paths.get(floodgateKeyFile);
|
||||
public Path getFloodgateKeyPath() {
|
||||
return Paths.get(getFloodgateKeyFile());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,18 +26,16 @@
|
|||
package org.geysermc.platform.standalone;
|
||||
|
||||
import lombok.extern.log4j.Log4j2;
|
||||
|
||||
import net.minecrell.terminalconsole.SimpleTerminalConsole;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.command.CommandSender;
|
||||
import org.geysermc.connector.common.ChatColor;
|
||||
|
||||
@Log4j2
|
||||
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org.geysermc.connector.GeyserLogger, CommandSender {
|
||||
|
||||
public class GeyserStandaloneLogger extends SimpleTerminalConsole implements GeyserLogger, CommandSender {
|
||||
private boolean colored = true;
|
||||
|
||||
@Override
|
||||
|
@ -99,10 +97,6 @@ public class GeyserStandaloneLogger extends SimpleTerminalConsole implements org
|
|||
Configurator.setLevel(log.getName(), debug ? Level.DEBUG : Level.INFO);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for setting debug mode in GUI mode
|
||||
* @return if debug is enabled
|
||||
*/
|
||||
public boolean isDebug() {
|
||||
return log.isDebugEnabled();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.platform.velocity;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.velocitypowered.api.plugin.PluginContainer;
|
||||
import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import lombok.Getter;
|
||||
|
@ -37,25 +36,15 @@ import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
|||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Optional;
|
||||
|
||||
@Getter
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class GeyserVelocityConfiguration extends GeyserJacksonConfiguration {
|
||||
|
||||
@JsonProperty("floodgate-key-file")
|
||||
private String floodgateKeyFile;
|
||||
|
||||
public final class GeyserVelocityConfiguration extends GeyserJacksonConfiguration {
|
||||
@JsonIgnore
|
||||
private Path floodgateKey;
|
||||
|
||||
@Override
|
||||
public Path getFloodgateKeyFile() {
|
||||
return floodgateKey;
|
||||
}
|
||||
private Path floodgateKeyPath;
|
||||
|
||||
public void loadFloodgate(GeyserVelocityPlugin plugin, ProxyServer proxyServer, File dataFolder) {
|
||||
Optional<PluginContainer> floodgate = proxyServer.getPluginManager().getPlugin("floodgate");
|
||||
floodgate.ifPresent(it -> floodgateKey = FloodgateKeyLoader.getKey(plugin.getGeyserLogger(), this, Paths.get(dataFolder.toString(), floodgateKeyFile.isEmpty() ? floodgateKeyFile : "public-key.pem"), it, Paths.get("plugins/floodgate/")));
|
||||
PluginContainer floodgate = proxyServer.getPluginManager().getPlugin("floodgate").orElse(null);
|
||||
floodgateKeyPath = FloodgateKeyLoader.getKeyPath(this, floodgate, Paths.get("plugins/floodgate/"), dataFolder.toPath(), plugin.getGeyserLogger());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,15 +26,16 @@
|
|||
package org.geysermc.platform.velocity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class GeyserVelocityLogger implements GeyserLogger {
|
||||
|
||||
private Logger logger;
|
||||
private boolean debugMode;
|
||||
private final Logger logger;
|
||||
@Getter @Setter
|
||||
private boolean debug;
|
||||
|
||||
@Override
|
||||
public void severe(String message) {
|
||||
|
@ -68,12 +69,8 @@ public class GeyserVelocityLogger implements GeyserLogger {
|
|||
|
||||
@Override
|
||||
public void debug(String message) {
|
||||
if (debugMode)
|
||||
if (debug) {
|
||||
info(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDebug(boolean debugMode) {
|
||||
this.debugMode = debugMode;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,17 +25,19 @@
|
|||
|
||||
package org.geysermc.connector;
|
||||
|
||||
import org.geysermc.connector.configuration.GeyserJacksonConfiguration;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FloodgateKeyLoader {
|
||||
public static Path getKey(GeyserLogger logger, GeyserConfiguration config, Path floodgateKey, Object floodgate, Path floodgateFolder) {
|
||||
public static Path getKeyPath(GeyserJacksonConfiguration config, Object floodgate, Path floodgateDataFolder, Path geyserDataFolder, GeyserLogger logger) {
|
||||
Path floodgateKey = geyserDataFolder.resolve(config.getFloodgateKeyFile());
|
||||
|
||||
if (!Files.exists(floodgateKey) && config.getRemote().getAuthType().equals("floodgate")) {
|
||||
if (floodgate != null) {
|
||||
Path autoKey = floodgateFolder.resolve("public-key.pem");
|
||||
Path autoKey = floodgateDataFolder.resolve("public-key.pem");
|
||||
if (Files.exists(autoKey)) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.bootstrap.floodgate.auto_loaded"));
|
||||
floodgateKey = autoKey;
|
||||
|
|
|
@ -84,4 +84,9 @@ public interface GeyserLogger {
|
|||
* @param debug if the logger should print debug messages
|
||||
*/
|
||||
void setDebug(boolean debug);
|
||||
|
||||
/**
|
||||
* If debug is enabled for this logger
|
||||
*/
|
||||
boolean isDebug();
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ package org.geysermc.connector.configuration;
|
|||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
@ -74,7 +73,7 @@ public interface GeyserConfiguration {
|
|||
|
||||
String getDefaultLocale();
|
||||
|
||||
Path getFloodgateKeyFile();
|
||||
Path getFloodgateKeyPath();
|
||||
|
||||
boolean isAboveBedrockNetherBuilding();
|
||||
|
||||
|
@ -125,6 +124,8 @@ public interface GeyserConfiguration {
|
|||
String getUniqueId();
|
||||
}
|
||||
|
||||
int getScoreboardPacketThreshold();
|
||||
|
||||
// if u have offline mode enabled pls be safe
|
||||
boolean isEnableProxyConnections();
|
||||
|
||||
|
|
|
@ -49,76 +49,75 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
private RemoteConfiguration remote;
|
||||
|
||||
@JsonProperty("floodgate-key-file")
|
||||
private String floodgateKeyFile;
|
||||
private String floodgateKeyFile = "public-key.pem";
|
||||
|
||||
public abstract Path getFloodgateKeyFile();
|
||||
public abstract Path getFloodgateKeyPath();
|
||||
|
||||
private Map<String, UserAuthenticationInfo> userAuths;
|
||||
|
||||
@JsonProperty("command-suggestions")
|
||||
private boolean commandSuggestions;
|
||||
private boolean commandSuggestions = true;
|
||||
|
||||
@JsonProperty("passthrough-motd")
|
||||
private boolean isPassthroughMotd;
|
||||
private boolean isPassthroughMotd = false;
|
||||
|
||||
@JsonProperty("passthrough-player-counts")
|
||||
private boolean isPassthroughPlayerCounts;
|
||||
private boolean isPassthroughPlayerCounts = false;
|
||||
|
||||
@JsonProperty("passthrough-protocol-name")
|
||||
private boolean isPassthroughProtocolName;
|
||||
private boolean isPassthroughProtocolName = false;
|
||||
|
||||
@JsonProperty("legacy-ping-passthrough")
|
||||
private boolean isLegacyPingPassthrough;
|
||||
private boolean isLegacyPingPassthrough = false;
|
||||
|
||||
@JsonProperty("ping-passthrough-interval")
|
||||
private int pingPassthroughInterval;
|
||||
private int pingPassthroughInterval = 3;
|
||||
|
||||
@JsonProperty("max-players")
|
||||
private int maxPlayers;
|
||||
private int maxPlayers = 100;
|
||||
|
||||
@JsonProperty("debug-mode")
|
||||
private boolean debugMode;
|
||||
private boolean debugMode = false;
|
||||
|
||||
@JsonProperty("general-thread-pool")
|
||||
private int generalThreadPool;
|
||||
private int generalThreadPool = 32;
|
||||
|
||||
@JsonProperty("allow-third-party-capes")
|
||||
private boolean allowThirdPartyCapes;
|
||||
private boolean allowThirdPartyCapes = true;
|
||||
|
||||
@JsonProperty("show-cooldown")
|
||||
private boolean showCooldown = true;
|
||||
|
||||
@JsonProperty("allow-third-party-ears")
|
||||
private boolean allowThirdPartyEars;
|
||||
private boolean allowThirdPartyEars = false;
|
||||
|
||||
@JsonProperty("default-locale")
|
||||
private String defaultLocale;
|
||||
private String defaultLocale = null; // is null by default so system language takes priority
|
||||
|
||||
@JsonProperty("cache-chunks")
|
||||
private boolean cacheChunks;
|
||||
private boolean cacheChunks = false;
|
||||
|
||||
@JsonProperty("cache-images")
|
||||
private int cacheImages = 0;
|
||||
|
||||
@JsonProperty("above-bedrock-nether-building")
|
||||
private boolean aboveBedrockNetherBuilding;
|
||||
private boolean aboveBedrockNetherBuilding = false;
|
||||
|
||||
private MetricsInfo metrics;
|
||||
|
||||
@Getter
|
||||
public static class BedrockConfiguration implements IBedrockConfiguration {
|
||||
|
||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||
private String address;
|
||||
private String address = "0.0.0.0";
|
||||
|
||||
@Setter
|
||||
private int port;
|
||||
private int port = 19132;
|
||||
|
||||
@JsonProperty("clone-remote-port")
|
||||
private boolean cloneRemotePort;
|
||||
private boolean cloneRemotePort = false;
|
||||
|
||||
private String motd1;
|
||||
private String motd2;
|
||||
private String motd1 = "GeyserMC";
|
||||
private String motd2 = "Geyser";
|
||||
|
||||
@JsonProperty("server-name")
|
||||
private String serverName = GeyserConnector.NAME;
|
||||
|
@ -126,17 +125,16 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
|
||||
@Getter
|
||||
public static class RemoteConfiguration implements IRemoteConfiguration {
|
||||
|
||||
@Setter
|
||||
@AsteriskSerializer.Asterisk(sensitive = true)
|
||||
private String address;
|
||||
private String address = "auto";
|
||||
|
||||
@Setter
|
||||
private int port;
|
||||
private int port = 25565;
|
||||
|
||||
@Setter
|
||||
@JsonProperty("auth-type")
|
||||
private String authType;
|
||||
private String authType = "online";
|
||||
}
|
||||
|
||||
@Getter
|
||||
|
@ -150,13 +148,15 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
|
||||
@Getter
|
||||
public static class MetricsInfo implements IMetricsInfo {
|
||||
|
||||
private boolean enabled;
|
||||
private boolean enabled = true;
|
||||
|
||||
@JsonProperty("uuid")
|
||||
private String uniqueId;
|
||||
private String uniqueId = "generateuuid";
|
||||
}
|
||||
|
||||
@JsonProperty("scoreboard-packet-threshold")
|
||||
private int scoreboardPacketThreshold = 10;
|
||||
|
||||
@JsonProperty("enable-proxy-connections")
|
||||
private boolean enableProxyConnections = false;
|
||||
|
||||
|
@ -164,5 +164,5 @@ public abstract class GeyserJacksonConfiguration implements GeyserConfiguration
|
|||
private int mtu = 1400;
|
||||
|
||||
@JsonProperty("config-version")
|
||||
private int configVersion;
|
||||
private int configVersion = 0;
|
||||
}
|
||||
|
|
|
@ -30,13 +30,15 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadat
|
|||
import com.github.steveice10.mc.protocol.data.message.TextMessage;
|
||||
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
|
||||
import com.nukkitx.math.vector.Vector3f;
|
||||
import com.nukkitx.protocol.bedrock.data.AdventureSetting;
|
||||
import com.nukkitx.protocol.bedrock.data.AttributeData;
|
||||
import com.nukkitx.protocol.bedrock.data.PlayerPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.command.CommandPermission;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityData;
|
||||
import com.nukkitx.protocol.bedrock.data.entity.EntityLinkData;
|
||||
import com.nukkitx.protocol.bedrock.packet.*;
|
||||
import com.nukkitx.protocol.bedrock.packet.AddPlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.MovePlayerPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetEntityLinkPacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.geysermc.connector.entity.attribute.Attribute;
|
||||
|
@ -48,7 +50,10 @@ import org.geysermc.connector.scoreboard.Team;
|
|||
import org.geysermc.connector.utils.AttributeUtils;
|
||||
import org.geysermc.connector.utils.MessageUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Getter @Setter
|
||||
|
@ -209,11 +214,6 @@ public class PlayerEntity extends LivingEntity {
|
|||
super.updateBedrockMetadata(entityMetadata, session);
|
||||
|
||||
if (entityMetadata.getId() == 2) {
|
||||
// System.out.println(session.getScoreboardCache().getScoreboard().getObjectives().keySet());
|
||||
for (Team team : session.getWorldCache().getScoreboard().getTeams().values()) {
|
||||
// session.getConnector().getLogger().info("team name " + team.getName());
|
||||
// session.getConnector().getLogger().info("team entities " + team.getEntities());
|
||||
}
|
||||
String username = this.username;
|
||||
TextMessage name = (TextMessage) entityMetadata.getValue();
|
||||
if (name != null) {
|
||||
|
@ -221,7 +221,6 @@ public class PlayerEntity extends LivingEntity {
|
|||
}
|
||||
Team team = session.getWorldCache().getScoreboard().getTeamFor(username);
|
||||
if (team != null) {
|
||||
// session.getConnector().getLogger().info("team name es " + team.getName() + " with prefix " + team.getPrefix() + " and suffix " + team.getSuffix());
|
||||
metadata.put(EntityData.NAMETAG, team.getPrefix() + MessageUtils.toChatColor(team.getColor()) + username + team.getSuffix());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -346,7 +346,7 @@ public class GeyserSession implements CommandSender {
|
|||
PublicKey key = null;
|
||||
try {
|
||||
key = EncryptionUtil.getKeyFromFile(
|
||||
connector.getConfig().getFloodgateKeyFile(),
|
||||
connector.getConfig().getFloodgateKeyPath(),
|
||||
PublicKey.class
|
||||
);
|
||||
} catch (IOException | InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||
|
@ -626,7 +626,7 @@ public class GeyserSession implements CommandSender {
|
|||
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||
*/
|
||||
public void sendUpstreamPacket(BedrockPacket packet) {
|
||||
if (upstream != null && !upstream.isClosed()) {
|
||||
if (upstream != null) {
|
||||
upstream.sendPacket(packet);
|
||||
} else {
|
||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " but the session was null");
|
||||
|
@ -639,7 +639,7 @@ public class GeyserSession implements CommandSender {
|
|||
* @param packet the bedrock packet from the NukkitX protocol lib
|
||||
*/
|
||||
public void sendUpstreamPacketImmediately(BedrockPacket packet) {
|
||||
if (upstream != null && !upstream.isClosed()) {
|
||||
if (upstream != null) {
|
||||
upstream.sendPacketImmediately(packet);
|
||||
} else {
|
||||
connector.getLogger().debug("Tried to send upstream packet " + packet.getClass().getSimpleName() + " immediately but the session was null");
|
||||
|
|
|
@ -41,17 +41,15 @@ public class UpstreamSession {
|
|||
private boolean initialized = false;
|
||||
|
||||
public void sendPacket(@NonNull BedrockPacket packet) {
|
||||
if (isClosed())
|
||||
return;
|
||||
|
||||
session.sendPacket(packet);
|
||||
if (!isClosed()) {
|
||||
session.sendPacket(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPacketImmediately(@NonNull BedrockPacket packet) {
|
||||
if (isClosed())
|
||||
return;
|
||||
|
||||
session.sendPacketImmediately(packet);
|
||||
if (!isClosed()) {
|
||||
session.sendPacketImmediately(packet);
|
||||
}
|
||||
}
|
||||
|
||||
public void disconnect(String reason) {
|
||||
|
|
|
@ -31,37 +31,40 @@ import lombok.Setter;
|
|||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
|
||||
@Getter
|
||||
public class WorldCache {
|
||||
|
||||
private GeyserSession session;
|
||||
|
||||
private final GeyserSession session;
|
||||
@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();
|
||||
}
|
||||
|
||||
public void removeScoreboard() {
|
||||
if (scoreboard != null) {
|
||||
Collection<Objective> objectives = scoreboard.getObjectives().values();
|
||||
scoreboard = new Scoreboard(session);
|
||||
|
||||
for (Objective objective : objectives) {
|
||||
for (Objective objective : scoreboard.getObjectives().values()) {
|
||||
scoreboard.despawnObjective(objective);
|
||||
}
|
||||
scoreboard = new Scoreboard(session);
|
||||
}
|
||||
}
|
||||
|
||||
public int increaseAndGetScoreboardPacketsPerSecond() {
|
||||
int pendingPps = scoreboardUpdater.incrementAndGetPacketsPerSecond();
|
||||
int pps = scoreboardUpdater.getPacketsPerSecond();
|
||||
return Math.max(pps, pendingPps);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the client to hide or show the coordinates
|
||||
*
|
||||
|
|
|
@ -41,13 +41,11 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
|||
|
||||
@Override
|
||||
public void translate(ServerScoreboardObjectivePacket packet, GeyserSession session) {
|
||||
WorldCache cache = session.getWorldCache();
|
||||
Scoreboard scoreboard = cache.getScoreboard();
|
||||
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
Objective objective = scoreboard.getObjective(packet.getName());
|
||||
|
||||
if (objective == null && packet.getAction() != ObjectiveAction.REMOVE) {
|
||||
objective = scoreboard.registerNewObjective(packet.getName(), true);
|
||||
objective = scoreboard.registerNewObjective(packet.getName(), false);
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
|
@ -61,6 +59,8 @@ public class JavaScoreboardObjectiveTranslator extends PacketTranslator<ServerSc
|
|||
break;
|
||||
}
|
||||
|
||||
if (objective != null && !objective.isTemp()) scoreboard.onUpdate();
|
||||
if (objective != null && objective.isActive()) {
|
||||
scoreboard.onUpdate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,10 +28,12 @@ package org.geysermc.connector.network.translators.java.scoreboard;
|
|||
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.GeyserLogger;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.translators.PacketTranslator;
|
||||
import org.geysermc.connector.network.translators.Translator;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.scoreboard.Team;
|
||||
import org.geysermc.connector.scoreboard.UpdateType;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
@ -42,10 +44,15 @@ import java.util.Set;
|
|||
|
||||
@Translator(packet = ServerTeamPacket.class)
|
||||
public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
||||
private static final GeyserLogger LOGGER = GeyserConnector.getInstance().getLogger();
|
||||
|
||||
@Override
|
||||
public void translate(ServerTeamPacket packet, GeyserSession session) {
|
||||
GeyserConnector.getInstance().getLogger().debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
||||
if (LOGGER.isDebug()) {
|
||||
LOGGER.debug("Team packet " + packet.getTeamName() + " " + packet.getAction() + " " + Arrays.toString(packet.getPlayers()));
|
||||
}
|
||||
|
||||
int pps = session.getWorldCache().increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
Team team = scoreboard.getTeam(packet.getTeamName());
|
||||
|
@ -58,38 +65,53 @@ public class JavaTeamTranslator extends PacketTranslator<ServerTeamPacket> {
|
|||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()));
|
||||
break;
|
||||
case UPDATE:
|
||||
if (team != null) {
|
||||
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
team.setName(MessageUtils.getBedrockMessage(packet.getDisplayName()))
|
||||
.setColor(packet.getColor())
|
||||
.setPrefix(MessageUtils.getTranslatedBedrockMessage(packet.getPrefix(), session.getClientData().getLanguageCode()))
|
||||
.setSuffix(MessageUtils.getTranslatedBedrockMessage(packet.getSuffix(), session.getClientData().getLanguageCode()))
|
||||
.setUpdateType(UpdateType.UPDATE);
|
||||
break;
|
||||
case ADD_PLAYER:
|
||||
if (team != null) {
|
||||
team.addEntities(packet.getPlayers());
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
team.addEntities(packet.getPlayers());
|
||||
break;
|
||||
case REMOVE_PLAYER:
|
||||
if (team != null) {
|
||||
team.removeEntities(packet.getPlayers());
|
||||
} else {
|
||||
GeyserConnector.getInstance().getLogger().debug(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_not_registered", packet.getAction(), packet.getTeamName()));
|
||||
if (team == null) {
|
||||
LOGGER.debug(LanguageUtils.getLocaleStringLog(
|
||||
"geyser.network.translator.team.failed_not_registered",
|
||||
packet.getAction(), packet.getTeamName()
|
||||
));
|
||||
return;
|
||||
}
|
||||
team.removeEntities(packet.getPlayers());
|
||||
break;
|
||||
case REMOVE:
|
||||
scoreboard.removeTeam(packet.getTeamName());
|
||||
break;
|
||||
}
|
||||
scoreboard.onUpdate();
|
||||
|
||||
// ScoreboardUpdater will handle it for us if the packets per second
|
||||
// (for score and team packets) is higher then the first threshold
|
||||
if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||
scoreboard.onUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> toPlayerSet(String[] players) {
|
||||
return new ObjectOpenHashSet<>(Arrays.asList(players));
|
||||
return new ObjectOpenHashSet<>(players);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,48 +25,58 @@
|
|||
|
||||
package org.geysermc.connector.network.translators.java.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
|
||||
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.scoreboard.Objective;
|
||||
import org.geysermc.connector.scoreboard.Scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardAction;
|
||||
import com.github.steveice10.mc.protocol.packet.ingame.server.scoreboard.ServerUpdateScorePacket;
|
||||
import org.geysermc.connector.scoreboard.ScoreboardUpdater;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
@Translator(packet = ServerUpdateScorePacket.class)
|
||||
public class JavaUpdateScoreTranslator extends PacketTranslator<ServerUpdateScorePacket> {
|
||||
private final GeyserLogger logger;
|
||||
|
||||
public JavaUpdateScoreTranslator() {
|
||||
logger = GeyserConnector.getInstance().getLogger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void translate(ServerUpdateScorePacket packet, GeyserSession session) {
|
||||
try {
|
||||
Scoreboard scoreboard = session.getWorldCache().getScoreboard();
|
||||
WorldCache worldCache = session.getWorldCache();
|
||||
Scoreboard scoreboard = worldCache.getScoreboard();
|
||||
int pps = worldCache.increaseAndGetScoreboardPacketsPerSecond();
|
||||
|
||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
||||
GeyserConnector.getInstance().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective()));
|
||||
return;
|
||||
}
|
||||
Objective objective = scoreboard.getObjective(packet.getObjective());
|
||||
if (objective == null && packet.getAction() != ScoreboardAction.REMOVE) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.score.failed_objective", packet.getObjective()));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (packet.getAction()) {
|
||||
case ADD_OR_UPDATE:
|
||||
objective.setScore(packet.getEntry(), packet.getValue());
|
||||
break;
|
||||
case REMOVE:
|
||||
if (objective != null) {
|
||||
objective.resetScore(packet.getEntry());
|
||||
} else {
|
||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
||||
objective1.resetScore(packet.getEntry());
|
||||
}
|
||||
switch (packet.getAction()) {
|
||||
case ADD_OR_UPDATE:
|
||||
objective.setScore(packet.getEntry(), packet.getValue());
|
||||
break;
|
||||
case REMOVE:
|
||||
if (objective != null) {
|
||||
objective.removeScore(packet.getEntry());
|
||||
} else {
|
||||
for (Objective objective1 : scoreboard.getObjectives().values()) {
|
||||
objective1.removeScore(packet.getEntry());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// ScoreboardUpdater will handle it for us if the packets per second
|
||||
// (for score and team packets) is higher then the first threshold
|
||||
if (pps < ScoreboardUpdater.FIRST_SCORE_PACKETS_PER_SECOND_THRESHOLD) {
|
||||
scoreboard.onUpdate();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,23 +29,23 @@ import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition
|
|||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Getter
|
||||
public class Objective {
|
||||
private Scoreboard scoreboard;
|
||||
private long id;
|
||||
private boolean temp;
|
||||
private final Scoreboard scoreboard;
|
||||
private final long id;
|
||||
private boolean active = true;
|
||||
|
||||
@Setter
|
||||
private UpdateType updateType = UpdateType.ADD;
|
||||
private String objectiveName;
|
||||
private String displaySlot;
|
||||
private String displaySlotName;
|
||||
private String displayName = "unknown";
|
||||
private int type = 0; // 0 = integer, 1 = heart
|
||||
|
||||
private Map<String, Score> scores = new HashMap<>();
|
||||
private Map<String, Score> scores = new ConcurrentHashMap<>();
|
||||
|
||||
private Objective(Scoreboard scoreboard) {
|
||||
this.id = scoreboard.getNextId().getAndIncrement();
|
||||
|
@ -54,23 +54,20 @@ public class Objective {
|
|||
|
||||
/**
|
||||
* /!\ This method is made for temporary objectives until the real objective is received
|
||||
* @param scoreboard the scoreboard
|
||||
*
|
||||
* @param scoreboard the scoreboard
|
||||
* @param objectiveName the name of the objective
|
||||
*/
|
||||
public Objective(Scoreboard scoreboard, String objectiveName) {
|
||||
this(scoreboard);
|
||||
this.objectiveName = objectiveName;
|
||||
this.temp = true;
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
public Objective(Scoreboard scoreboard, String objectiveName, ScoreboardPosition displaySlot, String displayName, int type) {
|
||||
this(scoreboard, objectiveName, displaySlot.name().toLowerCase(), displayName, type);
|
||||
}
|
||||
|
||||
public Objective(Scoreboard scoreboard, String objectiveName, String displaySlot, String displayName, int type) {
|
||||
this(scoreboard);
|
||||
this.objectiveName = objectiveName;
|
||||
this.displaySlot = displaySlot;
|
||||
this.displaySlotName = translateDisplaySlot(displaySlot);
|
||||
this.displayName = displayName;
|
||||
this.type = type;
|
||||
}
|
||||
|
@ -87,21 +84,9 @@ public class Objective {
|
|||
public void setScore(String id, int score) {
|
||||
if (scores.containsKey(id)) {
|
||||
scores.get(id).setScore(score).setUpdateType(UpdateType.ADD);
|
||||
} else {
|
||||
registerScore(id, score);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void setScoreText(String oldText, String newText) {
|
||||
if (!scores.containsKey(oldText) || oldText.equals(newText)) return;
|
||||
Score oldScore = scores.get(oldText);
|
||||
|
||||
Score newScore = new Score(this, newText)
|
||||
.setScore(oldScore.getScore())
|
||||
.setTeam(scoreboard.getTeamFor(newText));
|
||||
|
||||
scores.put(newText, newScore);
|
||||
oldScore.setUpdateType(UpdateType.REMOVE);
|
||||
registerScore(id, score);
|
||||
}
|
||||
|
||||
public int getScore(String id) {
|
||||
|
@ -113,37 +98,61 @@ public class Objective {
|
|||
|
||||
public Score getScore(int line) {
|
||||
for (Score score : scores.values()) {
|
||||
if (score.getScore() == line) return score;
|
||||
if (score.getScore() == line) {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void resetScore(String id) {
|
||||
public void removeScore(String id) {
|
||||
if (scores.containsKey(id)) {
|
||||
scores.get(id).setUpdateType(UpdateType.REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeScore(String id) {
|
||||
/**
|
||||
* Used internally to remove a score from the score map
|
||||
*/
|
||||
public void removeScore0(String id) {
|
||||
scores.remove(id);
|
||||
}
|
||||
|
||||
public Objective setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE;
|
||||
if (updateType == UpdateType.NOTHING) {
|
||||
updateType = UpdateType.UPDATE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Objective setType(int type) {
|
||||
this.type = type;
|
||||
if (updateType == UpdateType.NOTHING) updateType = UpdateType.UPDATE;
|
||||
if (updateType == UpdateType.NOTHING) {
|
||||
updateType = UpdateType.UPDATE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void removeTemp(ScoreboardPosition displaySlot) {
|
||||
if (temp) {
|
||||
temp = false;
|
||||
this.displaySlot = displaySlot.name().toLowerCase();
|
||||
public void setActive(ScoreboardPosition displaySlot) {
|
||||
if (!active) {
|
||||
active = true;
|
||||
displaySlotName = translateDisplaySlot(displaySlot);
|
||||
}
|
||||
}
|
||||
|
||||
public void removed() {
|
||||
scores = null;
|
||||
}
|
||||
|
||||
private static String translateDisplaySlot(ScoreboardPosition displaySlot) {
|
||||
switch (displaySlot) {
|
||||
case BELOW_NAME:
|
||||
return "belowname";
|
||||
case PLAYER_LIST:
|
||||
return "list";
|
||||
default:
|
||||
return "sidebar";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,40 +25,63 @@
|
|||
|
||||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
import com.nukkitx.protocol.bedrock.data.ScoreInfo;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Getter @Setter
|
||||
@Getter
|
||||
@Accessors(chain = true)
|
||||
public class Score {
|
||||
private Objective objective;
|
||||
private long id;
|
||||
private final Objective objective;
|
||||
private ScoreInfo cachedInfo;
|
||||
private final long id;
|
||||
|
||||
@Setter
|
||||
private UpdateType updateType = UpdateType.ADD;
|
||||
private String name;
|
||||
private final String name;
|
||||
private Team team;
|
||||
private int score;
|
||||
@Setter
|
||||
private int oldScore = Integer.MIN_VALUE;
|
||||
|
||||
public Score(Objective objective, String name) {
|
||||
this.id = objective.getScoreboard().getNextId().getAndIncrement();
|
||||
this.objective = objective;
|
||||
this.name = name;
|
||||
update();
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
if (team != null && team.getUpdateType() != UpdateType.REMOVE) {
|
||||
if (team != null) {
|
||||
return team.getPrefix() + name + team.getSuffix();
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public Score setScore(int score) {
|
||||
if (oldScore == Integer.MIN_VALUE) {
|
||||
this.oldScore = score;
|
||||
}
|
||||
this.score = score;
|
||||
updateType = UpdateType.UPDATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Score setTeam(Team team) {
|
||||
if (this.team != null && team != null) {
|
||||
if (!this.team.equals(team)) {
|
||||
this.team = team;
|
||||
updateType = UpdateType.UPDATE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
// simplified from (this.team != null && team == null) || (this.team == null && team != null)
|
||||
if (this.team != null || team != null) {
|
||||
this.team = team;
|
||||
updateType = UpdateType.UPDATE;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
cachedInfo = new ScoreInfo(id, objective.getObjectiveName(), score, getDisplayName());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,73 +26,72 @@
|
|||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
import com.github.steveice10.mc.protocol.data.game.scoreboard.ScoreboardPosition;
|
||||
import com.nukkitx.protocol.bedrock.BedrockPacket;
|
||||
import com.nukkitx.protocol.bedrock.data.ScoreInfo;
|
||||
import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket;
|
||||
import com.nukkitx.protocol.bedrock.packet.SetScorePacket;
|
||||
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
|
||||
import lombok.Getter;
|
||||
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.GeyserLogger;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.geysermc.connector.scoreboard.UpdateType.*;
|
||||
|
||||
@Getter
|
||||
public class Scoreboard {
|
||||
private GeyserSession session;
|
||||
private AtomicLong nextId = new AtomicLong(0);
|
||||
private final GeyserSession session;
|
||||
private final GeyserLogger logger;
|
||||
private final AtomicLong nextId = new AtomicLong(0);
|
||||
|
||||
private Map<String, Objective> objectives = new HashMap<>();
|
||||
private Map<String, Team> teams = new HashMap<>();
|
||||
private final Map<String, Objective> objectives = new ConcurrentHashMap<>();
|
||||
private final Map<String, Team> teams = new HashMap<>();
|
||||
|
||||
private int lastScoreCount = 0;
|
||||
|
||||
public Scoreboard(GeyserSession session) {
|
||||
this.session = session;
|
||||
this.logger = GeyserConnector.getInstance().getLogger();
|
||||
}
|
||||
|
||||
public Objective registerNewObjective(String objectiveId, boolean temp) {
|
||||
if (!temp || objectives.containsKey(objectiveId)) return objectives.get(objectiveId);
|
||||
public Objective registerNewObjective(String objectiveId, boolean active) {
|
||||
if (active || objectives.containsKey(objectiveId)) {
|
||||
return objectives.get(objectiveId);
|
||||
}
|
||||
Objective objective = new Objective(this, objectiveId);
|
||||
objectives.put(objectiveId, objective);
|
||||
return objective;
|
||||
}
|
||||
|
||||
public Objective registerNewObjective(String objectiveId, ScoreboardPosition displaySlot) {
|
||||
Objective objective = null;
|
||||
if (objectives.containsKey(objectiveId)) {
|
||||
objective = objectives.get(objectiveId);
|
||||
if (objective.isTemp()) objective.removeTemp(displaySlot);
|
||||
else {
|
||||
despawnObjective(objective);
|
||||
objective = null;
|
||||
Objective objective = objectives.get(objectiveId);
|
||||
if (objective != null) {
|
||||
if (!objective.isActive()) {
|
||||
objective.setActive(displaySlot);
|
||||
return objective;
|
||||
}
|
||||
despawnObjective(objective);
|
||||
}
|
||||
if (objective == null) {
|
||||
objective = new Objective(this, objectiveId, displaySlot, "unknown", 0);
|
||||
objectives.put(objectiveId, objective);
|
||||
}
|
||||
|
||||
objective = new Objective(this, objectiveId, displaySlot, "unknown", 0);
|
||||
objectives.put(objectiveId, objective);
|
||||
return objective;
|
||||
}
|
||||
|
||||
public Team registerNewTeam(String teamName, Set<String> players) {
|
||||
if (teams.containsKey(teamName)) {
|
||||
session.getConnector().getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName));
|
||||
return getTeam(teamName);
|
||||
Team team = teams.get(teamName);
|
||||
if (team != null) {
|
||||
logger.info(LanguageUtils.getLocaleStringLog("geyser.network.translator.team.failed_overrides", teamName));
|
||||
return team;
|
||||
}
|
||||
|
||||
Team team = new Team(this, teamName).setEntities(players);
|
||||
team = new Team(this, teamName).setEntities(players);
|
||||
teams.put(teamName, team);
|
||||
|
||||
for (Objective objective : objectives.values()) {
|
||||
for (Score score : objective.getScores().values()) {
|
||||
if (players.contains(score.getName())) {
|
||||
score.setTeam(team);
|
||||
}
|
||||
}
|
||||
}
|
||||
return team;
|
||||
}
|
||||
|
||||
|
@ -106,102 +105,119 @@ public class Scoreboard {
|
|||
|
||||
public void unregisterObjective(String objectiveName) {
|
||||
Objective objective = getObjective(objectiveName);
|
||||
if (objective != null) objective.setUpdateType(REMOVE);
|
||||
if (objective != null) {
|
||||
objective.setUpdateType(REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTeam(String teamName) {
|
||||
Team remove = teams.remove(teamName);
|
||||
if (remove != null) remove.setUpdateType(REMOVE);
|
||||
if (remove != null) {
|
||||
remove.setUpdateType(REMOVE);
|
||||
}
|
||||
}
|
||||
|
||||
public void onUpdate() {
|
||||
Set<Objective> changedObjectives = new ObjectOpenHashSet<>();
|
||||
List<ScoreInfo> addScores = new ArrayList<>();
|
||||
List<ScoreInfo> removeScores = new ArrayList<>();
|
||||
List<ScoreInfo> addScores = new ArrayList<>(getLastScoreCount());
|
||||
List<ScoreInfo> removeScores = new ArrayList<>(getLastScoreCount());
|
||||
|
||||
for (String objectiveId : new ArrayList<>(objectives.keySet())) {
|
||||
Objective objective = objectives.get(objectiveId);
|
||||
if (objective.isTemp()) {
|
||||
session.getConnector().getLogger().debug("Ignoring temp Scoreboard Objective '"+ objectiveId +'\'');
|
||||
for (Objective objective : objectives.values()) {
|
||||
if (!objective.isActive()) {
|
||||
logger.debug("Ignoring non-active Scoreboard Objective '"+ objective.getObjectiveName() +'\'');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (objective.getUpdateType() != NOTHING) changedObjectives.add(objective);
|
||||
// hearts can't hold teams, so we treat them differently
|
||||
if (objective.getType() == 1) {
|
||||
for (Score score : objective.getScores().values()) {
|
||||
if (score.getUpdateType() == NOTHING) {
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean update = score.getUpdateType() == UPDATE;
|
||||
if (update) {
|
||||
score.update();
|
||||
}
|
||||
|
||||
if (score.getUpdateType() == ADD || update) {
|
||||
addScores.add(score.getCachedInfo());
|
||||
}
|
||||
if (score.getUpdateType() == REMOVE || update) {
|
||||
removeScores.add(score.getCachedInfo());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
boolean globalUpdate = objective.getUpdateType() == UPDATE;
|
||||
boolean globalAdd = objective.getUpdateType() == ADD || globalUpdate;
|
||||
boolean globalRemove = objective.getUpdateType() == REMOVE || globalUpdate;
|
||||
boolean globalAdd = objective.getUpdateType() == ADD;
|
||||
boolean globalRemove = objective.getUpdateType() == REMOVE;
|
||||
|
||||
boolean hasUpdate = globalUpdate;
|
||||
|
||||
List<Score> handledScores = new ArrayList<>();
|
||||
for (String identifier : new ObjectOpenHashSet<>(objective.getScores().keySet())) {
|
||||
Score score = objective.getScores().get(identifier);
|
||||
for (Score score : objective.getScores().values()) {
|
||||
Team team = score.getTeam();
|
||||
|
||||
boolean inTeam = team != null && team.getEntities().contains(score.getName());
|
||||
boolean add = globalAdd || globalUpdate;
|
||||
boolean remove = globalRemove;
|
||||
boolean teamChanged = false;
|
||||
if (team != null) {
|
||||
if (team.getUpdateType() == REMOVE || !team.hasEntity(score.getName())) {
|
||||
score.setTeam(null);
|
||||
teamChanged = true;
|
||||
}
|
||||
|
||||
boolean teamAdd = team != null && (team.getUpdateType() == ADD || team.getUpdateType() == UPDATE);
|
||||
boolean teamRemove = team != null && (team.getUpdateType() == REMOVE || team.getUpdateType() == UPDATE);
|
||||
teamChanged |= team.getUpdateType() == UPDATE;
|
||||
|
||||
if (team != null && (team.getUpdateType() == REMOVE || !inTeam)) score.setTeam(null);
|
||||
|
||||
boolean add = (hasUpdate || globalAdd || teamAdd || teamRemove || score.getUpdateType() == ADD || score.getUpdateType() == UPDATE) && (score.getUpdateType() != REMOVE);
|
||||
boolean remove = hasUpdate || globalRemove || teamAdd || teamRemove || score.getUpdateType() == REMOVE || score.getUpdateType() == UPDATE;
|
||||
|
||||
boolean updated = false;
|
||||
if (!hasUpdate) {
|
||||
updated = hasUpdate = add;
|
||||
add |= team.getUpdateType() == ADD || team.getUpdateType() == UPDATE;
|
||||
remove |= team.getUpdateType() == REMOVE;
|
||||
}
|
||||
|
||||
if (updated) {
|
||||
for (Score score1 : handledScores) {
|
||||
ScoreInfo scoreInfo = new ScoreInfo(score1.getId(), score1.getObjective().getObjectiveName(), score1.getScore(), score1.getDisplayName());
|
||||
addScores.add(scoreInfo);
|
||||
removeScores.add(scoreInfo);
|
||||
}
|
||||
add |= score.getUpdateType() == ADD || score.getUpdateType() == UPDATE;
|
||||
remove |= score.getUpdateType() == REMOVE;
|
||||
if (score.getUpdateType() == REMOVE) {
|
||||
add = false;
|
||||
}
|
||||
|
||||
if (score.getUpdateType() == UPDATE || teamChanged) {
|
||||
score.update();
|
||||
}
|
||||
|
||||
if (add) {
|
||||
addScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getScore(), score.getDisplayName()));
|
||||
addScores.add(score.getCachedInfo());
|
||||
}
|
||||
if (remove) {
|
||||
removeScores.add(new ScoreInfo(score.getId(), score.getObjective().getObjectiveName(), score.getOldScore(), score.getDisplayName()));
|
||||
removeScores.add(score.getCachedInfo());
|
||||
}
|
||||
// score is pending to be updated, so we use the current score as the old score
|
||||
score.setOldScore(score.getScore());
|
||||
|
||||
// score is pending to be removed, so we can remove it from the objective
|
||||
if (score.getUpdateType() == REMOVE) {
|
||||
objective.removeScore(score.getName());
|
||||
objective.removeScore0(score.getName());
|
||||
}
|
||||
|
||||
if (add || remove) {
|
||||
changedObjectives.add(objective);
|
||||
} else { // stays the same like before
|
||||
handledScores.add(score);
|
||||
}
|
||||
score.setUpdateType(NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
for (Objective objective : changedObjectives) {
|
||||
boolean update = objective.getUpdateType() == NOTHING || objective.getUpdateType() == UPDATE;
|
||||
if (objective.getUpdateType() == REMOVE || update) {
|
||||
if (globalRemove || globalUpdate) {
|
||||
RemoveObjectivePacket removeObjectivePacket = new RemoveObjectivePacket();
|
||||
removeObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||
session.sendUpstreamPacket(removeObjectivePacket);
|
||||
if (objective.getUpdateType() == REMOVE) {
|
||||
objectives.remove(objective.getObjectiveName()); // now we can deregister
|
||||
objective.removed();
|
||||
}
|
||||
}
|
||||
if (objective.getUpdateType() == ADD || update) {
|
||||
|
||||
if (globalAdd || globalUpdate) {
|
||||
SetDisplayObjectivePacket displayObjectivePacket = new SetDisplayObjectivePacket();
|
||||
displayObjectivePacket.setObjectiveId(objective.getObjectiveName());
|
||||
displayObjectivePacket.setDisplayName(objective.getDisplayName());
|
||||
displayObjectivePacket.setCriteria("dummy");
|
||||
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlot());
|
||||
displayObjectivePacket.setDisplaySlot(objective.getDisplaySlotName());
|
||||
displayObjectivePacket.setSortOrder(1); // ??
|
||||
session.sendUpstreamPacket(displayObjectivePacket);
|
||||
}
|
||||
|
||||
objective.setUpdateType(NOTHING);
|
||||
}
|
||||
|
||||
|
@ -218,6 +234,8 @@ public class Scoreboard {
|
|||
setScorePacket.setInfos(addScores);
|
||||
session.sendUpstreamPacket(setScorePacket);
|
||||
}
|
||||
|
||||
lastScoreCount = addScores.size();
|
||||
}
|
||||
|
||||
public void despawnObjective(Objective objective) {
|
||||
|
@ -235,6 +253,8 @@ public class Scoreboard {
|
|||
));
|
||||
}
|
||||
|
||||
objective.removed();
|
||||
|
||||
if (!toRemove.isEmpty()) {
|
||||
SetScorePacket setScorePacket = new SetScorePacket();
|
||||
setScorePacket.setAction(SetScorePacket.Action.REMOVE);
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (c) 2019-2020 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.connector.scoreboard;
|
||||
|
||||
import org.geysermc.connector.GeyserConnector;
|
||||
import org.geysermc.connector.configuration.GeyserConfiguration;
|
||||
import org.geysermc.connector.network.session.GeyserSession;
|
||||
import org.geysermc.connector.network.session.cache.WorldCache;
|
||||
import org.geysermc.connector.utils.LanguageUtils;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class ScoreboardUpdater extends Thread {
|
||||
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 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() {
|
||||
while (!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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getPacketsPerSecond() {
|
||||
return packetsPerSecond.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the Scoreboard Packets Per Second and return the updated value
|
||||
*/
|
||||
public int incrementAndGetPacketsPerSecond() {
|
||||
return pendingPacketsPerSecond.incrementAndGet();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
|
@ -35,8 +35,7 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Getter @Setter
|
||||
@Accessors(chain = true)
|
||||
public class Team {
|
||||
private final Scoreboard scoreboard;
|
||||
|
@ -44,6 +43,7 @@ public class Team {
|
|||
|
||||
private UpdateType updateType = UpdateType.ADD;
|
||||
private String name;
|
||||
|
||||
private String prefix;
|
||||
private TeamColor color;
|
||||
private String suffix;
|
||||
|
@ -57,8 +57,7 @@ public class Team {
|
|||
public void addEntities(String... names) {
|
||||
List<String> added = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
if (!entities.contains(name)) {
|
||||
entities.add(name);
|
||||
if (entities.add(name)) {
|
||||
added.add(name);
|
||||
}
|
||||
}
|
||||
|
@ -78,4 +77,35 @@ public class Team {
|
|||
}
|
||||
setUpdateType(UpdateType.UPDATE);
|
||||
}
|
||||
|
||||
public boolean hasEntity(String name) {
|
||||
return entities.contains(name);
|
||||
}
|
||||
|
||||
public Team setPrefix(String prefix) {
|
||||
// replace "null" to an empty string,
|
||||
// we do this here to improve the performance of Score#getDisplayName
|
||||
if (prefix.length() == 4 && "null".equals(prefix)) {
|
||||
this.prefix = "";
|
||||
return this;
|
||||
}
|
||||
this.prefix = prefix;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Team setSuffix(String suffix) {
|
||||
// replace "null" to an empty string,
|
||||
// we do this here to improve the performance of Score#getDisplayName
|
||||
if (suffix.length() == 4 && "null".equals(suffix)) {
|
||||
this.suffix = "";
|
||||
return this;
|
||||
}
|
||||
this.suffix = suffix;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -124,6 +124,12 @@ metrics:
|
|||
|
||||
# ADVANCED OPTIONS - DO NOT TOUCH UNLESS YOU KNOW WHAT YOU ARE DOING!
|
||||
|
||||
# Geyser updates the Scoreboard after every Scoreboard packet, but when Geyser tries to handle
|
||||
# a lot of scoreboard packets per second can cause serious lag.
|
||||
# This option allows you to specify after how many Scoreboard packets per seconds
|
||||
# the Scoreboard updates will be limited to four updates per second.
|
||||
scoreboard-packet-threshold: 20
|
||||
|
||||
# Allow connections from ProxyPass and Waterdog.
|
||||
# See https://www.spigotmc.org/wiki/firewall-guide/ for assistance - use UDP instead of TCP.
|
||||
enable-proxy-connections: false
|
||||
|
|
Loading…
Reference in a new issue