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

Velocity Support #139

Open
wants to merge 21 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
6 changes: 5 additions & 1 deletion .etc/example-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,8 @@ cache_ttl = 60
# How big the cache can be in kb.
cache_capacity = 20_000

whitelist = false
# Velocity configuration
[velocity]
enabled = false
# The key from forwarding.secret
secret = ""
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,18 @@ thiserror = "2.0.3"
rand = "0.9.0-beta.0"
fnv = "1.0.7"
wyhash = "0.5.0"
sha2 = "=0.10.8"
hmac = "0.12.1"

# Encoding/Serialization
toml = "0.8.19"
serde = { version = "1.0.210", features = ["derive"] }
serde_json = "1.0.128"
serde_derive = "1.0.210"
base64 = "0.22.1"
bitcode = "0.6.3"
bitcode_derive = "0.6.3"
toml = "0.8.19"
#bitmask-enum = "2.2.5"

# Bit manipulation
byteorder = "1.5.0"
Expand Down
5 changes: 4 additions & 1 deletion src/bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,20 @@ ferrumc-macros = { workspace = true }
ferrumc-nbt = { workspace = true }
ferrumc-general-purpose = { workspace = true }
ferrumc-state = { workspace = true }
ferrumc-text = { workspace = true }

parking_lot = { workspace = true, features = ["deadlock_detection"] }
tracing = { workspace = true }
tokio = { workspace = true }
futures = { workspace = true }
async-trait = { workspace = true }
clap = { workspace = true, features = ["derive"] }
hmac = { workspace = true }
sha2 = { workspace = true }
rand = { workspace = true }
flate2 = { workspace = true }
ctor = { workspace = true }


[[bin]]
name = "ferrumc"
path = "src/main.rs"
5 changes: 3 additions & 2 deletions src/bin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![forbid(unsafe_code)]
extern crate core;

use crate::cli::{CLIArgs, Command, ImportArgs};
use crate::errors::BinaryError;
use clap::Parser;
use ferrumc_config::statics::get_global_config;
Expand All @@ -18,9 +19,8 @@ use std::sync::Arc;
use systems::definition;
use tracing::{error, info, trace};

pub(crate) mod errors;
use crate::cli::{CLIArgs, Command, ImportArgs};
mod cli;
pub(crate) mod errors;
mod packet_handlers;
mod systems;

Expand Down Expand Up @@ -125,6 +125,7 @@ async fn create_state() -> Result<ServerState> {
world: World::new().await,
})
}

fn check_deadlocks() {
{
use parking_lot::deadlock;
Expand Down
113 changes: 49 additions & 64 deletions src/bin/src/packet_handlers/login_process.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use ferrumc_config::statics::{get_global_config, get_whitelist};
use ferrumc_core::chunks::chunk_receiver::ChunkReceiver;
use ferrumc_core::identity::player_identity::PlayerIdentity;
use ferrumc_core::transform::grounded::OnGround;
use ferrumc_core::transform::position::Position;
use ferrumc_core::transform::rotation::Rotation;
use ferrumc_ecs::components::storage::ComponentRefMut;
use ferrumc_ecs::entities::Entity;
use ferrumc_events::errors::EventsError;
use ferrumc_events::infrastructure::Event;
use ferrumc_macros::event_handler;
use ferrumc_net::connection::{ConnectionState, StreamWriter};
use ferrumc_net::connection::{ConnectionState, PlayerStartLoginEvent, StreamWriter};
use ferrumc_net::errors::NetError;
use ferrumc_net::packets::incoming::ack_finish_configuration::AckFinishConfigurationEvent;
use ferrumc_net::packets::incoming::keep_alive::IncomingKeepAlivePacket;
Expand All @@ -18,9 +19,7 @@ use ferrumc_net::packets::outgoing::client_bound_known_packs::ClientBoundKnownPa
use ferrumc_net::packets::outgoing::finish_configuration::FinishConfigurationPacket;
use ferrumc_net::packets::outgoing::game_event::GameEventPacket;
use ferrumc_net::packets::outgoing::keep_alive::OutgoingKeepAlivePacket;
use ferrumc_net::packets::outgoing::login_disconnect::LoginDisconnectPacket;
use ferrumc_net::packets::outgoing::login_play::LoginPlayPacket;
use ferrumc_net::packets::outgoing::login_success::LoginSuccessPacket;
use ferrumc_net::packets::outgoing::player_info_update::PlayerInfoUpdatePacket;
use ferrumc_net::packets::outgoing::registry_data::get_registry_packets;
use ferrumc_net::packets::outgoing::set_center_chunk::SetCenterChunk;
Expand All @@ -43,53 +42,28 @@ async fn handle_login_start(
) -> Result<LoginStartEvent, NetError> {
let uuid = login_start_event.login_start_packet.uuid;
let username = login_start_event.login_start_packet.username.as_str();
let player_identity = PlayerIdentity::new(username.to_string(), uuid);
debug!("Handling login start event for user: {username}, uuid: {uuid}");

// Add the player identity component to the ECS for the entity.
state
.universe
.add_component::<PlayerIdentity>(
login_start_event.conn_id,
PlayerIdentity::new(username.to_string(), uuid),
)?
/*.add_component::<ChunkReceiver>(login_start_event.conn_id, ChunkReceiver::default())?*/;

//Send a Login Success Response to further the login sequence
let mut writer = state
.universe
.get_mut::<StreamWriter>(login_start_event.conn_id)?;

if get_global_config().whitelist {
let whitelist = get_whitelist();

if whitelist.get(&uuid).is_none() {
writer
.send_packet(
&LoginDisconnectPacket::new(
"{\"translate\":\"multiplayer.disconnect.not_whitelisted\"}",
),
&NetEncodeOpts::WithLength,
)
.await?;
return Ok(login_start_event);
let event = PlayerStartLoginEvent {
entity: login_start_event.conn_id,
profile: PlayerIdentity::new(username.to_string(), uuid),
};

match PlayerStartLoginEvent::trigger(event, state.clone()).await {
Err(NetError::Kick(msg)) => Err(NetError::Kick(msg)),
Err(NetError::EventsError(EventsError::Cancelled)) => Ok(login_start_event),
Ok(event) => {
// Add the player identity component to the ECS for the entity.
ferrumc_net::connection::send_login_success(
state,
login_start_event.conn_id,
event.profile,
)
.await?;
Ok(login_start_event)
}
e => e.map(|_| login_start_event),
}

// Add the player identity component to the ECS for the entity.
state
.universe
.add_component::<PlayerIdentity>(login_start_event.conn_id, player_identity)?;

//Send a Login Success Response to further the login sequence
writer
.send_packet(
&LoginSuccessPacket::new(uuid, username),
&NetEncodeOpts::WithLength,
)
.await?;

Ok(login_start_event)
}

#[event_handler]
Expand Down Expand Up @@ -158,12 +132,20 @@ async fn handle_ack_finish_configuration(

*conn_state = ConnectionState::Play;

let chunk = state.world.load_chunk(0, 0, "overworld").await.ok();

let y = if let Some(ref chunk) = chunk {
(chunk.heightmaps.motion_blocking_height(0, 0)) as f64
} else {
256.0
};

// add components to the entity after the connection state has been set to play.
// to avoid wasting resources on entities that are fetching stuff like server status etc.
state
.universe
.add_component::<Position>(entity_id, Position::default())?
.add_component::<Rotation>(entity_id, Rotation::default())?
.add_component::<Position>(entity_id, Position::new(0.0, y, 0.0))?
.add_component::<Rotation>(entity_id, Rotation::new(0.0, 0.0))?
.add_component::<OnGround>(entity_id, OnGround::default())?
.add_component::<ChunkReceiver>(entity_id, ChunkReceiver::default())?;

Expand All @@ -174,7 +156,7 @@ async fn handle_ack_finish_configuration(
.await?;
writer // 29
.send_packet(
&SynchronizePlayerPositionPacket::default(), // The coordinates here should be used for the center chunk.
&SynchronizePlayerPositionPacket::from_player(entity_id, state.clone())?, // The coordinates here should be used for the center chunk.
&NetEncodeOpts::WithLength,
)
.await?;
Expand Down Expand Up @@ -205,17 +187,23 @@ async fn handle_ack_finish_configuration(

send_keep_alive(entity_id, &state, &mut writer).await?;

let pos = state.universe.get_mut::<Position>(entity_id)?;
let mut chunk_recv = state.universe.get_mut::<ChunkReceiver>(entity_id)?;
chunk_recv.last_chunk = Some((pos.x as i32, pos.z as i32, String::from("overworld")));
chunk_recv.calculate_chunks().await;
if let Some(ref chunk) = chunk {
writer.send_packet(&ferrumc_net::packets::outgoing::chunk_and_light_data::ChunkAndLightData::from_chunk(chunk)?, &NetEncodeOpts::WithLength).await?;
}
}

let pos = state.universe.get::<Position>(entity_id)?;
let mut chunk_recv = state.universe.get_mut::<ChunkReceiver>(entity_id)?;
chunk_recv.last_chunk = Some((pos.x as i32, pos.z as i32, String::from("overworld")));
chunk_recv.calculate_chunks().await;
drop(chunk_recv);

player_info_update_packets(entity_id, &state).await?;
broadcast_spawn_entity_packet(entity_id, &state).await?;

Ok(ack_finish_configuration_event)
}

async fn send_keep_alive(
conn_id: usize,
state: &GlobalState,
Expand Down Expand Up @@ -244,12 +232,7 @@ async fn player_info_update_packets(entity_id: Entity, state: &GlobalState) -> N
let packet = PlayerInfoUpdatePacket::new_player_join_packet(entity_id, state);

let start = Instant::now();
broadcast(
&packet,
state,
BroadcastOptions::default().except([entity_id]),
)
.await?;
broadcast(&packet, state, BroadcastOptions::default().all()).await?;
trace!(
"Broadcasting player info update took: {:?}",
start.elapsed()
Expand Down Expand Up @@ -286,10 +269,12 @@ async fn broadcast_spawn_entity_packet(entity_id: Entity, state: &GlobalState) -
let writer = state.universe.get_mut::<StreamWriter>(entity_id)?;
futures::stream::iter(get_all_play_players(state))
.fold(writer, |mut writer, entity| async move {
if let Ok(packet) = SpawnEntityPacket::player(entity, state) {
let _ = writer
.send_packet(&packet, &NetEncodeOpts::WithLength)
.await;
if entity != entity_id {
if let Ok(packet) = SpawnEntityPacket::player(entity, state) {
let _ = writer
.send_packet(&packet, &NetEncodeOpts::WithLength)
.await;
}
}
writer
})
Expand Down
3 changes: 3 additions & 0 deletions src/bin/src/packet_handlers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ mod login_process;
mod player;
mod player_leave;
mod tick_handler;

mod velocity;
mod whitelist;
10 changes: 8 additions & 2 deletions src/bin/src/packet_handlers/player/do_action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ async fn handle_player_do_action(
) -> Result<PlayerDoActionEvent, NetError> {
trace!("player just did: {:?}", event.action);

// TODO: replace this with a better system to support multiple actions
match event.action {
PlayerCommandAction::StartSneaking => {
let packet = EntityMetadataPacket::new(
Expand All @@ -26,8 +27,13 @@ async fn handle_player_do_action(
broadcast(&packet, &state, Default::default()).await?;
}
PlayerCommandAction::StopSneaking => {
let packet =
EntityMetadataPacket::new(event.entity_id, [EntityMetadata::entity_standing()]);
let packet = EntityMetadataPacket::new(
event.entity_id,
[
EntityMetadata::entity_state_none(),
EntityMetadata::entity_standing(),
],
);

broadcast(&packet, &state, Default::default()).await?;
}
Expand Down
14 changes: 12 additions & 2 deletions src/bin/src/packet_handlers/player_leave.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use ferrumc_core::identity::player_identity::PlayerIdentity;
use ferrumc_macros::event_handler;
use ferrumc_net::connection::PlayerDisconnectEvent;
use ferrumc_net::errors::NetError;
use ferrumc_net::packets::outgoing::player_info_remove::PlayerInfoRemovePacket;
use ferrumc_net::packets::outgoing::remove_entities::RemoveEntitiesPacket;
use ferrumc_net::utils::broadcast::{broadcast, BroadcastOptions};
use ferrumc_state::GlobalState;
Expand All @@ -15,10 +17,18 @@ async fn handle_player_disconnect(

info!("Player disconnected: {:?}", entity_id);

let remove_entity_packet = RemoveEntitiesPacket::from_entities([entity_id]);
{
let profile = state.universe.get::<PlayerIdentity>(entity_id)?;
broadcast(
&PlayerInfoRemovePacket::new(vec![profile.uuid]),
&state,
BroadcastOptions::default().all(),
)
.await?;
}

broadcast(
&remove_entity_packet,
&RemoveEntitiesPacket::from_entities([entity_id]),
&state,
BroadcastOptions::default().all(),
)
Expand Down
Loading