Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add client setting for cooldown type, refactor show-coordinates #2193

Merged
merged 12 commits into from
May 15, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) {

defaultAuthType = AuthType.getByName(config.getRemote().getAuthType());

CooldownUtils.setShowCooldown(config.getShowCooldown());
CooldownUtils.setDefaultShowCooldown(config.getShowCooldown());
DimensionUtils.changeBedrockNetherId(config.isAboveBedrockNetherBuilding()); // Apply End dimension ID workaround to Nether
SkullBlockEntityTranslator.ALLOW_CUSTOM_SKULLS = config.isAllowCustomSkulls();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ public class GeyserSession implements CommandSender {
private ChunkCache chunkCache;
private EntityCache entityCache;
private EntityEffectCache effectCache;
private final PreferencesCache preferencesCache;
private final TagCache tagCache;
private WorldCache worldCache;
private WindowCache windowCache;
Expand Down Expand Up @@ -453,6 +454,7 @@ public GeyserSession(GeyserConnector connector, BedrockServerSession bedrockServ
this.chunkCache = new ChunkCache(this);
this.entityCache = new EntityCache(this);
this.effectCache = new EntityEffectCache();
this.preferencesCache = new PreferencesCache(this);
this.tagCache = new TagCache();
this.worldCache = new WorldCache(this);
this.windowCache = new WindowCache(this);
Expand Down Expand Up @@ -1226,7 +1228,7 @@ public void sendDownstreamPacket(Packet packet) {
public void setReducedDebugInfo(boolean value) {
reducedDebugInfo = value;
// Set the showCoordinates data. This is done because updateShowCoordinates() uses this gamerule as a variable.
getWorldCache().updateShowCoordinates();
getPreferencesCache().updateShowCoordinates();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be the Lombok call, and can just be preferencesCache.updateShowCoordinates()

}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2019-2021 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.network.session.cache;

import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.utils.CooldownUtils;

@Getter
public class PreferencesCache {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this name should be changed, but I'm not sure to what. I'm going to have a PR after OptionalPack is merged that allows a plugin to change if a client can Bedrock godbridge or not, so the boolean for that might as well be stuck here. Open to suggestions.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it would stop all connected geyser players from god-bridging, why would the boolean be stored in every session instance?

Do you mean the PR would allow plugins to change an allowGodbridging value that geyser uses to prevent god-bridging, or it would allow an extension plugin to do the work of preventing god-bridging?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it would stop all connected geyser players from god-bridging, why would the boolean be stored in every session instance?

We can't do that because of GeyserConnect, or if server owners only wanted to block per-world for example. The plugin would be separate and use plugin messages to communicate to Geyser.

private final GeyserSession session;

/**
* True if the client prefers being shown their coordinates, regardless if they're being shown or not.
* This will be true everytime the client joins the server because neither the client nor server store the preference permanently.
*/
@Setter
private boolean prefersShowCoordinates = true;
/**
* If the client's preference will be ignored, this will return false.
*/
private boolean allowShowCoordinates;

/**
* Which CooldownType the client prefers. Initially set to {@link CooldownUtils#getDefaultShowCooldown()}.
*/
@Setter
private CooldownUtils.CooldownType cooldownPreference = CooldownUtils.getDefaultShowCooldown();

public PreferencesCache(GeyserSession session) {
this.session = session;
}

/**
* Tell the client to hide or show the coordinates.
*
* If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply: <br>
* <br>
* {@link GeyserSession#reducedDebugInfo} is enabled
* {@link GeyserConfiguration#isShowCoordinates()} is disabled
*/
public void updateShowCoordinates() {
allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates();
session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import com.github.steveice10.mc.protocol.data.game.setting.Difficulty;
import lombok.Getter;
import lombok.Setter;
import org.geysermc.connector.configuration.GeyserConfiguration;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.scoreboard.Objective;
import org.geysermc.connector.scoreboard.Scoreboard;
Expand All @@ -40,13 +39,6 @@ public class WorldCache {
@Setter
private Difficulty difficulty = Difficulty.EASY;

/**
* True if the client prefers being shown their coordinates, regardless if they're being shown or not.
* This will be true everytime the client joins the server because neither the client nor server store the preference permanently.
*/
@Setter
private boolean prefersShowCoordinates = true;

private Scoreboard scoreboard;
private final ScoreboardUpdater scoreboardUpdater;

Expand All @@ -71,17 +63,4 @@ public int increaseAndGetScoreboardPacketsPerSecond() {
int pps = scoreboardUpdater.getPacketsPerSecond();
return Math.max(pps, pendingPps);
}

/**
* Tell the client to hide or show the coordinates.
*
* If {@link #prefersShowCoordinates} is true, coordinates will be shown, unless either of the following conditions apply: <br>
* <br>
* {@link GeyserSession#reducedDebugInfo} is enabled
* {@link GeyserConfiguration#isShowCoordinates()} is disabled
*/
public void updateShowCoordinates() {
boolean allowShowCoordinates = !session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates();
session.sendGameRule("showcoordinates", allowShowCoordinates && prefersShowCoordinates);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.nukkitx.protocol.bedrock.packet.SetTitlePacket;
import lombok.Getter;
import org.geysermc.connector.network.session.GeyserSession;
import org.geysermc.connector.network.session.cache.PreferencesCache;

import java.util.concurrent.TimeUnit;

Expand All @@ -36,18 +37,24 @@
* Much of the work here is from the wonderful folks from ViaRewind: https://github.com/ViaVersion/ViaRewind
*/
public class CooldownUtils {
private static CooldownType SHOW_COOLDOWN;
private static CooldownType DEFAULT_SHOW_COOLDOWN;

public static void setShowCooldown(String showCooldown) {
SHOW_COOLDOWN = CooldownType.getByName(showCooldown);
public static void setDefaultShowCooldown(String showCooldown) {
DEFAULT_SHOW_COOLDOWN = CooldownType.getByName(showCooldown);
}
Camotoy marked this conversation as resolved.
Show resolved Hide resolved
public static CooldownType getDefaultShowCooldown() {
return DEFAULT_SHOW_COOLDOWN;
}

/**
* Starts sending the fake cooldown to the Bedrock client.
* Starts sending the fake cooldown to the Bedrock client. If the cooldown is not disabled, the sent type is {@link PreferencesCache#getCooldownPreference()}
* @param session GeyserSession
*/
public static void sendCooldown(GeyserSession session) {
if (SHOW_COOLDOWN == CooldownType.DISABLED) return;
if (DEFAULT_SHOW_COOLDOWN == CooldownType.DISABLED) return;
CooldownType sessionPreference = session.getPreferencesCache().getCooldownPreference();
if (sessionPreference == CooldownType.DISABLED) return;

if (session.getAttackSpeed() == 0.0 || session.getAttackSpeed() > 20) return; // 0.0 usually happens on login and causes issues with visuals; anything above 20 means a plugin like OldCombatMechanics is being used
// Needs to be sent or no subtitle packet is recognized by the client
SetTitlePacket titlePacket = new SetTitlePacket();
Expand All @@ -56,19 +63,20 @@ public static void sendCooldown(GeyserSession session) {
session.sendUpstreamPacket(titlePacket);
session.setLastHitTime(System.currentTimeMillis());
long lastHitTime = session.getLastHitTime(); // Used later to prevent multiple scheduled cooldown threads
computeCooldown(session, lastHitTime);
computeCooldown(session, sessionPreference, lastHitTime);
}

/**
* Keeps updating the cooldown until the bar is complete.
* @param session GeyserSession
* @param sessionPreference The type of cooldown the client prefers
* @param lastHitTime The time of the last hit. Used to gauge how long the cooldown is taking.
*/
private static void computeCooldown(GeyserSession session, long lastHitTime) {
private static void computeCooldown(GeyserSession session, CooldownType sessionPreference, long lastHitTime) {
if (session.isClosed()) return; // Don't run scheduled tasks if the client left
if (lastHitTime != session.getLastHitTime()) return; // Means another cooldown has started so there's no need to continue this one
SetTitlePacket titlePacket = new SetTitlePacket();
if (SHOW_COOLDOWN == CooldownType.ACTIONBAR) {
if (sessionPreference == CooldownType.ACTIONBAR) {
titlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
} else {
titlePacket.setType(SetTitlePacket.Type.SUBTITLE);
Expand All @@ -79,10 +87,10 @@ private static void computeCooldown(GeyserSession session, long lastHitTime) {
titlePacket.setStayTime(2);
session.sendUpstreamPacket(titlePacket);
if (hasCooldown(session)) {
session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
session.getConnector().getGeneralThreadPool().schedule(() -> computeCooldown(session, sessionPreference, lastHitTime), 50, TimeUnit.MILLISECONDS); // Updated per tick. 1000 divided by 20 ticks equals 50
} else {
SetTitlePacket removeTitlePacket = new SetTitlePacket();
if (SHOW_COOLDOWN == CooldownType.ACTIONBAR) {
if (sessionPreference == CooldownType.ACTIONBAR) {
removeTitlePacket.setType(SetTitlePacket.Type.ACTIONBAR);
} else {
removeTitlePacket.setType(SetTitlePacket.Type.SUBTITLE);
Expand Down Expand Up @@ -149,7 +157,7 @@ public static CooldownType getByName(String name) {
return type;
}
}
return DISABLED;
return TITLE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't do this because legacy configs that have their cooldown settings set to false will break.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,24 @@ public static void buildForm(GeyserSession session) {
CustomFormBuilder builder = new CustomFormBuilder(LanguageUtils.getPlayerLocaleString("geyser.settings.title.main", language));
builder.setIcon(new FormImage(FormImage.FormImageType.PATH, "textures/ui/settings_glyph_color_2x.png"));

// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) {
// Only show the client title if any of the client settings are available
if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) {
builder.addComponent(new LabelComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.title.client", language)));

builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getWorldCache().isPrefersShowCoordinates()));
// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
if (session.getPreferencesCache().isAllowShowCoordinates()) {
builder.addComponent(new ToggleComponent(LanguageUtils.getPlayerLocaleString("geyser.settings.option.coordinates", language), session.getPreferencesCache().isPrefersShowCoordinates()));
}

if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) {
DropdownComponent cooldownDropdown = new DropdownComponent();
cooldownDropdown.setText("Attack Cooldown Animation");
cooldownDropdown.setOptions(new ArrayList<>());
Camotoy marked this conversation as resolved.
Show resolved Hide resolved
cooldownDropdown.addOption("Under Crosshair", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.TITLE);
cooldownDropdown.addOption("Action Bar", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.ACTIONBAR);
cooldownDropdown.addOption("Hide Animation", session.getPreferencesCache().getCooldownPreference() == CooldownUtils.CooldownType.DISABLED);
builder.addComponent(cooldownDropdown);
}
}


Expand Down Expand Up @@ -121,13 +134,23 @@ public static boolean handleSettingsForm(GeyserSession session, String response)
}
int offset = 0;

// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
if (!session.isReducedDebugInfo() && session.getConnector().getConfig().isShowCoordinates()) {
if (session.getPreferencesCache().isAllowShowCoordinates() || CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) {
offset++; // Client settings title

session.getWorldCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset));
session.getWorldCache().updateShowCoordinates();
offset++;
// Client can only see its coordinates if reducedDebugInfo is disabled and coordinates are enabled in geyser config.
if (session.getPreferencesCache().isAllowShowCoordinates()) {
session.getPreferencesCache().setPrefersShowCoordinates(settingsResponse.getToggleResponses().get(offset));
session.getPreferencesCache().updateShowCoordinates();
offset++;
}

if (CooldownUtils.getDefaultShowCooldown() != CooldownUtils.CooldownType.DISABLED) {
CooldownUtils.CooldownType cooldownType = CooldownUtils.CooldownType.values()[settingsResponse.getDropdownResponses().get(offset).getElementID()];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use CooldownType.VALUES here. values() is a function that allocates a new array (which we don't need when we have one.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, I wasn't sure if there was a significant difference

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the same apply to the gamemode and difficulty stuff below this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not believe those have a VALUES static field.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the gamerule enum is ours, but the VALUES static field is private. the difficulty enum is mcprotocollibs and doesn't have one.

not sure if this is going out of the scope of this pr

if (cooldownType != null) {
Camotoy marked this conversation as resolved.
Show resolved Hide resolved
session.getPreferencesCache().setCooldownPreference(cooldownType);
}
offset++;
}
}

if (session.getOpPermissionLevel() >= 2 || session.hasPermission("geyser.settings.server")) {
Expand Down