mirror of
https://github.com/GeyserMC/Geyser.git
synced 2024-08-14 23:57:35 +00:00
Merge branch 'dev' into feature/1.21
This commit is contained in:
commit
e2a61d4365
22 changed files with 333 additions and 99 deletions
|
@ -770,6 +770,7 @@ public class GeyserImpl implements GeyserApi {
|
|||
return 0;
|
||||
}
|
||||
|
||||
//noinspection DataFlowIssue
|
||||
return Integer.parseInt(BUILD_NUMBER);
|
||||
}
|
||||
|
||||
|
|
|
@ -31,9 +31,11 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.AttributeData;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityDataTypes;
|
||||
import org.cloudburstmc.protocol.bedrock.data.entity.EntityFlag;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.UpdateAttributesPacket;
|
||||
import org.geysermc.geyser.entity.attribute.GeyserAttributeType;
|
||||
import org.geysermc.geyser.item.Items;
|
||||
import org.geysermc.geyser.network.GameProtocol;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.util.AttributeUtils;
|
||||
import org.geysermc.geyser.util.DimensionUtils;
|
||||
|
@ -65,6 +67,8 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
@Getter
|
||||
private boolean isRidingInFront;
|
||||
|
||||
private int lastAirSupply = getMaxAir();
|
||||
|
||||
public SessionPlayerEntity(GeyserSession session) {
|
||||
super(session, -1, 1, null, Vector3f.ZERO, Vector3f.ZERO, 0, 0, 0, null, null);
|
||||
|
||||
|
@ -159,7 +163,13 @@ public class SessionPlayerEntity extends PlayerEntity {
|
|||
|
||||
@Override
|
||||
protected void setAirSupply(int amount) {
|
||||
if (amount == getMaxAir()) {
|
||||
// Seemingly required to be sent as of Bedrock 1.21. Otherwise, bubbles will appear as empty
|
||||
// Also, this changes how the air bubble graphics/sounds are presented. Breathing on means sound effects and
|
||||
// the bubbles visually pop
|
||||
setFlag(EntityFlag.BREATHING, amount >= this.lastAirSupply);
|
||||
this.lastAirSupply = amount;
|
||||
|
||||
if (amount == getMaxAir() && GameProtocol.isPre1_21_0(session)) {
|
||||
super.setAirSupply(0); // Hide the bubble counter from the UI for the player
|
||||
} else {
|
||||
super.setAirSupply(amount);
|
||||
|
|
|
@ -32,24 +32,50 @@ import org.cloudburstmc.math.vector.Vector2f;
|
|||
import org.cloudburstmc.math.vector.Vector3f;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeAction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.CameraShakeType;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudElement;
|
||||
import org.cloudburstmc.protocol.bedrock.data.HudVisibility;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraEase;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraFadeInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.data.camera.CameraSetInstruction;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraInstructionPacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CameraShakePacket;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.PlayerFogPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.*;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.SetHudPacket;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraData;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraEaseType;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraFade;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPerspective;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraPosition;
|
||||
import org.geysermc.geyser.api.bedrock.camera.CameraShake;
|
||||
import org.geysermc.geyser.api.bedrock.camera.GuiElement;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeyserCameraData implements CameraData {
|
||||
private static final HudElement[] HUD_ELEMENT_VALUES = HudElement.values();
|
||||
private static final Set<HudElement> ALL_HUD_ELEMENTS = Set.of(HUD_ELEMENT_VALUES);
|
||||
|
||||
/**
|
||||
* An array of elements to hide when the player is in spectator mode.
|
||||
* Helps with tidying up the GUI; Java-style.
|
||||
*/
|
||||
private static final GuiElement[] SPECTATOR_HIDDEN_ELEMENTS = {
|
||||
GuiElement.AIR_BUBBLES_BAR,
|
||||
GuiElement.ARMOR,
|
||||
GuiElement.HEALTH,
|
||||
GuiElement.FOOD_BAR,
|
||||
GuiElement.PROGRESS_BAR,
|
||||
GuiElement.TOOL_TIPS
|
||||
};
|
||||
|
||||
private final GeyserSession session;
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
/**
|
||||
* All fog effects that are currently applied to the client.
|
||||
*/
|
||||
|
@ -57,6 +83,14 @@ public class GeyserCameraData implements CameraData {
|
|||
|
||||
private final Set<UUID> cameraLockOwners = new HashSet<>();
|
||||
|
||||
/**
|
||||
* All currently hidden HUD elements
|
||||
*/
|
||||
private final Set<GuiElement> hiddenHudElements = new HashSet<>();
|
||||
|
||||
@Getter
|
||||
private CameraPerspective cameraPerspective;
|
||||
|
||||
public GeyserCameraData(GeyserSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
@ -223,4 +257,67 @@ public class GeyserCameraData implements CameraData {
|
|||
public boolean isCameraLocked() {
|
||||
return !this.cameraLockOwners.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideElement(GuiElement... elements) {
|
||||
Objects.requireNonNull(elements);
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.HIDE);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.add(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetElement(GuiElement... elements) {
|
||||
SetHudPacket packet = new SetHudPacket();
|
||||
packet.setVisibility(HudVisibility.RESET);
|
||||
Set<HudElement> elementSet = packet.getElements();
|
||||
|
||||
if (elements != null && elements.length != 0) {
|
||||
for (GuiElement element : elements) {
|
||||
this.hiddenHudElements.remove(element);
|
||||
elementSet.add(HUD_ELEMENT_VALUES[element.id()]);
|
||||
}
|
||||
} else {
|
||||
this.hiddenHudElements.clear();
|
||||
elementSet.addAll(ALL_HUD_ELEMENTS);
|
||||
}
|
||||
|
||||
session.sendUpstreamPacket(packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHudElementHidden(@NonNull GuiElement element) {
|
||||
Objects.requireNonNull(element);
|
||||
return this.hiddenHudElements.contains(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<GuiElement> hiddenElements() {
|
||||
return Collections.unmodifiableSet(hiddenHudElements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals with hiding hud elements while in spectator.
|
||||
*
|
||||
* @param currentlySpectator whether the player is currently in spectator mode
|
||||
* @param newGameMode the new GameMode to switch to
|
||||
*/
|
||||
public void handleGameModeChange(boolean currentlySpectator, GameMode newGameMode) {
|
||||
if (newGameMode == GameMode.SPECTATOR) {
|
||||
if (!currentlySpectator) {
|
||||
hideElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
} else {
|
||||
if (currentlySpectator) {
|
||||
resetElement(SPECTATOR_HIDDEN_ELEMENTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,6 +132,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler {
|
|||
}
|
||||
|
||||
session.getUpstream().getSession().setCodec(packetCodec);
|
||||
// FIXME temporary until 1.20.80 is dropped
|
||||
session.getPlayerEntity().resetAir();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,6 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
*/
|
||||
private volatile boolean closed;
|
||||
|
||||
@Setter
|
||||
private GameMode gameMode = GameMode.SURVIVAL;
|
||||
|
||||
/**
|
||||
|
@ -1302,6 +1301,12 @@ public class GeyserSession implements GeyserConnection, GeyserCommandSource {
|
|||
}
|
||||
}
|
||||
|
||||
public void setGameMode(GameMode newGamemode) {
|
||||
boolean currentlySpectator = this.gameMode == GameMode.SPECTATOR;
|
||||
this.gameMode = newGamemode;
|
||||
this.cameraData.handleGameModeChange(currentlySpectator, newGamemode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to reduce amount of duplicate code. Sends ServerboundUseItemPacket.
|
||||
*/
|
||||
|
|
|
@ -148,9 +148,9 @@ public class GeyserLocale {
|
|||
} catch (IOException ignored) {}
|
||||
}
|
||||
} else {
|
||||
if (GeyserImpl.getInstance() != null && !validLocalLanguage) {
|
||||
if (!validLocalLanguage) {
|
||||
// Don't warn on missing locales if a local file has been found
|
||||
GeyserImpl.getInstance().getLogger().warning("Missing locale: " + locale);
|
||||
bootstrap.getGeyserLogger().warning("Missing locale: " + locale);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,12 +162,7 @@ public class GeyserLocale {
|
|||
localeProp.load(stream);
|
||||
} catch (IOException e) {
|
||||
String message = "Unable to load custom language override!";
|
||||
if (GeyserImpl.getInstance() != null) {
|
||||
GeyserImpl.getInstance().getLogger().error(message, e);
|
||||
} else {
|
||||
System.err.println(message);
|
||||
e.printStackTrace();
|
||||
}
|
||||
bootstrap.getGeyserLogger().error(message, e);
|
||||
}
|
||||
|
||||
LOCALE_MAPPINGS.putIfAbsent(locale, localeProp);
|
||||
|
|
|
@ -26,8 +26,8 @@
|
|||
package org.geysermc.geyser.translator.protocol.bedrock;
|
||||
|
||||
import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.util.PlatformType;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.geysermc.geyser.translator.protocol.Translator;
|
||||
|
@ -39,15 +39,17 @@ public class BedrockCommandRequestTranslator extends PacketTranslator<CommandReq
|
|||
@Override
|
||||
public void translate(GeyserSession session, CommandRequestPacket packet) {
|
||||
String command = MessageTranslator.convertToPlainText(packet.getCommand());
|
||||
handleCommand(session, MessageTranslator.normalizeSpace(command).substring(1));
|
||||
}
|
||||
|
||||
static void handleCommand(GeyserSession session, String command) {
|
||||
if (!(session.getGeyser().getPlatformType() == PlatformType.STANDALONE
|
||||
&& GeyserImpl.getInstance().commandManager().runCommand(session, command.substring(1)))) {
|
||||
&& GeyserImpl.getInstance().commandManager().runCommand(session, command))) {
|
||||
if (MessageTranslator.isTooLong(command, session)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// running commands via Bedrock's command select menu adds a trailing whitespace which Java doesn't like
|
||||
// https://github.com/GeyserMC/Geyser/issues/3877
|
||||
session.sendCommand(command.substring(1).stripTrailing());
|
||||
session.sendCommand(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,13 +36,22 @@ public class BedrockTextTranslator extends PacketTranslator<TextPacket> {
|
|||
|
||||
@Override
|
||||
public void translate(GeyserSession session, TextPacket packet) {
|
||||
String message = MessageTranslator.convertToPlainText(packet.getMessage());
|
||||
// Java trims all messages, and then checks for the leading slash
|
||||
String message = MessageTranslator.convertToPlainText(
|
||||
MessageTranslator.normalizeSpace(packet.getMessage())
|
||||
);
|
||||
|
||||
if (message.isBlank()) {
|
||||
// Java Edition (as of 1.17.1) just doesn't pass on these messages, so... we won't either!
|
||||
return;
|
||||
}
|
||||
|
||||
if (message.startsWith("/")) {
|
||||
// Yes, Java actually allows whitespaces before commands and will still see those as valid
|
||||
BedrockCommandRequestTranslator.handleCommand(session, message.substring(1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MessageTranslator.isTooLong(message, session)) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -391,6 +391,39 @@ public class MessageTranslator {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes whitespaces - a thing a vanilla client apparently does with commands and chat messages.
|
||||
*/
|
||||
public static String normalizeSpace(String string) {
|
||||
if (string == null || string.isEmpty()) {
|
||||
return string;
|
||||
}
|
||||
final int size = string.length();
|
||||
final char[] newChars = new char[size];
|
||||
int count = 0;
|
||||
int whitespacesCount = 0;
|
||||
boolean startWhitespaces = true;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final char actualChar = string.charAt(i);
|
||||
final boolean isWhitespace = Character.isWhitespace(actualChar);
|
||||
if (isWhitespace) {
|
||||
if (whitespacesCount == 0 && !startWhitespaces) {
|
||||
newChars[count++] = ' ';
|
||||
}
|
||||
whitespacesCount++;
|
||||
} else {
|
||||
startWhitespaces = false;
|
||||
// Replace non-breaking spaces with regular spaces for normalization
|
||||
newChars[count++] = (actualChar == '\u00A0' ? ' ' : actualChar);
|
||||
whitespacesCount = 0;
|
||||
}
|
||||
}
|
||||
if (startWhitespaces) {
|
||||
return "";
|
||||
}
|
||||
return new String(newChars, 0, count - (whitespacesCount > 0 ? 1 : 0)).trim();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
// no-op
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue