added Play state
This commit is contained in:
parent
8a5e69f8e3
commit
4b8720efdc
@ -1,10 +1,13 @@
|
|||||||
|
use std::{rc::Rc, sync::Mutex};
|
||||||
|
|
||||||
use crate::protocol::{
|
use crate::protocol::{
|
||||||
client::{
|
client::{
|
||||||
|
config::CFinishConfig,
|
||||||
login::{CEncryptionRequest, CLoginSuccess},
|
login::{CEncryptionRequest, CLoginSuccess},
|
||||||
status::{CPingResponse, CStatusResponse},
|
status::{CPingResponse, CStatusResponse},
|
||||||
},
|
},
|
||||||
server::{
|
server::{
|
||||||
|
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||||
handshake::SHandShake,
|
handshake::SHandShake,
|
||||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||||
status::{SPingRequest, SStatusRequest},
|
status::{SPingRequest, SStatusRequest},
|
||||||
@ -12,7 +15,7 @@ use crate::protocol::{
|
|||||||
ConnectionState,
|
ConnectionState,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Client;
|
use super::{Client, PlayerConfig};
|
||||||
|
|
||||||
pub trait ClientPacketProcessor {
|
pub trait ClientPacketProcessor {
|
||||||
// Handshake
|
// Handshake
|
||||||
@ -25,6 +28,9 @@ pub trait ClientPacketProcessor {
|
|||||||
fn handle_encryption_response(&mut self, encryption_response: SEncryptionResponse);
|
fn handle_encryption_response(&mut self, encryption_response: SEncryptionResponse);
|
||||||
fn handle_plugin_response(&mut self, plugin_response: SLoginPluginResponse);
|
fn handle_plugin_response(&mut self, plugin_response: SLoginPluginResponse);
|
||||||
fn handle_login_acknowledged(&mut self, login_acknowledged: SLoginAcknowledged);
|
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 {
|
impl ClientPacketProcessor for Client {
|
||||||
@ -37,9 +43,15 @@ impl ClientPacketProcessor for Client {
|
|||||||
fn handle_status_request(&mut self, _status_request: SStatusRequest) {
|
fn handle_status_request(&mut self, _status_request: SStatusRequest) {
|
||||||
dbg!("sending status");
|
dbg!("sending status");
|
||||||
|
|
||||||
self.send_packet(CStatusResponse::new(
|
dbg!("test first");
|
||||||
serde_json::to_string(&self.server.status_response).unwrap(),
|
|
||||||
))
|
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) {
|
fn handle_ping_request(&mut self, ping_request: SPingRequest) {
|
||||||
@ -53,11 +65,17 @@ impl ClientPacketProcessor for Client {
|
|||||||
self.name = Some(login_start.name);
|
self.name = Some(login_start.name);
|
||||||
self.uuid = Some(login_start.uuid);
|
self.uuid = Some(login_start.uuid);
|
||||||
let verify_token: [u8; 4] = rand::random();
|
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(
|
let packet = CEncryptionRequest::new(
|
||||||
"".into(),
|
"".into(),
|
||||||
public_key_der.len() as i32,
|
public_key_der.len() as i32,
|
||||||
public_key_der,
|
&public_key_der,
|
||||||
verify_token.len() as i32,
|
verify_token.len() as i32,
|
||||||
&verify_token,
|
&verify_token,
|
||||||
false, // TODO
|
false, // TODO
|
||||||
@ -85,4 +103,25 @@ impl ClientPacketProcessor for Client {
|
|||||||
self.connection_state = ConnectionState::Config;
|
self.connection_state = ConnectionState::Config;
|
||||||
dbg!("login achnowlaged");
|
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,
|
collections::VecDeque,
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
entity::player::{ChatMode, Hand},
|
||||||
protocol::{
|
protocol::{
|
||||||
client::login::CLoginDisconnect,
|
client::{config::CConfigDisconnect, login::CLoginDisconnect},
|
||||||
server::{
|
server::{
|
||||||
|
config::{SAcknowledgeFinishConfig, SClientInformation},
|
||||||
handshake::SHandShake,
|
handshake::SHandShake,
|
||||||
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
|
||||||
status::{SPingRequest, SStatusRequest},
|
status::{SPingRequest, SStatusRequest},
|
||||||
@ -19,14 +22,13 @@ use crate::{
|
|||||||
|
|
||||||
use crate::protocol::ConnectionState;
|
use crate::protocol::ConnectionState;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use mio::{event::Event, net::TcpStream, Registry};
|
use mio::{event::Event, net::TcpStream, Registry, Token};
|
||||||
use packet_decoder::PacketDecoder;
|
use packet_decoder::PacketDecoder;
|
||||||
use packet_encoder::PacketEncoder;
|
use packet_encoder::PacketEncoder;
|
||||||
use rsa::Pkcs1v15Encrypt;
|
use rsa::Pkcs1v15Encrypt;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
mod client_packet;
|
mod client_packet;
|
||||||
pub mod player;
|
|
||||||
|
|
||||||
mod packet_decoder;
|
mod packet_decoder;
|
||||||
mod packet_encoder;
|
mod packet_encoder;
|
||||||
@ -35,13 +37,27 @@ use client_packet::ClientPacketProcessor;
|
|||||||
|
|
||||||
pub const MAX_PACKET_SIZE: i32 = 2097152;
|
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 struct Client {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub uuid: Option<uuid::Uuid>,
|
pub uuid: Option<uuid::Uuid>,
|
||||||
pub server: Rc<Server>,
|
pub config: Option<PlayerConfig>,
|
||||||
|
|
||||||
|
pub server: Arc<Mutex<Server>>,
|
||||||
pub connection_state: ConnectionState,
|
pub connection_state: ConnectionState,
|
||||||
pub encrytion: bool,
|
pub encrytion: bool,
|
||||||
pub closed: bool,
|
pub closed: bool,
|
||||||
|
pub token: Rc<Token>,
|
||||||
pub connection: TcpStream,
|
pub connection: TcpStream,
|
||||||
enc: PacketEncoder,
|
enc: PacketEncoder,
|
||||||
dec: PacketDecoder,
|
dec: PacketDecoder,
|
||||||
@ -49,11 +65,13 @@ pub struct Client {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
Self {
|
||||||
name: None,
|
name: None,
|
||||||
uuid: None,
|
uuid: None,
|
||||||
|
config: None,
|
||||||
server,
|
server,
|
||||||
|
token,
|
||||||
connection_state: ConnectionState::HandShake,
|
connection_state: ConnectionState::HandShake,
|
||||||
connection,
|
connection,
|
||||||
enc: PacketEncoder::default(),
|
enc: PacketEncoder::default(),
|
||||||
@ -72,6 +90,8 @@ impl Client {
|
|||||||
self.encrytion = true;
|
self.encrytion = true;
|
||||||
let shared_secret = self
|
let shared_secret = self
|
||||||
.server
|
.server
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
.private_key
|
.private_key
|
||||||
.decrypt(Pkcs1v15Encrypt, &shared_secret)
|
.decrypt(Pkcs1v15Encrypt, &shared_secret)
|
||||||
.context("failed to decrypt shared secret")?;
|
.context("failed to decrypt shared secret")?;
|
||||||
@ -136,6 +156,18 @@ impl Client {
|
|||||||
packet.id
|
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),
|
_ => log::error!("Invalid Connection state {:?}", self.connection_state),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -184,8 +216,16 @@ impl Client {
|
|||||||
|
|
||||||
pub fn kick(&mut self, reason: String) {
|
pub fn kick(&mut self, reason: String) {
|
||||||
// Todo
|
// Todo
|
||||||
if self.connection_state == ConnectionState::Login {
|
match self.connection_state {
|
||||||
self.send_packet(CLoginDisconnect::new(reason));
|
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()
|
self.close()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, KeyIvInit};
|
use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, KeyIvInit};
|
||||||
use anyhow::{bail, ensure, Context};
|
use anyhow::{bail, ensure, Context};
|
||||||
use bytes::{Buf, BytesMut};
|
use bytes::{Buf, BytesMut};
|
||||||
|
|||||||
@ -37,7 +37,7 @@ impl PacketEncoder {
|
|||||||
let data_len = self.buf.len() - start_len;
|
let data_len = self.buf.len() - start_len;
|
||||||
|
|
||||||
if false { // compression
|
if false { // compression
|
||||||
}
|
}
|
||||||
let packet_len = data_len;
|
let packet_len = data_len;
|
||||||
|
|
||||||
ensure!(
|
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::net::TcpListener;
|
||||||
use mio::{Events, Interest, Poll, Token};
|
use mio::{Events, Interest, Poll, Token};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -9,13 +7,17 @@ use std::io::{self};
|
|||||||
const SERVER: Token = Token(0);
|
const SERVER: Token = Token(0);
|
||||||
|
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
pub mod entity;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod server;
|
pub mod server;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
#[cfg(not(target_os = "wasi"))]
|
#[cfg(not(target_os = "wasi"))]
|
||||||
fn main() -> io::Result<()> {
|
fn main() -> io::Result<()> {
|
||||||
use std::rc::Rc;
|
use std::{
|
||||||
|
rc::Rc,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
use client::{interrupted, Client};
|
use client::{interrupted, Client};
|
||||||
use server::Server;
|
use server::Server;
|
||||||
@ -35,14 +37,12 @@ fn main() -> io::Result<()> {
|
|||||||
poll.registry()
|
poll.registry()
|
||||||
.register(&mut listener, SERVER, Interest::READABLE)?;
|
.register(&mut listener, SERVER, Interest::READABLE)?;
|
||||||
|
|
||||||
// Map of `Token` -> `TcpStream`.
|
|
||||||
let mut connections = HashMap::new();
|
|
||||||
// Unique token for each incoming connection.
|
// Unique token for each incoming connection.
|
||||||
let mut unique_token = Token(SERVER.0 + 1);
|
let mut unique_token = Token(SERVER.0 + 1);
|
||||||
|
|
||||||
log::info!("You now can connect to the server");
|
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 {
|
loop {
|
||||||
if let Err(err) = poll.poll(&mut events, None) {
|
if let Err(err) = poll.poll(&mut events, None) {
|
||||||
@ -82,11 +82,14 @@ fn main() -> io::Result<()> {
|
|||||||
Interest::READABLE.add(Interest::WRITABLE),
|
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 => {
|
token => {
|
||||||
// Maybe received an event for a TCP connection.
|
// 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.poll(poll.registry(), event).unwrap();
|
||||||
client.closed
|
client.closed
|
||||||
} else {
|
} else {
|
||||||
@ -94,7 +97,7 @@ fn main() -> io::Result<()> {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
if done {
|
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)?;
|
poll.registry().deregister(&mut client.connection)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -423,6 +423,12 @@ impl ByteBuffer {
|
|||||||
self.write_bytes_len(val.as_bytes(), max_len);
|
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 operations
|
||||||
|
|
||||||
/// Read a defined amount of raw bytes, or return an IO error if not enough bytes are
|
/// 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
|
// Clientbound Packets
|
||||||
// Server -> Client
|
// Server -> Client
|
||||||
//
|
//
|
||||||
|
pub mod config;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
|
pub mod play;
|
||||||
pub mod status;
|
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 {
|
pub enum ConnectionState {
|
||||||
HandShake,
|
HandShake,
|
||||||
Status,
|
Status,
|
||||||
Login,
|
Login,
|
||||||
Transfer,
|
Transfer,
|
||||||
Config,
|
Config,
|
||||||
|
Play,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConnectionState {
|
impl ConnectionState {
|
||||||
@ -102,7 +103,10 @@ impl ConnectionState {
|
|||||||
1 => Self::Status,
|
1 => Self::Status,
|
||||||
2 => Self::Login,
|
2 => Self::Login,
|
||||||
3 => Self::Transfer,
|
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 struct Players {
|
||||||
pub max: u32,
|
pub max: u32,
|
||||||
pub online: u32,
|
pub online: u32,
|
||||||
pub sample: Sample,
|
pub sample: Vec<Sample>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[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
|
// Serverbound Packets
|
||||||
// Client -> Server
|
// Client -> Server
|
||||||
|
pub mod config;
|
||||||
pub mod handshake;
|
pub mod handshake;
|
||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod status;
|
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 base64::{engine::general_purpose, Engine};
|
||||||
|
use mio::{net::TcpStream, Token};
|
||||||
use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
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 struct Server {
|
||||||
pub compression_threshold: Option<u8>,
|
pub compression_threshold: Option<u8>,
|
||||||
|
|
||||||
pub online_mode: bool,
|
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 public_key: RsaPublicKey,
|
||||||
pub private_key: RsaPrivateKey,
|
pub private_key: RsaPrivateKey,
|
||||||
pub public_key_der: Box<[u8]>,
|
pub public_key_der: Box<[u8]>,
|
||||||
|
|
||||||
|
pub max_players: u32,
|
||||||
|
|
||||||
pub status_response: StatusResponse,
|
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 {
|
impl Default for Server {
|
||||||
@ -25,7 +49,8 @@ impl Default for Server {
|
|||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new() -> Self {
|
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
|
// todo, only create when needed
|
||||||
let (public_key, private_key) = Self::generate_keys();
|
let (public_key, private_key) = Self::generate_keys();
|
||||||
@ -37,17 +62,66 @@ impl Server {
|
|||||||
.into_boxed_slice();
|
.into_boxed_slice();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
// 0 is invalid
|
||||||
|
entity_id: 2.into(),
|
||||||
online_mode: true,
|
online_mode: true,
|
||||||
encriyption: true,
|
encryption: true,
|
||||||
compression_threshold: None, // 256
|
compression_threshold: None, // 256
|
||||||
public_key,
|
public_key,
|
||||||
private_key,
|
private_key,
|
||||||
|
max_players,
|
||||||
status_response,
|
status_response,
|
||||||
public_key_der,
|
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");
|
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/icon.png");
|
||||||
|
|
||||||
StatusResponse {
|
StatusResponse {
|
||||||
@ -56,12 +130,12 @@ impl Server {
|
|||||||
protocol: 767,
|
protocol: 767,
|
||||||
},
|
},
|
||||||
players: Players {
|
players: Players {
|
||||||
max: 20,
|
max: max_players,
|
||||||
online: 0,
|
online: 0,
|
||||||
sample: Sample {
|
sample: vec![Sample {
|
||||||
name: "".into(),
|
name: "".into(),
|
||||||
id: "".into(),
|
id: "".into(),
|
||||||
},
|
}],
|
||||||
},
|
},
|
||||||
description: "Pumpkin Server".into(),
|
description: "Pumpkin Server".into(),
|
||||||
favicon: Self::load_icon(path),
|
favicon: Self::load_icon(path),
|
||||||
@ -88,3 +162,10 @@ impl Server {
|
|||||||
(pub_key, priv_key)
|
(pub_key, priv_key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub enum Difficulty {
|
||||||
|
Easy,
|
||||||
|
Normal,
|
||||||
|
Hard,
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user