Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 128 additions & 18 deletions feather/protocol/src/packets/server/play.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use anyhow::bail;
use std::io::Cursor;

use anyhow::{anyhow, bail};

use base::{
BlockState, EntityMetadata, Gamemode, ParticleKind, ProfileProperty, ValidBlockPosition,
Expand All @@ -7,7 +9,7 @@ pub use chunk_data::{ChunkData, ChunkDataKind};
use quill_common::components::PreviousGamemode;
pub use update_light::UpdateLight;

use crate::{io::VarLong, Readable, Writeable};
use crate::{io::VarLong, ProtocolVersion, Readable, Writeable};

use super::*;

Expand Down Expand Up @@ -334,8 +336,7 @@ packets! {
}

ChangeGameState {
reason StateReason;
value f32;
state_change GameStateChange;
}

OpenHorseWindow {
Expand All @@ -356,22 +357,131 @@ packets! {
}
}

def_enum! {
StateReason (i8) {
0 = NoRespawnBlock,
1 = EndRaining,
2 = BeginningRain,
3 = ChangeGameMode,
4 = WinGame,
5 = DemoEvent,
6 = ArrowHitPlayer,
7 = RainLevelChange,
8 = ThunderLevelChange,
9 = PufferfishSting,
10 = ElderGuardianAppearance,
11 = EnableRespawnScreen,
#[derive(Debug, Clone)]
pub enum GameStateChange {
/// Sends block.minecraft.spawn.not_valid to client
SendNoRespawnBlockAvailableMessage,
EndRaining,
BeginRaining,
ChangeGamemode {
gamemode: Gamemode,
},
/// Sent when the player enters an end portal from minecraft:the_end to minecraft:overworld
WinGame {
show_credits: bool,
},
/// See https://help.minecraft.net/hc/en-us/articles/4408948974989-Minecraft-Java-Edition-Demo-Mode-
DemoEvent(DemoEventType),
/// Sent when any player is struck by an arrow.
ArrowHitAnyPlayer,
/// Seems to change both skycolor and lightning.
RainLevelChange {
/// Possible values are from 0 to 1
rain_level: f64,
},
/// Seems to change both skycolor and lightning (same as Rain level change, but doesn't start rain).
/// It also requires rain to render by notchian client.
ThunderLevelChange {
/// Possible values are from 0 to 1
thunder_level: f64,
},
PlayPufferfishStingSound,
PlayElderGuardianAppearance,
/// Send when doImmediateRespawn gamerule changes.
EnableRespawnScreen {
enable: bool,
},
}

#[derive(Debug, Clone)]
pub enum DemoEventType {
ShowWelcomeToDemoScreen,
TellMovementControls,
TellJumpControl,
TellInventoryControl,
TellDemoIsOver,
}

impl Writeable for GameStateChange {
fn write(&self, buffer: &mut Vec<u8>, version: ProtocolVersion) -> anyhow::Result<()> {
// Reason
match self {
GameStateChange::SendNoRespawnBlockAvailableMessage => 0u8,
GameStateChange::EndRaining => 1,
GameStateChange::BeginRaining => 2,
GameStateChange::ChangeGamemode { .. } => 3,
GameStateChange::WinGame { .. } => 4,
GameStateChange::DemoEvent(_) => 5,
GameStateChange::ArrowHitAnyPlayer => 6,
GameStateChange::RainLevelChange { .. } => 7,
GameStateChange::ThunderLevelChange { .. } => 8,
GameStateChange::PlayPufferfishStingSound => 9,
GameStateChange::PlayElderGuardianAppearance => 10,
GameStateChange::EnableRespawnScreen { .. } => 11,
}
.write(buffer, version)?;

// Value
match self {
GameStateChange::ChangeGamemode { gamemode } => *gamemode as u8 as f64,
GameStateChange::WinGame { show_credits } => *show_credits as u8 as f64,
GameStateChange::DemoEvent(DemoEventType::ShowWelcomeToDemoScreen) => 0.0,
GameStateChange::DemoEvent(DemoEventType::TellMovementControls) => 101.0,
GameStateChange::DemoEvent(DemoEventType::TellJumpControl) => 102.0,
GameStateChange::DemoEvent(DemoEventType::TellInventoryControl) => 103.0,
GameStateChange::DemoEvent(DemoEventType::TellDemoIsOver) => 104.0,
GameStateChange::RainLevelChange { rain_level } => *rain_level,
GameStateChange::ThunderLevelChange { thunder_level } => *thunder_level,
GameStateChange::EnableRespawnScreen { enable } => !enable as u8 as f64,
_ => 0.0,
}
.write(buffer, version)?;

Ok(())
}
}

impl Readable for GameStateChange {
fn read(buffer: &mut Cursor<&[u8]>, version: ProtocolVersion) -> anyhow::Result<Self>
where
Self: Sized,
{
let reason = u8::read(buffer, version)?;
let value = f64::read(buffer, version)?;
Ok(match reason {
0 => GameStateChange::SendNoRespawnBlockAvailableMessage,
1 => GameStateChange::EndRaining,
2 => GameStateChange::BeginRaining,
3 => GameStateChange::ChangeGamemode {
gamemode: Gamemode::from_id(value as u8)
.ok_or(anyhow!("Unsupported gamemode ID"))?,
},
4 => GameStateChange::WinGame {
show_credits: value as u8 != 0,
},
5 => GameStateChange::DemoEvent(match value as u8 {
0 => DemoEventType::ShowWelcomeToDemoScreen,
101 => DemoEventType::TellMovementControls,
102 => DemoEventType::TellJumpControl,
103 => DemoEventType::TellInventoryControl,
104 => DemoEventType::TellDemoIsOver,
other => bail!("Invalid demo event type: {}", other),
}),
6 => GameStateChange::ArrowHitAnyPlayer,
7 => GameStateChange::RainLevelChange { rain_level: value },
8 => GameStateChange::ThunderLevelChange {
thunder_level: value,
},
9 => GameStateChange::PlayPufferfishStingSound,
10 => GameStateChange::PlayElderGuardianAppearance,
11 => GameStateChange::EnableRespawnScreen {
enable: value as u8 == 0,
},
other => bail!("Invalid game state change reason: {}", other),
})
}
}

packets! {
JoinGame {
entity_id i32;
Expand Down
15 changes: 13 additions & 2 deletions feather/server/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::{

use ahash::AHashSet;
use flume::{Receiver, Sender};
use slab::Slab;
use uuid::Uuid;

use base::{
Expand All @@ -20,7 +21,8 @@ use common::{
use libcraft_items::InventorySlot;
use packets::server::{Particle, SetSlot, SpawnLivingEntity, UpdateLight, WindowConfirmation};
use protocol::packets::server::{
EntityPosition, EntityPositionAndRotation, EntityTeleport, HeldItemChange, PlayerAbilities,
ChangeGameState, EntityPosition, EntityPositionAndRotation, EntityTeleport, GameStateChange,
HeldItemChange, PlayerAbilities,
};
use protocol::{
packets::{
Expand All @@ -42,7 +44,6 @@ use crate::{
network_id_registry::NetworkId,
Options,
};
use slab::Slab;

/// Max number of chunks to send to a client per tick.
const MAX_CHUNKS_PER_TICK: usize = 10;
Expand Down Expand Up @@ -331,6 +332,10 @@ impl Client {
self.send_packet(PlayerInfo::RemovePlayers(vec![uuid]));
}

pub fn change_player_tablist_gamemode(&self, uuid: Uuid, gamemode: Gamemode) {
self.send_packet(PlayerInfo::UpdateGamemodes(vec![(uuid, gamemode)]));
}

pub fn unload_entity(&self, id: NetworkId) {
log::trace!("Unloading {:?} on {}", id, self.username);
self.sent_entities.borrow_mut().remove(&id);
Expand Down Expand Up @@ -602,6 +607,12 @@ impl Client {
self.send_packet(HeldItemChange { slot });
}

pub fn change_gamemode(&self, gamemode: Gamemode) {
self.send_packet(ChangeGameState {
state_change: GameStateChange::ChangeGamemode { gamemode },
})
}

fn register_entity(&self, network_id: NetworkId) {
self.sent_entities.borrow_mut().insert(network_id);
}
Expand Down
2 changes: 2 additions & 0 deletions feather/server/src/systems.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
mod block;
mod chat;
mod entity;
mod gamemode;
mod particle;
mod player_join;
mod player_leave;
Expand Down Expand Up @@ -36,6 +37,7 @@ pub fn register(server: Server, game: &mut Game, systems: &mut SystemExecutor<Ga
chat::register(game, systems);
particle::register(systems);
plugin_message::register(systems);
gamemode::register(systems);

systems.group::<Server>().add_system(tick_clients);
}
Expand Down
Loading