mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
added Play state
This commit is contained in:
@@ -1,10 +1,13 @@
|
||||
use std::{rc::Rc, sync::Mutex};
|
||||
|
||||
use crate::protocol::{
|
||||
client::{
|
||||
config::CFinishConfig,
|
||||
login::{CEncryptionRequest, CLoginSuccess},
|
||||
status::{CPingResponse, CStatusResponse},
|
||||
},
|
||||
server::{
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||
status::{SPingRequest, SStatusRequest},
|
||||
@@ -12,7 +15,7 @@ use crate::protocol::{
|
||||
ConnectionState,
|
||||
};
|
||||
|
||||
use super::Client;
|
||||
use super::{Client, PlayerConfig};
|
||||
|
||||
pub trait ClientPacketProcessor {
|
||||
// Handshake
|
||||
@@ -25,6 +28,9 @@ pub trait ClientPacketProcessor {
|
||||
fn handle_encryption_response(&mut self, encryption_response: SEncryptionResponse);
|
||||
fn handle_plugin_response(&mut self, plugin_response: SLoginPluginResponse);
|
||||
fn handle_login_acknowledged(&mut self, login_acknowledged: SLoginAcknowledged);
|
||||
// Config
|
||||
fn handle_client_information(&mut self, client_information: SClientInformation);
|
||||
fn handle_config_acknowledged(&mut self, config_acknowledged: SAcknowledgeFinishConfig);
|
||||
}
|
||||
|
||||
impl ClientPacketProcessor for Client {
|
||||
@@ -37,9 +43,15 @@ impl ClientPacketProcessor for Client {
|
||||
fn handle_status_request(&mut self, _status_request: SStatusRequest) {
|
||||
dbg!("sending status");
|
||||
|
||||
self.send_packet(CStatusResponse::new(
|
||||
serde_json::to_string(&self.server.status_response).unwrap(),
|
||||
))
|
||||
dbg!("test first");
|
||||
|
||||
let guard = self.server.try_lock().unwrap();
|
||||
dbg!("test");
|
||||
|
||||
let response = serde_json::to_string(&guard.status_response).unwrap();
|
||||
drop(guard);
|
||||
|
||||
self.send_packet(CStatusResponse::new(response));
|
||||
}
|
||||
|
||||
fn handle_ping_request(&mut self, ping_request: SPingRequest) {
|
||||
@@ -53,11 +65,17 @@ impl ClientPacketProcessor for Client {
|
||||
self.name = Some(login_start.name);
|
||||
self.uuid = Some(login_start.uuid);
|
||||
let verify_token: [u8; 4] = rand::random();
|
||||
let public_key_der = &self.server.to_owned().public_key_der;
|
||||
let public_key_der = self
|
||||
.server
|
||||
.to_owned()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.public_key_der
|
||||
.clone(); // todo do not clone
|
||||
let packet = CEncryptionRequest::new(
|
||||
"".into(),
|
||||
public_key_der.len() as i32,
|
||||
public_key_der,
|
||||
&public_key_der,
|
||||
verify_token.len() as i32,
|
||||
&verify_token,
|
||||
false, // TODO
|
||||
@@ -85,4 +103,25 @@ impl ClientPacketProcessor for Client {
|
||||
self.connection_state = ConnectionState::Config;
|
||||
dbg!("login achnowlaged");
|
||||
}
|
||||
fn handle_client_information(&mut self, client_information: SClientInformation) {
|
||||
self.config = Some(PlayerConfig {
|
||||
locale: client_information.locale,
|
||||
view_distance: client_information.view_distance,
|
||||
chat_mode: client_information.chat_mode,
|
||||
chat_colors: client_information.chat_colors,
|
||||
skin_parts: client_information.skin_parts,
|
||||
main_hand: client_information.main_hand,
|
||||
text_filtering: client_information.text_filtering,
|
||||
server_listing: client_information.server_listing,
|
||||
});
|
||||
// We are done with configuring
|
||||
self.send_packet(CFinishConfig::new());
|
||||
}
|
||||
|
||||
fn handle_config_acknowledged(&mut self, config_acknowledged: SAcknowledgeFinishConfig) {
|
||||
dbg!("config acknowledged");
|
||||
self.connection_state = ConnectionState::Play;
|
||||
// generate a player
|
||||
self.server.lock().unwrap().spawn_player(&self.token);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,15 @@ use std::{
|
||||
collections::VecDeque,
|
||||
io::{self, Write},
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
entity::player::{ChatMode, Hand},
|
||||
protocol::{
|
||||
client::login::CLoginDisconnect,
|
||||
client::{config::CConfigDisconnect, login::CLoginDisconnect},
|
||||
server::{
|
||||
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||
handshake::SHandShake,
|
||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||
status::{SPingRequest, SStatusRequest},
|
||||
@@ -19,14 +22,13 @@ use crate::{
|
||||
|
||||
use crate::protocol::ConnectionState;
|
||||
use anyhow::Context;
|
||||
use mio::{event::Event, net::TcpStream, Registry};
|
||||
use mio::{event::Event, net::TcpStream, Registry, Token};
|
||||
use packet_decoder::PacketDecoder;
|
||||
use packet_encoder::PacketEncoder;
|
||||
use rsa::Pkcs1v15Encrypt;
|
||||
use std::io::Read;
|
||||
|
||||
mod client_packet;
|
||||
pub mod player;
|
||||
|
||||
mod packet_decoder;
|
||||
mod packet_encoder;
|
||||
@@ -35,13 +37,27 @@ use client_packet::ClientPacketProcessor;
|
||||
|
||||
pub const MAX_PACKET_SIZE: i32 = 2097152;
|
||||
|
||||
pub struct PlayerConfig {
|
||||
locale: String, // 16
|
||||
view_distance: i8,
|
||||
chat_mode: ChatMode,
|
||||
chat_colors: bool,
|
||||
skin_parts: u8,
|
||||
main_hand: Hand,
|
||||
text_filtering: bool,
|
||||
server_listing: bool,
|
||||
}
|
||||
|
||||
pub struct Client {
|
||||
pub name: Option<String>,
|
||||
pub uuid: Option<uuid::Uuid>,
|
||||
pub server: Rc<Server>,
|
||||
pub config: Option<PlayerConfig>,
|
||||
|
||||
pub server: Arc<Mutex<Server>>,
|
||||
pub connection_state: ConnectionState,
|
||||
pub encrytion: bool,
|
||||
pub closed: bool,
|
||||
pub token: Rc<Token>,
|
||||
pub connection: TcpStream,
|
||||
enc: PacketEncoder,
|
||||
dec: PacketDecoder,
|
||||
@@ -49,11 +65,13 @@ pub struct Client {
|
||||
}
|
||||
|
||||
impl Client {
|
||||
pub fn new(server: Rc<Server>, connection: TcpStream) -> Self {
|
||||
pub fn new(server: Arc<Mutex<Server>>, token: Rc<Token>, connection: TcpStream) -> Self {
|
||||
Self {
|
||||
name: None,
|
||||
uuid: None,
|
||||
config: None,
|
||||
server,
|
||||
token,
|
||||
connection_state: ConnectionState::HandShake,
|
||||
connection,
|
||||
enc: PacketEncoder::default(),
|
||||
@@ -72,6 +90,8 @@ impl Client {
|
||||
self.encrytion = true;
|
||||
let shared_secret = self
|
||||
.server
|
||||
.lock()
|
||||
.unwrap()
|
||||
.private_key
|
||||
.decrypt(Pkcs1v15Encrypt, &shared_secret)
|
||||
.context("failed to decrypt shared secret")?;
|
||||
@@ -136,6 +156,18 @@ impl Client {
|
||||
packet.id
|
||||
),
|
||||
},
|
||||
crate::protocol::ConnectionState::Config => match packet.id {
|
||||
SClientInformation::PACKET_ID => {
|
||||
self.handle_client_information(SClientInformation::read(bytebuf))
|
||||
}
|
||||
SAcknowledgeFinishConfig::PACKET_ID => {
|
||||
self.handle_config_acknowledged(SAcknowledgeFinishConfig::read(bytebuf))
|
||||
}
|
||||
_ => log::error!(
|
||||
"Failed to handle packet id {} while in Config state",
|
||||
packet.id
|
||||
),
|
||||
},
|
||||
_ => log::error!("Invalid Connection state {:?}", self.connection_state),
|
||||
}
|
||||
}
|
||||
@@ -184,8 +216,16 @@ impl Client {
|
||||
|
||||
pub fn kick(&mut self, reason: String) {
|
||||
// Todo
|
||||
if self.connection_state == ConnectionState::Login {
|
||||
self.send_packet(CLoginDisconnect::new(reason));
|
||||
match self.connection_state {
|
||||
ConnectionState::Login => {
|
||||
self.send_packet(CLoginDisconnect::new(reason));
|
||||
}
|
||||
ConnectionState::Config => {
|
||||
self.send_packet(CConfigDisconnect::new(reason));
|
||||
}
|
||||
_ => {
|
||||
log::warn!("Cant't kick in {:?} State", self.connection_state)
|
||||
}
|
||||
}
|
||||
self.close()
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, KeyIvInit};
|
||||
use anyhow::{bail, ensure, Context};
|
||||
use bytes::{Buf, BytesMut};
|
||||
|
||||
@@ -37,7 +37,7 @@ impl PacketEncoder {
|
||||
let data_len = self.buf.len() - start_len;
|
||||
|
||||
if false { // compression
|
||||
}
|
||||
}
|
||||
let packet_len = data_len;
|
||||
|
||||
ensure!(
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
use crate::protocol::RawPacket;
|
||||
|
||||
use super::Client;
|
||||
|
||||
pub struct Player {
|
||||
// All networking stuff
|
||||
pub client: Client,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn handle_packet(&mut self, _packet: &mut RawPacket) {}
|
||||
}
|
||||
13
pumpkin/src/entity/mod.rs
Normal file
13
pumpkin/src/entity/mod.rs
Normal file
@@ -0,0 +1,13 @@
|
||||
pub mod player;
|
||||
|
||||
pub type EntityId = i32;
|
||||
|
||||
pub struct Entity {
|
||||
pub entity_id: EntityId,
|
||||
}
|
||||
|
||||
impl Entity {
|
||||
pub fn new(entity_id: EntityId) -> Self {
|
||||
Self { entity_id }
|
||||
}
|
||||
}
|
||||
103
pumpkin/src/entity/player.rs
Normal file
103
pumpkin/src/entity/player.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
protocol::{ClientPacket, RawPacket, VarInt},
|
||||
};
|
||||
|
||||
use super::{Entity, EntityId};
|
||||
|
||||
pub struct Player {
|
||||
pub entity: Entity,
|
||||
// All networking stuff
|
||||
pub client: Client,
|
||||
}
|
||||
|
||||
impl Player {
|
||||
pub fn new(entity: Entity, client: Client) -> Self {
|
||||
Self { entity, client }
|
||||
}
|
||||
|
||||
pub fn entity_id(&self) -> EntityId {
|
||||
self.entity.entity_id
|
||||
}
|
||||
|
||||
pub fn handle_packet(&mut self, _packet: &mut RawPacket) {}
|
||||
|
||||
pub fn send_packet<P: ClientPacket>(&mut self, packet: P) {
|
||||
self.client.send_packet(packet);
|
||||
}
|
||||
}
|
||||
|
||||
pub enum Hand {
|
||||
Main,
|
||||
Off,
|
||||
}
|
||||
|
||||
impl Hand {
|
||||
pub fn from_varint(varint: VarInt) -> Self {
|
||||
match varint {
|
||||
0 => Self::Off,
|
||||
1 => Self::Main,
|
||||
_ => {
|
||||
log::info!("Unexpected Hand {}", varint);
|
||||
Self::Main
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ChatMode {
|
||||
Enabled,
|
||||
CommandsOnly,
|
||||
Hidden,
|
||||
}
|
||||
|
||||
impl ChatMode {
|
||||
pub fn from_varint(varint: VarInt) -> Self {
|
||||
match varint {
|
||||
0 => Self::Enabled,
|
||||
1 => Self::CommandsOnly,
|
||||
2 => Self::Hidden,
|
||||
_ => {
|
||||
log::info!("Unexpected ChatMode {}", varint);
|
||||
Self::Enabled
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum GameMode {
|
||||
Undefined,
|
||||
Survival,
|
||||
Creative,
|
||||
Adventure,
|
||||
Spectator,
|
||||
}
|
||||
|
||||
impl GameMode {
|
||||
pub fn from_byte(byte: i8) -> Self {
|
||||
match byte {
|
||||
-1 => GameMode::Undefined,
|
||||
0 => GameMode::Survival,
|
||||
1 => GameMode::Creative,
|
||||
2 => GameMode::Adventure,
|
||||
3 => GameMode::Spectator,
|
||||
_ => {
|
||||
log::info!("Unexpected GameMode {}", byte);
|
||||
Self::Survival
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_byte(self) -> i8 {
|
||||
match self {
|
||||
Self::Undefined => -1,
|
||||
Self::Survival => 0,
|
||||
Self::Creative => 1,
|
||||
Self::Adventure => 2,
|
||||
Self::Spectator => 3,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
#![feature(read_buf)]
|
||||
|
||||
use mio::net::TcpListener;
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use std::collections::HashMap;
|
||||
@@ -9,13 +7,17 @@ use std::io::{self};
|
||||
const SERVER: Token = Token(0);
|
||||
|
||||
pub mod client;
|
||||
pub mod entity;
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
pub mod util;
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
fn main() -> io::Result<()> {
|
||||
use std::rc::Rc;
|
||||
use std::{
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use client::{interrupted, Client};
|
||||
use server::Server;
|
||||
@@ -35,14 +37,12 @@ fn main() -> io::Result<()> {
|
||||
poll.registry()
|
||||
.register(&mut listener, SERVER, Interest::READABLE)?;
|
||||
|
||||
// Map of `Token` -> `TcpStream`.
|
||||
let mut connections = HashMap::new();
|
||||
// Unique token for each incoming connection.
|
||||
let mut unique_token = Token(SERVER.0 + 1);
|
||||
|
||||
log::info!("You now can connect to the server");
|
||||
|
||||
let server = Rc::new(Server::new());
|
||||
let mut server = Arc::new(Mutex::new(Server::new()));
|
||||
|
||||
loop {
|
||||
if let Err(err) = poll.poll(&mut events, None) {
|
||||
@@ -82,11 +82,14 @@ fn main() -> io::Result<()> {
|
||||
Interest::READABLE.add(Interest::WRITABLE),
|
||||
)?;
|
||||
|
||||
connections.insert(token, Client::new(Rc::clone(&server), connection));
|
||||
let rc_server = Arc::clone(&server);
|
||||
let mut guard = server.try_lock().unwrap();
|
||||
guard.new_client(rc_server, connection, token);
|
||||
},
|
||||
|
||||
token => {
|
||||
// Maybe received an event for a TCP connection.
|
||||
let done = if let Some(client) = connections.get_mut(&token) {
|
||||
let done = if let Some(client) = server.try_lock().unwrap().connections.get_mut(&token) {
|
||||
client.poll(poll.registry(), event).unwrap();
|
||||
client.closed
|
||||
} else {
|
||||
@@ -94,7 +97,7 @@ fn main() -> io::Result<()> {
|
||||
false
|
||||
};
|
||||
if done {
|
||||
if let Some(mut client) = connections.remove(&token) {
|
||||
if let Some(mut client) = server.try_lock().unwrap().connections.remove(&token) {
|
||||
poll.registry().deregister(&mut client.connection)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,6 +423,12 @@ impl ByteBuffer {
|
||||
self.write_bytes_len(val.as_bytes(), max_len);
|
||||
}
|
||||
|
||||
pub fn write_string_array(&mut self, array: &[String]) {
|
||||
for string in array {
|
||||
self.write_string(string)
|
||||
}
|
||||
}
|
||||
|
||||
// Read operations
|
||||
|
||||
/// Read a defined amount of raw bytes, or return an IO error if not enough bytes are
|
||||
|
||||
43
pumpkin/src/protocol/client/config/mod.rs
Normal file
43
pumpkin/src/protocol/client/config/mod.rs
Normal file
@@ -0,0 +1,43 @@
|
||||
use crate::protocol::ClientPacket;
|
||||
|
||||
pub struct CCookieRequest {
|
||||
// TODO
|
||||
}
|
||||
|
||||
impl ClientPacket for CCookieRequest {
|
||||
const PACKET_ID: crate::protocol::VarInt = 0;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
||||
}
|
||||
|
||||
pub struct CConfigDisconnect {
|
||||
reason: String,
|
||||
}
|
||||
|
||||
impl CConfigDisconnect {
|
||||
pub fn new(reason: String) -> Self {
|
||||
Self { reason }
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPacket for CConfigDisconnect {
|
||||
const PACKET_ID: crate::protocol::VarInt = 2;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_string(&self.reason);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CFinishConfig {}
|
||||
|
||||
impl CFinishConfig {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPacket for CFinishConfig {
|
||||
const PACKET_ID: crate::protocol::VarInt = 3;
|
||||
|
||||
fn write(&self, _bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// Clientbound Packets
|
||||
// Server -> Client
|
||||
//
|
||||
pub mod config;
|
||||
pub mod login;
|
||||
pub mod play;
|
||||
pub mod status;
|
||||
|
||||
110
pumpkin/src/protocol/client/play/mod.rs
Normal file
110
pumpkin/src/protocol/client/play/mod.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use crate::{
|
||||
entity::player::GameMode,
|
||||
protocol::{ClientPacket, VarInt},
|
||||
};
|
||||
|
||||
pub struct CLogin {
|
||||
entity_id: i32,
|
||||
is_hardcore: bool,
|
||||
dimension_count: VarInt,
|
||||
dimension_names: Vec<String>,
|
||||
max_players: VarInt,
|
||||
view_distance: VarInt,
|
||||
simulated_distance: VarInt,
|
||||
reduced_debug_info: bool,
|
||||
enabled_respawn_screen: bool,
|
||||
limited_crafting: bool,
|
||||
dimension_type: VarInt,
|
||||
dimension_name: String,
|
||||
hashed_seed: i64,
|
||||
game_mode: GameMode,
|
||||
previous_gamemode: GameMode,
|
||||
debug: bool,
|
||||
is_flat: bool,
|
||||
has_death_loc: bool,
|
||||
death_dimension_name: Option<String>,
|
||||
death_loc: Option<String>, // POSITION NOT STRING
|
||||
portal_cooldown: VarInt,
|
||||
enforce_secure_chat: bool,
|
||||
}
|
||||
|
||||
impl CLogin {
|
||||
pub fn new(
|
||||
entity_id: i32,
|
||||
is_hardcore: bool,
|
||||
dimension_count: VarInt,
|
||||
dimension_names: Vec<String>,
|
||||
max_players: VarInt,
|
||||
view_distance: VarInt,
|
||||
simulated_distance: VarInt,
|
||||
reduced_debug_info: bool,
|
||||
enabled_respawn_screen: bool,
|
||||
limited_crafting: bool,
|
||||
dimension_type: VarInt,
|
||||
dimension_name: String,
|
||||
hashed_seed: i64,
|
||||
game_mode: GameMode,
|
||||
previous_gamemode: GameMode,
|
||||
debug: bool,
|
||||
is_flat: bool,
|
||||
has_death_loc: bool,
|
||||
death_dimension_name: Option<String>,
|
||||
death_loc: Option<String>,
|
||||
portal_cooldown: VarInt,
|
||||
enforce_secure_chat: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
entity_id,
|
||||
is_hardcore,
|
||||
dimension_count,
|
||||
dimension_names,
|
||||
max_players,
|
||||
view_distance,
|
||||
simulated_distance,
|
||||
reduced_debug_info,
|
||||
enabled_respawn_screen,
|
||||
limited_crafting,
|
||||
dimension_type,
|
||||
dimension_name,
|
||||
hashed_seed,
|
||||
game_mode,
|
||||
previous_gamemode,
|
||||
debug,
|
||||
is_flat,
|
||||
has_death_loc,
|
||||
death_dimension_name,
|
||||
death_loc,
|
||||
portal_cooldown,
|
||||
enforce_secure_chat,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientPacket for CLogin {
|
||||
const PACKET_ID: VarInt = 0x2B;
|
||||
|
||||
fn write(&self, bytebuf: &mut crate::protocol::bytebuf::buffer::ByteBuffer) {
|
||||
bytebuf.write_i32(self.entity_id);
|
||||
bytebuf.write_bool(self.is_hardcore);
|
||||
bytebuf.write_var_int(self.dimension_count);
|
||||
bytebuf.write_string_array(self.dimension_names.as_slice());
|
||||
bytebuf.write_var_int(self.max_players);
|
||||
bytebuf.write_var_int(self.view_distance);
|
||||
bytebuf.write_var_int(self.simulated_distance);
|
||||
bytebuf.write_bool(self.reduced_debug_info);
|
||||
bytebuf.write_bool(self.enabled_respawn_screen);
|
||||
bytebuf.write_bool(self.limited_crafting);
|
||||
bytebuf.write_var_int(self.dimension_type);
|
||||
bytebuf.write_string(&self.dimension_name);
|
||||
bytebuf.write_i64(self.hashed_seed);
|
||||
bytebuf.write_u8(self.game_mode.to_byte() as u8);
|
||||
bytebuf.write_i8(self.previous_gamemode.to_byte());
|
||||
bytebuf.write_bool(self.debug);
|
||||
bytebuf.write_bool(self.is_flat);
|
||||
bytebuf.write_bool(self.has_death_loc);
|
||||
bytebuf.write_option(&self.death_dimension_name, |buf, v| buf.write_string(v));
|
||||
bytebuf.write_option(&self.death_loc, |buf, v| buf.write_string(v));
|
||||
bytebuf.write_var_int(self.portal_cooldown);
|
||||
bytebuf.write_bool(self.enforce_secure_chat);
|
||||
}
|
||||
}
|
||||
@@ -91,9 +91,10 @@ pub type VarLong = i64;
|
||||
pub enum ConnectionState {
|
||||
HandShake,
|
||||
Status,
|
||||
Login,
|
||||
Login,
|
||||
Transfer,
|
||||
Config,
|
||||
Play,
|
||||
}
|
||||
|
||||
impl ConnectionState {
|
||||
@@ -102,7 +103,10 @@ impl ConnectionState {
|
||||
1 => Self::Status,
|
||||
2 => Self::Login,
|
||||
3 => Self::Transfer,
|
||||
_ => panic!("Unexpected Status {}", var_int),
|
||||
_ => {
|
||||
log::info!("Unexpected Status {}", var_int);
|
||||
Self::Status
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -138,7 +142,7 @@ pub struct Version {
|
||||
pub struct Players {
|
||||
pub max: u32,
|
||||
pub online: u32,
|
||||
pub sample: Sample,
|
||||
pub sample: Vec<Sample>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
|
||||
42
pumpkin/src/protocol/server/config/mod.rs
Normal file
42
pumpkin/src/protocol/server/config/mod.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use crate::{
|
||||
entity::player::{ChatMode, Hand},
|
||||
protocol::{bytebuf::buffer::ByteBuffer, VarInt},
|
||||
};
|
||||
|
||||
pub struct SClientInformation {
|
||||
pub locale: String, // 16
|
||||
pub view_distance: i8,
|
||||
pub chat_mode: ChatMode, // Varint
|
||||
pub chat_colors: bool,
|
||||
pub skin_parts: u8,
|
||||
pub main_hand: Hand,
|
||||
pub text_filtering: bool,
|
||||
pub server_listing: bool,
|
||||
}
|
||||
|
||||
impl SClientInformation {
|
||||
pub const PACKET_ID: VarInt = 0;
|
||||
|
||||
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {
|
||||
locale: bytebuf.read_string_len(16).unwrap(),
|
||||
view_distance: bytebuf.read_i8().unwrap(),
|
||||
chat_mode: ChatMode::from_varint(bytebuf.read_var_int().unwrap()),
|
||||
chat_colors: bytebuf.read_bool().unwrap(),
|
||||
skin_parts: bytebuf.read_u8().unwrap(),
|
||||
main_hand: Hand::from_varint(bytebuf.read_var_int().unwrap()),
|
||||
text_filtering: bytebuf.read_bool().unwrap(),
|
||||
server_listing: bytebuf.read_bool().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SAcknowledgeFinishConfig {}
|
||||
|
||||
impl SAcknowledgeFinishConfig {
|
||||
pub const PACKET_ID: VarInt = 3;
|
||||
|
||||
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
// Serverbound Packets
|
||||
// Client -> Server
|
||||
pub mod config;
|
||||
pub mod handshake;
|
||||
pub mod login;
|
||||
pub mod status;
|
||||
|
||||
0
pumpkin/src/protocol/server/play/mod.rs
Normal file
0
pumpkin/src/protocol/server/play/mod.rs
Normal file
@@ -1,20 +1,44 @@
|
||||
use std::io::Cursor;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io::Cursor,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicI32, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use mio::{net::TcpStream, Token};
|
||||
use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
|
||||
use crate::protocol::{Players, Sample, StatusResponse, Version};
|
||||
use crate::{
|
||||
client::Client,
|
||||
entity::{
|
||||
player::{GameMode, Player},
|
||||
Entity, EntityId,
|
||||
},
|
||||
protocol::{client::play::CLogin, Players, Sample, StatusResponse, VarInt, Version},
|
||||
};
|
||||
|
||||
pub struct Server {
|
||||
pub compression_threshold: Option<u8>,
|
||||
|
||||
pub online_mode: bool,
|
||||
pub encriyption: bool, // encription is always required when online_mode is disabled
|
||||
pub encryption: bool, // encryptiony is always required when online_mode is disabled
|
||||
pub public_key: RsaPublicKey,
|
||||
pub private_key: RsaPrivateKey,
|
||||
pub public_key_der: Box<[u8]>,
|
||||
|
||||
pub max_players: u32,
|
||||
|
||||
pub status_response: StatusResponse,
|
||||
pub connections: HashMap<Token, Client>,
|
||||
|
||||
// todo replace with HashMap <World, Player>
|
||||
entity_id: AtomicI32, // todo: place this into every world
|
||||
pub players: Vec<Player>,
|
||||
pub difficulty: Difficulty,
|
||||
}
|
||||
|
||||
impl Default for Server {
|
||||
@@ -25,7 +49,8 @@ impl Default for Server {
|
||||
|
||||
impl Server {
|
||||
pub fn new() -> Self {
|
||||
let status_response = Self::default_response();
|
||||
let max_players = 20;
|
||||
let status_response = Self::default_response(max_players);
|
||||
|
||||
// todo, only create when needed
|
||||
let (public_key, private_key) = Self::generate_keys();
|
||||
@@ -37,17 +62,66 @@ impl Server {
|
||||
.into_boxed_slice();
|
||||
|
||||
Self {
|
||||
// 0 is invalid
|
||||
entity_id: 2.into(),
|
||||
online_mode: true,
|
||||
encriyption: true,
|
||||
encryption: true,
|
||||
compression_threshold: None, // 256
|
||||
public_key,
|
||||
private_key,
|
||||
max_players,
|
||||
status_response,
|
||||
public_key_der,
|
||||
connections: HashMap::new(),
|
||||
players: Vec::new(),
|
||||
difficulty: Difficulty::Normal,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_response() -> StatusResponse {
|
||||
pub fn new_client(&mut self, rc: Arc<Mutex<Server>>, connection: TcpStream, token: Token) {
|
||||
self.connections
|
||||
.insert(token, Client::new(rc, Rc::new(token), connection));
|
||||
}
|
||||
|
||||
pub fn spawn_player(&mut self, token: &Token) {
|
||||
let mut player = Player {
|
||||
entity: Entity::new(self.new_entity_id()),
|
||||
client: self.connections.remove(token).unwrap(),
|
||||
};
|
||||
player.send_packet(CLogin::new(
|
||||
player.entity_id(),
|
||||
self.difficulty == Difficulty::Hard,
|
||||
1,
|
||||
vec!["minecraft:overworld".into()],
|
||||
self.max_players as VarInt,
|
||||
8, // view distance todo
|
||||
8, // sim view dinstance todo
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
1,
|
||||
"minecraft:overworld".into(),
|
||||
0, // seed
|
||||
GameMode::Survival,
|
||||
GameMode::Undefined,
|
||||
false,
|
||||
false,
|
||||
false, // deth loc
|
||||
None,
|
||||
None,
|
||||
0,
|
||||
false,
|
||||
));
|
||||
|
||||
self.players.push(player);
|
||||
}
|
||||
|
||||
// move to world
|
||||
pub fn new_entity_id(&self) -> EntityId {
|
||||
self.entity_id.fetch_add(1, Ordering::SeqCst)
|
||||
}
|
||||
|
||||
pub fn default_response(max_players: u32) -> StatusResponse {
|
||||
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png");
|
||||
|
||||
StatusResponse {
|
||||
@@ -56,12 +130,12 @@ impl Server {
|
||||
protocol: 767,
|
||||
},
|
||||
players: Players {
|
||||
max: 20,
|
||||
max: max_players,
|
||||
online: 0,
|
||||
sample: Sample {
|
||||
sample: vec![Sample {
|
||||
name: "".into(),
|
||||
id: "".into(),
|
||||
},
|
||||
}],
|
||||
},
|
||||
description: "Pumpkin Server".into(),
|
||||
favicon: Self::load_icon(path),
|
||||
@@ -88,3 +162,10 @@ impl Server {
|
||||
(pub_key, priv_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub enum Difficulty {
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user