Skip to content

Commit

Permalink
Merge pull request #104 from Bryntet/keepalive
Browse files Browse the repository at this point in the history
Add keep alive packets
  • Loading branch information
lukas0008 authored Sep 23, 2024
2 parents 14a1343 + cda86fd commit 5823f32
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 19 deletions.
8 changes: 8 additions & 0 deletions pumpkin-protocol/src/client/play/c_keep_alive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pumpkin_macros::packet;
use serde::Serialize;

#[packet(0x26)]
#[derive(Serialize)]
pub struct CKeepAlive {
pub keep_alive_id: i64,
}
2 changes: 2 additions & 0 deletions pumpkin-protocol/src/client/play/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ mod c_entity_velocity;
mod c_game_event;
mod c_head_rot;
mod c_hurt_animation;
mod c_keep_alive;
mod c_login;
mod c_open_screen;
mod c_particle;
Expand Down Expand Up @@ -57,6 +58,7 @@ pub use c_entity_velocity::*;
pub use c_game_event::*;
pub use c_head_rot::*;
pub use c_hurt_animation::*;
pub use c_keep_alive::*;
pub use c_login::*;
pub use c_open_screen::*;
pub use c_particle::*;
Expand Down
2 changes: 2 additions & 0 deletions pumpkin-protocol/src/server/play/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod s_client_information;
mod s_close_container;
mod s_confirm_teleport;
mod s_interact;
mod s_keep_alive;
mod s_ping_request;
mod s_player_action;
mod s_player_command;
Expand All @@ -25,6 +26,7 @@ pub use s_client_information::*;
pub use s_close_container::*;
pub use s_confirm_teleport::*;
pub use s_interact::*;
pub use s_keep_alive::*;
pub use s_ping_request::*;
pub use s_player_action::*;
pub use s_player_command::*;
Expand Down
8 changes: 8 additions & 0 deletions pumpkin-protocol/src/server/play/s_keep_alive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pumpkin_macros::packet;
use serde::Deserialize;

#[packet(0x18)]
#[derive(Deserialize)]
pub struct SKeepAlive {
pub keep_alive_id: i64,
}
13 changes: 12 additions & 1 deletion pumpkin/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,10 +112,19 @@ pub struct Client {

/// Indicates whether the client should be converted into a player.
pub make_player: AtomicBool,
/// Sends each keep alive packet that the server receives for a player to here, which gets picked up in a tokio task
pub keep_alive_sender: Arc<tokio::sync::mpsc::Sender<i64>>,
/// Stores the last time it was confirmed that the client is alive
pub last_alive_received: AtomicCell<std::time::Instant>,
}

impl Client {
pub fn new(token: Token, connection: TcpStream, address: SocketAddr) -> Self {
pub fn new(
token: Token,
connection: TcpStream,
address: SocketAddr,
keep_alive_sender: Arc<tokio::sync::mpsc::Sender<i64>>,
) -> Self {
Self {
protocol_version: AtomicI32::new(0),
gameprofile: Mutex::new(None),
Expand All @@ -131,6 +140,8 @@ impl Client {
closed: AtomicBool::new(false),
client_packets_queue: Arc::new(Mutex::new(Vec::new())),
make_player: AtomicBool::new(false),
keep_alive_sender,
last_alive_received: AtomicCell::new(std::time::Instant::now()),
}
}

Expand Down
19 changes: 16 additions & 3 deletions pumpkin/src/entity/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use pumpkin_protocol::{
ConnectionState, RawPacket, ServerPacket, VarInt,
};

use pumpkin_protocol::server::play::SCloseContainer;
use pumpkin_protocol::server::play::{SCloseContainer, SKeepAlive};
use pumpkin_world::item::ItemStack;

use crate::{
Expand All @@ -50,7 +50,7 @@ pub struct Player {
/// The player's game profile information, including their username and UUID.
pub gameprofile: GameProfile,
/// The client connection associated with the player.
pub client: Client,
pub client: Arc<Client>,
/// The player's configuration settings. Changes when the Player changes their settings.
pub config: Mutex<PlayerConfig>,
/// The player's current gamemode (e.g., Survival, Creative, Adventure).
Expand Down Expand Up @@ -90,7 +90,12 @@ pub struct Player {
}

impl Player {
pub fn new(client: Client, world: Arc<World>, entity_id: EntityId, gamemode: GameMode) -> Self {
pub fn new(
client: Arc<Client>,
world: Arc<World>,
entity_id: EntityId,
gamemode: GameMode,
) -> Self {
let gameprofile = match client.gameprofile.lock().clone() {
Some(profile) => profile,
None => {
Expand Down Expand Up @@ -371,6 +376,14 @@ impl Player {
self.handle_close_container(server, SCloseContainer::read(bytebuf)?);
Ok(())
}
SKeepAlive::PACKET_ID => {
self.client
.keep_alive_sender
.send(SKeepAlive::read(bytebuf)?.keep_alive_id)
.await
.unwrap();
Ok(())
}
_ => {
log::error!("Failed to handle player packet id {:#04x}", packet.id.0);
Ok(())
Expand Down
46 changes: 41 additions & 5 deletions pumpkin/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ compile_error!("Compiling for WASI targets is not supported!");
use mio::net::TcpListener;
use mio::{Events, Interest, Poll, Token};

use std::collections::HashMap;
use std::io::{self, Read};

use client::{interrupted, Client};
use pumpkin_protocol::client::play::CKeepAlive;
use pumpkin_protocol::ConnectionState;
use server::Server;
use std::collections::HashMap;
use std::io::{self, Read};
use std::time::Duration;

// Setup some tokens to allow us to identify which event is for which socket.

Expand Down Expand Up @@ -78,7 +80,7 @@ fn main() -> io::Result<()> {
let use_console = ADVANCED_CONFIG.commands.use_console;
let rcon = ADVANCED_CONFIG.rcon.clone();

let mut clients: HashMap<Token, Client> = HashMap::new();
let mut clients: HashMap<Token, Arc<Client>> = HashMap::new();
let mut players: HashMap<Token, Arc<Player>> = HashMap::new();

let server = Arc::new(Server::new());
Expand Down Expand Up @@ -152,7 +154,41 @@ fn main() -> io::Result<()> {
token,
Interest::READABLE.add(Interest::WRITABLE),
)?;
let client = Client::new(token, connection, addr);
let keep_alive = tokio::sync::mpsc::channel(1024);
let client =
Arc::new(Client::new(token, connection, addr, keep_alive.0.into()));

{
let client = client.clone();
let mut receiver = keep_alive.1;
tokio::spawn(async move {
let mut interval = tokio::time::interval(Duration::from_secs(1));
loop {
interval.tick().await;
let now = std::time::Instant::now();
if client.connection_state.load() == ConnectionState::Play {
if now.duration_since(client.last_alive_received.load())
>= Duration::from_secs(15)
{
dbg!("no keep alive");
client.kick("No keep alive received");
break;
}
let random = rand::random::<i64>();
client.send_packet(&CKeepAlive {
keep_alive_id: random,
});
if let Some(id) = receiver.recv().await {
if id == random {
client.last_alive_received.store(now);
}
}
} else {
client.last_alive_received.store(now);
}
}
});
}
clients.insert(token, client);
},

Expand Down
9 changes: 4 additions & 5 deletions pumpkin/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ use parking_lot::{Mutex, RwLock};
use pumpkin_config::BASIC_CONFIG;
use pumpkin_core::GameMode;
use pumpkin_entity::EntityId;
use pumpkin_inventory::drag_handler::DragHandler;
use pumpkin_inventory::{Container, OpenContainer};
use pumpkin_plugin::PluginLoader;
use pumpkin_protocol::client::login::CEncryptionRequest;
use pumpkin_protocol::client::status::CStatusResponse;
use pumpkin_protocol::{client::config::CPluginMessage, ClientPacket};
use pumpkin_registry::Registry;
use pumpkin_world::dimension::Dimension;
use std::collections::HashMap;
use std::{
Expand All @@ -19,10 +22,6 @@ use std::{
time::Duration,
};

use pumpkin_inventory::drag_handler::DragHandler;
use pumpkin_inventory::{Container, OpenContainer};
use pumpkin_registry::Registry;

use crate::client::EncryptionError;
use crate::{
client::Client,
Expand Down Expand Up @@ -96,7 +95,7 @@ impl Server {
}
}

pub async fn add_player(&self, token: Token, client: Client) -> (Arc<Player>, Arc<World>) {
pub async fn add_player(&self, token: Token, client: Arc<Client>) -> (Arc<Player>, Arc<World>) {
let entity_id = self.new_entity_id();
let gamemode = match BASIC_CONFIG.default_gamemode {
GameMode::Undefined => GameMode::Survival,
Expand Down
9 changes: 4 additions & 5 deletions pumpkin/src/world/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ use std::{collections::HashMap, sync::Arc};

pub mod player_chunker;

use crate::{
client::Client,
entity::{player::Player, Entity},
};
use mio::Token;
use num_traits::ToPrimitive;
use parking_lot::Mutex;
Expand All @@ -18,11 +22,6 @@ use pumpkin_protocol::{
use pumpkin_world::level::Level;
use tokio::sync::mpsc;

use crate::{
client::Client,
entity::{player::Player, Entity},
};

/// Represents a Minecraft world, containing entities, players, and the underlying level data.
///
/// Each dimension (Overworld, Nether, End) typically has its own `World`.
Expand Down

0 comments on commit 5823f32

Please sign in to comment.