Merge remote-tracking branch 'upstream/master' into feature/blocky

This commit is contained in:
Joshua Castle 2023-02-03 22:01:32 -08:00
commit 9e74e904e7
No known key found for this signature in database
GPG key ID: F674F38216C35D5D
5 changed files with 63 additions and 59 deletions

View file

@ -195,6 +195,9 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
geyserConfig.loadFloodgate(this); geyserConfig.loadFloodgate(this);
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
this.geyserCommandManager.init();
if (!INITIALIZED) { if (!INITIALIZED) {
// Needs to be an anonymous inner class otherwise Bukkit complains about missing classes // Needs to be an anonymous inner class otherwise Bukkit complains about missing classes
Bukkit.getPluginManager().registerEvents(new Listener() { Bukkit.getPluginManager().registerEvents(new Listener() {
@ -206,9 +209,6 @@ public class GeyserSpigotPlugin extends JavaPlugin implements GeyserBootstrap {
} }
}, this); }, this);
this.geyserCommandManager = new GeyserSpigotCommandManager(geyser);
this.geyserCommandManager.init();
// Because Bukkit locks its command map upon startup, we need to // Because Bukkit locks its command map upon startup, we need to
// add our plugin commands in onEnable, but populating the executor // add our plugin commands in onEnable, but populating the executor
// can happen at any time // can happen at any time

View file

@ -100,6 +100,7 @@ public class PlayerEntity extends LivingEntity {
super(session, entityId, geyserId, uuid, EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw); super(session, entityId, geyserId, uuid, EntityDefinitions.PLAYER, position, motion, yaw, pitch, headYaw);
this.username = username; this.username = username;
this.nametag = username;
this.texturesProperty = texturesProperty; this.texturesProperty = texturesProperty;
} }
@ -119,7 +120,7 @@ public class PlayerEntity extends LivingEntity {
} }
// The name can't be updated later (the entity metadata for it is ignored), so we need to check for this now // The name can't be updated later (the entity metadata for it is ignored), so we need to check for this now
updateDisplayName(null, false); updateDisplayName(session.getWorldCache().getScoreboard().getTeamFor(username));
AddPlayerPacket addPlayerPacket = new AddPlayerPacket(); AddPlayerPacket addPlayerPacket = new AddPlayerPacket();
addPlayerPacket.setUuid(uuid); addPlayerPacket.setUuid(uuid);
@ -315,19 +316,10 @@ public class PlayerEntity extends LivingEntity {
} }
//todo this will become common entity logic once UUID support is implemented for them //todo this will become common entity logic once UUID support is implemented for them
/** public void updateDisplayName(@Nullable Team team) {
* @param useGivenTeam even if there is no team, update the username in the entity metadata anyway, and don't look for a team
*/
public void updateDisplayName(@Nullable Team team, boolean useGivenTeam) {
if (team == null && !useGivenTeam) {
// Only search for the team if we are not supposed to use the given team
// If the given team is null, this is intentional that we are being removed from the team
team = session.getWorldCache().getScoreboard().getTeamFor(username);
}
boolean needsUpdate; boolean needsUpdate;
String newDisplayName = this.username;
if (team != null) { if (team != null) {
String newDisplayName;
if (team.isVisibleFor(session.getPlayerEntity().getUsername())) { if (team.isVisibleFor(session.getPlayerEntity().getUsername())) {
TeamColor color = team.getColor(); TeamColor color = team.getColor();
String chatColor = MessageTranslator.toChatColor(color); String chatColor = MessageTranslator.toChatColor(color);
@ -339,23 +331,16 @@ public class PlayerEntity extends LivingEntity {
// The name is not visible to the session player; clear name // The name is not visible to the session player; clear name
newDisplayName = ""; newDisplayName = "";
} }
needsUpdate = useGivenTeam && !newDisplayName.equals(nametag); needsUpdate = !newDisplayName.equals(this.nametag);
nametag = newDisplayName; this.nametag = newDisplayName;
dirtyMetadata.put(EntityData.NAMETAG, newDisplayName);
} else if (useGivenTeam) {
// The name has reset, if it was previously something else
needsUpdate = !newDisplayName.equals(nametag);
dirtyMetadata.put(EntityData.NAMETAG, this.username);
} else { } else {
needsUpdate = false; // The name has reset, if it was previously something else
needsUpdate = !this.nametag.equals(this.username);
this.nametag = this.username;
} }
if (needsUpdate) { if (needsUpdate) {
// Update the metadata as it won't be updated later dirtyMetadata.put(EntityData.NAMETAG, this.nametag);
SetEntityDataPacket packet = new SetEntityDataPacket();
packet.getMetadata().put(EntityData.NAMETAG, newDisplayName);
packet.setRuntimeEntityId(geyserId);
session.sendUpstreamPacket(packet);
} }
} }

View file

@ -30,6 +30,7 @@ import com.nukkitx.protocol.bedrock.data.ScoreInfo;
import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket; import com.nukkitx.protocol.bedrock.packet.RemoveObjectivePacket;
import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket; import com.nukkitx.protocol.bedrock.packet.SetDisplayObjectivePacket;
import com.nukkitx.protocol.bedrock.packet.SetScorePacket; import com.nukkitx.protocol.bedrock.packet.SetScorePacket;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import lombok.Getter; import lombok.Getter;
import org.geysermc.geyser.GeyserImpl; import org.geysermc.geyser.GeyserImpl;
import org.geysermc.geyser.GeyserLogger; import org.geysermc.geyser.GeyserLogger;
@ -56,6 +57,13 @@ public final class Scoreboard {
@Getter @Getter
private final Map<ScoreboardPosition, Objective> objectiveSlots = new EnumMap<>(ScoreboardPosition.class); private final Map<ScoreboardPosition, Objective> objectiveSlots = new EnumMap<>(ScoreboardPosition.class);
private final Map<String, Team> teams = new ConcurrentHashMap<>(); // updated on multiple threads private final Map<String, Team> teams = new ConcurrentHashMap<>(); // updated on multiple threads
/**
* Required to preserve vanilla behavior, which also uses a map.
* Otherwise, for example, if TAB has a team for a player and vanilla has a team, "race conditions" that do not
* match vanilla could occur.
*/
@Getter
private final Map<String, Team> playerToTeam = new Object2ObjectOpenHashMap<>();
private int lastAddScoreCount = 0; private int lastAddScoreCount = 0;
private int lastRemoveScoreCount = 0; private int lastRemoveScoreCount = 0;
@ -333,12 +341,7 @@ public final class Scoreboard {
} }
public Team getTeamFor(String entity) { public Team getTeamFor(String entity) {
for (Team team : teams.values()) { return playerToTeam.get(entity);
if (team.hasEntity(entity)) {
return team;
}
}
return null;
} }
public void removeTeam(String teamName) { public void removeTeam(String teamName) {
@ -348,6 +351,9 @@ public final class Scoreboard {
// We need to use the direct entities list here, so #refreshSessionPlayerDisplays also updates accordingly // We need to use the direct entities list here, so #refreshSessionPlayerDisplays also updates accordingly
// With the player's lack of a team in visibility checks // With the player's lack of a team in visibility checks
updateEntityNames(remove, remove.getEntities(), true); updateEntityNames(remove, remove.getEntities(), true);
for (String name : remove.getEntities()) {
playerToTeam.remove(name, remove);
}
session.removeCommandEnum("Geyser_Teams", remove.getId()); session.removeCommandEnum("Geyser_Teams", remove.getId());
} }
@ -380,7 +386,8 @@ public final class Scoreboard {
for (Entity entity : session.getEntityCache().getEntities().values()) { for (Entity entity : session.getEntityCache().getEntities().values()) {
// This more complex logic is for the future to iterate over all entities, not just players // This more complex logic is for the future to iterate over all entities, not just players
if (entity instanceof PlayerEntity player && names.remove(player.getUsername())) { if (entity instanceof PlayerEntity player && names.remove(player.getUsername())) {
player.updateDisplayName(team, true); player.updateDisplayName(team);
player.updateBedrockMetadata();
if (names.isEmpty()) { if (names.isEmpty()) {
break; break;
} }
@ -396,7 +403,8 @@ public final class Scoreboard {
for (Entity entity : session.getEntityCache().getEntities().values()) { for (Entity entity : session.getEntityCache().getEntities().values()) {
if (entity instanceof PlayerEntity player) { if (entity instanceof PlayerEntity player) {
Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername()); Team playerTeam = session.getWorldCache().getScoreboard().getTeamFor(player.getUsername());
player.updateDisplayName(playerTeam, true); player.updateDisplayName(playerTeam);
player.updateBedrockMetadata();
} }
} }
} }

View file

@ -65,6 +65,7 @@ public final class Team {
if (entities.add(name)) { if (entities.add(name)) {
added.add(name); added.add(name);
} }
scoreboard.getPlayerToTeam().put(name, this);
} }
if (added.isEmpty()) { if (added.isEmpty()) {
@ -93,6 +94,7 @@ public final class Team {
if (entities.remove(name)) { if (entities.remove(name)) {
removed.add(name); removed.add(name);
} }
scoreboard.getPlayerToTeam().remove(name, this);
} }
return removed; return removed;
} }

View file

@ -92,11 +92,29 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
if (isValidMove(session, entity.getPosition(), packet.getPosition())) { if (isValidMove(session, entity.getPosition(), packet.getPosition())) {
Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT); Vector3d position = session.getCollisionManager().adjustBedrockPosition(packet.getPosition(), packet.isOnGround(), packet.getMode() == MovePlayerPacket.Mode.TELEPORT);
if (position != null) { // A null return value cancels the packet if (position != null) { // A null return value cancels the packet
boolean onGround = packet.isOnGround();
boolean teleportThroughVoidFloor;
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
if (entity.getPosition().getY() >= packet.getPosition().getY()) {
int floorY = position.getFloorY();
// The void floor is offset about 40 blocks below the bottom of the world
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension();
int voidFloorLocation = bedrockDimension.minY() - 40;
teleportThroughVoidFloor = floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation;
if (teleportThroughVoidFloor) {
// https://github.com/GeyserMC/Geyser/issues/3521 - no void floor in Java so we cannot be on the ground.
onGround = false;
}
} else {
teleportThroughVoidFloor = false;
}
Packet movePacket; Packet movePacket;
if (rotationChanged) { if (rotationChanged) {
// Send rotation updates as well // Send rotation updates as well
movePacket = new ServerboundMovePlayerPosRotPacket( movePacket = new ServerboundMovePlayerPosRotPacket(
packet.isOnGround(), onGround,
position.getX(), position.getY(), position.getZ(), position.getX(), position.getY(), position.getZ(),
yaw, pitch yaw, pitch
); );
@ -105,35 +123,26 @@ public class BedrockMovePlayerTranslator extends PacketTranslator<MovePlayerPack
entity.setHeadYaw(headYaw); entity.setHeadYaw(headYaw);
} else { } else {
// Rotation did not change; don't send an update with rotation // Rotation did not change; don't send an update with rotation
movePacket = new ServerboundMovePlayerPosPacket(packet.isOnGround(), position.getX(), position.getY(), position.getZ()); movePacket = new ServerboundMovePlayerPosPacket(onGround, position.getX(), position.getY(), position.getZ());
} }
// Compare positions here for void floor fix below before the player's position variable is set to the packet position
boolean notMovingUp = entity.getPosition().getY() >= packet.getPosition().getY();
entity.setPositionManual(packet.getPosition()); entity.setPositionManual(packet.getPosition());
entity.setOnGround(packet.isOnGround()); entity.setOnGround(onGround);
// Send final movement changes // Send final movement changes
session.sendDownstreamPacket(movePacket); session.sendDownstreamPacket(movePacket);
if (notMovingUp) { if (teleportThroughVoidFloor) {
int floorY = position.getFloorY(); // Work around there being a floor at the bottom of the world and teleport the player below it
// The void floor is offset about 40 blocks below the bottom of the world // Moving from below to above the void floor works fine
BedrockDimension bedrockDimension = session.getChunkCache().getBedrockDimension(); entity.setPosition(entity.getPosition().sub(0, 4f, 0));
int voidFloorLocation = bedrockDimension.minY() - 40; MovePlayerPacket movePlayerPacket = new MovePlayerPacket();
if (floorY <= (voidFloorLocation + 2) && floorY >= voidFloorLocation) { movePlayerPacket.setRuntimeEntityId(entity.getGeyserId());
// Work around there being a floor at the bottom of the world and teleport the player below it movePlayerPacket.setPosition(entity.getPosition());
// Moving from below to above the void floor works fine movePlayerPacket.setRotation(entity.getBedrockRotation());
entity.setPosition(entity.getPosition().sub(0, 4f, 0)); movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
MovePlayerPacket movePlayerPacket = new MovePlayerPacket(); movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
movePlayerPacket.setRuntimeEntityId(entity.getGeyserId()); session.sendUpstreamPacket(movePlayerPacket);
movePlayerPacket.setPosition(entity.getPosition());
movePlayerPacket.setRotation(entity.getBedrockRotation());
movePlayerPacket.setMode(MovePlayerPacket.Mode.TELEPORT);
movePlayerPacket.setTeleportationCause(MovePlayerPacket.TeleportationCause.BEHAVIOR);
session.sendUpstreamPacket(movePlayerPacket);
}
} }
session.getSkullCache().updateVisibleSkulls(); session.getSkullCache().updateVisibleSkulls();