Abstract scoreboard display position (#286)

## Description

The scoreboard display packet has a position field that can contain one
of the following:
- 0: List
- 1: Sidebar
- 2: BelowName
- 3..=18: Team specific sidebar indexed as 3 + team color (according to
wiki.vg)

I'm not really sure how team specific sidebars are supposed to work, so
left that as a byte, maybe that can have some more abstraction?

## Test Plan

<details>

<summary>Playground</summary>

```rust
use valence::client::despawn_disconnected_clients;
use valence::client::event::default_event_handler;
use valence::prelude::*;
use valence::protocol::packet::s2c::play::scoreboard_display::ScoreboardPosition;
use valence::protocol::packet::s2c::play::scoreboard_objective_update::{Mode, RenderType};
use valence::protocol::packet::s2c::play::scoreboard_player_update::Action;
use valence::protocol::packet::s2c::play::{
    ScoreboardDisplayS2c, ScoreboardObjectiveUpdateS2c, ScoreboardPlayerUpdateS2c,
};
use valence::protocol::var_int::VarInt;

const SPAWN_Y: i32 = 64;

pub fn build_app(app: &mut App) {
    app.add_plugin(ServerPlugin::new(()))
        .add_system(default_event_handler.in_schedule(EventLoopSchedule))
        .add_startup_system(setup)
        .add_system(init_clients)
        .add_system(scoreboard)
        .add_system(despawn_disconnected_clients)
        .add_systems(PlayerList::default_systems());
}

fn setup(mut commands: Commands, server: Res<Server>) {
    let mut instance = server.new_instance(DimensionId::default());

    for z in -5..5 {
        for x in -5..5 {
            instance.insert_chunk([x, z], Chunk::default());
        }
    }

    for z in -25..25 {
        for x in -25..25 {
            instance.set_block([x, SPAWN_Y, z], BlockState::GRASS_BLOCK);
        }
    }

    commands.spawn(instance);
}

fn init_clients(
    mut clients: Query<&mut Client, Added<Client>>,
    instances: Query<Entity, With<Instance>>,
) {
    for mut client in &mut clients {
        client.set_position([0.0, SPAWN_Y as f64 + 1.0, 0.0]);
        client.set_instance(instances.single());
        client.set_game_mode(GameMode::Creative);
    }
}

// Add new systems here!
fn scoreboard(mut clients: Query<&mut Client, Added<Client>>) {
    for mut client in &mut clients {
        client.write_packet(&ScoreboardObjectiveUpdateS2c {
            objective_name: "sidebar",
            mode: Mode::Create {
                objective_display_name: "Sidebar".into_text(),
                render_type: RenderType::Integer,
            },
        });
        client.write_packet(&ScoreboardPlayerUpdateS2c {
            action: Action::Update {
                objective_score: VarInt(42),
                objective_name: "sidebar",
            },
            entity_name: "name",
        });
        client.write_packet(&ScoreboardDisplayS2c {
            position: ScoreboardPosition::Sidebar, // Position is now an Enum
            score_name: "sidebar",
        });
    }
}
```

</details>

---------

Co-authored-by: Ryan Johnson <ryanj00a@gmail.com>
This commit is contained in:
AviiNL 2023-03-12 12:51:49 +01:00 committed by GitHub
parent 41907261e3
commit 68cf6e83a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -1,7 +1,74 @@
use crate::{Decode, Encode};
use super::team::TeamColor;
#[derive(Copy, Clone, Debug, Encode, Decode)]
pub struct ScoreboardDisplayS2c<'a> {
pub position: u8,
pub position: ScoreboardPosition,
pub score_name: &'a str,
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub enum ScoreboardPosition {
List,
Sidebar,
BelowName,
SidebarTeam(TeamColor),
}
impl Encode for ScoreboardPosition {
fn encode(&self, w: impl std::io::Write) -> anyhow::Result<()> {
match self {
ScoreboardPosition::List => 0u8.encode(w),
ScoreboardPosition::Sidebar => 1u8.encode(w),
ScoreboardPosition::BelowName => 2u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Black) => 3u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue) => 4u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen) => 5u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan) => 6u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::DarkRed) => 7u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Purple) => 8u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Gold) => 9u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Gray) => 10u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::DarkGray) => 11u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Blue) => 12u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen) => 13u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Cyan) => 14u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Red) => 15u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Pink) => 16u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::Yellow) => 17u8.encode(w),
ScoreboardPosition::SidebarTeam(TeamColor::White) => 18u8.encode(w),
ScoreboardPosition::SidebarTeam(_) => {
Err(anyhow::anyhow!("Invalid scoreboard display position"))
}
}
}
}
impl<'a> Decode<'a> for ScoreboardPosition {
fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
let value = u8::decode(r)?;
match value {
0 => Ok(ScoreboardPosition::List),
1 => Ok(ScoreboardPosition::Sidebar),
2 => Ok(ScoreboardPosition::BelowName),
3 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Black)),
4 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkBlue)),
5 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGreen)),
6 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkCyan)),
7 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkRed)),
8 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Purple)),
9 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gold)),
10 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Gray)),
11 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::DarkGray)),
12 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Blue)),
13 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::BrightGreen)),
14 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Cyan)),
15 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Red)),
16 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Pink)),
17 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::Yellow)),
18 => Ok(ScoreboardPosition::SidebarTeam(TeamColor::White)),
_ => Err(anyhow::anyhow!("Invalid scoreboard display position")),
}
}
}