add keep alive packets

This commit is contained in:
Edvin Bryntesson
2024-09-22 17:59:56 +02:00
parent 0da078a0fb
commit cda86fd59e
9 changed files with 97 additions and 19 deletions

View File

@@ -0,0 +1,8 @@
use pumpkin_macros::packet;
use serde::Serialize;
#[packet(0x26)]
#[derive(Serialize)]
pub struct CKeepAlive {
pub keep_alive_id: i64,
}

View File

@@ -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;
@@ -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::*;

View File

@@ -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;
@@ -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::*;

View File

@@ -0,0 +1,8 @@
use pumpkin_macros::packet;
use serde::Deserialize;
#[packet(0x18)]
#[derive(Deserialize)]
pub struct SKeepAlive {
pub keep_alive_id: i64,
}

View File

@@ -113,10 +113,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),
@@ -132,6 +141,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()),
}
}

View File

@@ -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::{
@@ -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).
@@ -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 => {
@@ -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(())

View File

@@ -6,11 +6,13 @@ compile_error!("Compiling for WASI targets is not supported!");
use mio::net::TcpListener;
use mio::{Events, Interest, Poll, Token};
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 client::{interrupted, Client};
use server::Server;
use std::time::Duration;
// Setup some tokens to allow us to identify which event is for which socket.
@@ -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());
@@ -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);
},

View File

@@ -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::{
@@ -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,
@@ -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,

View File

@@ -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;
@@ -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`.