mirror of
https://github.com/Pumpkin-MC/Pumpkin.git
synced 2026-08-30 20:14:23 +00:00
Merge pull request #68 from DaniD3v/master
Move config to lazy_lock and into own crate
This commit is contained in:
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -1906,6 +1906,7 @@ dependencies = [
|
||||
"num-bigint",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"pumpkin-config",
|
||||
"pumpkin-core",
|
||||
"pumpkin-entity",
|
||||
"pumpkin-inventory",
|
||||
@@ -1929,6 +1930,16 @@ dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-config"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pumpkin-core",
|
||||
"serde",
|
||||
"toml 0.8.19",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pumpkin-core"
|
||||
version = "0.1.0"
|
||||
@@ -1936,6 +1947,8 @@ dependencies = [
|
||||
"colored",
|
||||
"fastnbt",
|
||||
"md5",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"serde",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = [ "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"]
|
||||
members = [ "pumpkin-config", "pumpkin-core", "pumpkin-entity", "pumpkin-inventory", "pumpkin-macros/", "pumpkin-plugin", "pumpkin-protocol/", "pumpkin-registry/", "pumpkin-world", "pumpkin/"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
|
||||
12
pumpkin-config/Cargo.toml
Normal file
12
pumpkin-config/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "pumpkin-config"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
pumpkin-core = { path = "../pumpkin-core" }
|
||||
|
||||
serde = "1.0"
|
||||
toml = "0.8"
|
||||
|
||||
log.workspace = true
|
||||
@@ -1,7 +1,6 @@
|
||||
use pumpkin_core::ProfileAction;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::client::authentication::ProfileAction;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct AuthenticationConfig {
|
||||
/// Whether to use Mojang authentication.
|
||||
14
pumpkin-config/src/commands.rs
Normal file
14
pumpkin-config/src/commands.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CommandsConfig {
|
||||
/// Are commands from the Console accepted ?
|
||||
pub use_console: bool,
|
||||
// TODO: commands...
|
||||
}
|
||||
|
||||
impl Default for CommandsConfig {
|
||||
fn default() -> Self {
|
||||
Self { use_console: true }
|
||||
}
|
||||
}
|
||||
24
pumpkin-config/src/compression.rs
Normal file
24
pumpkin-config/src/compression.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
// Packet compression
|
||||
pub struct CompressionConfig {
|
||||
/// Is compression enabled ?
|
||||
pub enabled: bool,
|
||||
/// The compression threshold used when compression is enabled
|
||||
pub compression_threshold: u32,
|
||||
/// A value between 0..9
|
||||
/// 1 = Optimize for the best speed of encoding.
|
||||
/// 9 = Optimize for the size of data being encoded.
|
||||
pub compression_level: u32,
|
||||
}
|
||||
|
||||
impl Default for CompressionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
compression_threshold: 256,
|
||||
compression_level: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
176
pumpkin-config/src/lib.rs
Normal file
176
pumpkin-config/src/lib.rs
Normal file
@@ -0,0 +1,176 @@
|
||||
use log::warn;
|
||||
use pumpkin_core::{Difficulty, GameMode};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
use std::{
|
||||
fs,
|
||||
net::{Ipv4Addr, SocketAddr},
|
||||
path::Path,
|
||||
sync::LazyLock,
|
||||
};
|
||||
|
||||
pub mod auth;
|
||||
pub mod proxy;
|
||||
pub mod resource_pack;
|
||||
|
||||
pub use auth::AuthenticationConfig;
|
||||
pub use commands::CommandsConfig;
|
||||
pub use compression::CompressionConfig;
|
||||
pub use pvp::PVPConfig;
|
||||
pub use rcon::RCONConfig;
|
||||
|
||||
mod commands;
|
||||
mod compression;
|
||||
mod pvp;
|
||||
mod rcon;
|
||||
|
||||
use proxy::ProxyConfig;
|
||||
use resource_pack::ResourcePackConfig;
|
||||
|
||||
/// Current Config version of the Base Config
|
||||
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
||||
|
||||
pub static ADVANCED_CONFIG: LazyLock<AdvancedConfiguration> =
|
||||
LazyLock::new(AdvancedConfiguration::load);
|
||||
|
||||
pub static BASIC_CONFIG: LazyLock<BasicConfiguration> = LazyLock::new(BasicConfiguration::load);
|
||||
|
||||
/// The idea is that Pumpkin should very customizable.
|
||||
/// You can Enable or Disable Features depending on your needs.
|
||||
///
|
||||
/// This also allows you get some Performance or Resource boosts.
|
||||
/// Important: The Configuration should match Vanilla by default
|
||||
#[derive(Deserialize, Serialize, Default)]
|
||||
pub struct AdvancedConfiguration {
|
||||
pub proxy: ProxyConfig,
|
||||
pub authentication: AuthenticationConfig,
|
||||
pub packet_compression: CompressionConfig,
|
||||
pub resource_pack: ResourcePackConfig,
|
||||
pub commands: CommandsConfig,
|
||||
pub rcon: RCONConfig,
|
||||
pub pvp: PVPConfig,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BasicConfiguration {
|
||||
/// A version identifier for the configuration format.
|
||||
pub config_version: String,
|
||||
/// The address to bind the server to.
|
||||
pub server_address: SocketAddr,
|
||||
/// The seed for world generation.
|
||||
pub seed: String,
|
||||
/// The maximum number of players allowed on the server.
|
||||
pub max_players: u32,
|
||||
/// The maximum view distance for players.
|
||||
pub view_distance: u8,
|
||||
/// The maximum simulated view distance.
|
||||
pub simulation_distance: u8,
|
||||
/// The default game difficulty.
|
||||
pub default_difficulty: Difficulty,
|
||||
/// Whether the Nether dimension is enabled.
|
||||
pub allow_nether: bool,
|
||||
/// Whether the server is in hardcore mode.
|
||||
pub hardcore: bool,
|
||||
/// Whether online mode is enabled. Requires valid Minecraft accounts.
|
||||
pub online_mode: bool,
|
||||
/// Whether packet encryption is enabled. Required when online mode is enabled.
|
||||
pub encryption: bool,
|
||||
/// The server's description displayed on the status screen.
|
||||
pub motd: String,
|
||||
/// The default game mode for players.
|
||||
pub default_gamemode: GameMode,
|
||||
}
|
||||
|
||||
impl Default for BasicConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config_version: CURRENT_BASE_VERSION.to_string(),
|
||||
server_address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25565),
|
||||
seed: "".to_string(),
|
||||
max_players: 100000,
|
||||
view_distance: 10,
|
||||
simulation_distance: 10,
|
||||
default_difficulty: Difficulty::Normal,
|
||||
allow_nether: true,
|
||||
hardcore: false,
|
||||
online_mode: true,
|
||||
encryption: true,
|
||||
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
||||
default_gamemode: GameMode::Survival,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait LoadConfiguration {
|
||||
fn load() -> Self
|
||||
where
|
||||
Self: Sized + Default + Serialize + DeserializeOwned,
|
||||
{
|
||||
let path = Self::get_path();
|
||||
|
||||
let config = if path.exists() {
|
||||
let file_content = fs::read_to_string(path)
|
||||
.unwrap_or_else(|_| panic!("Couldn't read configuration file at {:?}", path));
|
||||
|
||||
toml::from_str(&file_content).unwrap_or_else(|err| {
|
||||
panic!(
|
||||
"Couldn't parse config at {:?}. Reason: {}",
|
||||
path,
|
||||
err.message()
|
||||
)
|
||||
})
|
||||
} else {
|
||||
let content = Self::default();
|
||||
|
||||
if let Err(err) = fs::write(path, toml::to_string(&content).unwrap()) {
|
||||
warn!(
|
||||
"Couldn't write default config to {:?}. Reason: {}",
|
||||
path, err
|
||||
);
|
||||
}
|
||||
|
||||
content
|
||||
};
|
||||
|
||||
config.validate();
|
||||
config
|
||||
}
|
||||
|
||||
fn get_path() -> &'static Path;
|
||||
|
||||
fn validate(&self);
|
||||
}
|
||||
|
||||
impl LoadConfiguration for AdvancedConfiguration {
|
||||
fn get_path() -> &'static Path {
|
||||
Path::new("features.toml")
|
||||
}
|
||||
|
||||
fn validate(&self) {
|
||||
self.resource_pack.validate()
|
||||
}
|
||||
}
|
||||
|
||||
impl LoadConfiguration for BasicConfiguration {
|
||||
fn get_path() -> &'static Path {
|
||||
Path::new("configuration.toml")
|
||||
}
|
||||
|
||||
fn validate(&self) {
|
||||
assert_eq!(
|
||||
self.config_version, CURRENT_BASE_VERSION,
|
||||
"Config version does not match used Config version. Please update your config"
|
||||
);
|
||||
assert!(self.view_distance >= 2, "View distance must be at least 2");
|
||||
assert!(
|
||||
self.view_distance <= 32,
|
||||
"View distance must be less than 32"
|
||||
);
|
||||
if self.online_mode {
|
||||
assert!(
|
||||
self.encryption,
|
||||
"When Online Mode is enabled, Encryption must be enabled"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
27
pumpkin-config/src/pvp.rs
Normal file
27
pumpkin-config/src/pvp.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct PVPConfig {
|
||||
/// Is PVP enabled ?
|
||||
pub enabled: bool,
|
||||
/// Do we want to have the Red hurt animation & fov bobbing
|
||||
pub hurt_animation: bool,
|
||||
/// Should players in creative be protected against PVP
|
||||
pub protect_creative: bool,
|
||||
/// Has PVP Knockback?
|
||||
pub knockback: bool,
|
||||
/// Should player swing when attacking?
|
||||
pub swing: bool,
|
||||
}
|
||||
|
||||
impl Default for PVPConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
hurt_animation: true,
|
||||
protect_creative: true,
|
||||
knockback: true,
|
||||
swing: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
20
pumpkin-config/src/rcon.rs
Normal file
20
pumpkin-config/src/rcon.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
use std::net::{Ipv4Addr, SocketAddr};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct RCONConfig {
|
||||
pub enabled: bool,
|
||||
pub address: SocketAddr,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
impl Default for RCONConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
address: SocketAddr::new(Ipv4Addr::new(0, 0, 0, 0).into(), 25575),
|
||||
password: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,3 +9,6 @@ fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
|
||||
uuid.workspace = true
|
||||
colored = "2"
|
||||
md5 = "0.7.0"
|
||||
|
||||
num-traits = "0.2.19"
|
||||
num-derive = { version = "0.4.2" }
|
||||
|
||||
30
pumpkin-core/src/gamemode.rs
Normal file
30
pumpkin-core/src/gamemode.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ParseGameModeError;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
|
||||
pub enum GameMode {
|
||||
Undefined = -1,
|
||||
Survival,
|
||||
Creative,
|
||||
Adventure,
|
||||
Spectator,
|
||||
}
|
||||
|
||||
impl FromStr for GameMode {
|
||||
type Err = ParseGameModeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"survival" => Ok(Self::Survival),
|
||||
"creative" => Ok(Self::Creative),
|
||||
"adventure" => Ok(Self::Adventure),
|
||||
"spectator" => Ok(Self::Spectator),
|
||||
_ => Err(ParseGameModeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,22 @@
|
||||
pub mod gamemode;
|
||||
pub mod random;
|
||||
pub mod text;
|
||||
|
||||
pub use gamemode::GameMode;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub enum Difficulty {
|
||||
Peaceful,
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
|
||||
pub enum ProfileAction {
|
||||
ForcedNameChange,
|
||||
UsingBannedSkin,
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ default = []
|
||||
[dependencies]
|
||||
# pumpkin
|
||||
pumpkin-core = { path = "../pumpkin-core"}
|
||||
pumpkin-config = { path = "../pumpkin-config" }
|
||||
pumpkin-plugin = { path = "../pumpkin-plugin"}
|
||||
pumpkin-inventory = { path = "../pumpkin-inventory"}
|
||||
pumpkin-world = { path = "../pumpkin-world"}
|
||||
|
||||
@@ -2,13 +2,15 @@ use std::{collections::HashMap, net::IpAddr};
|
||||
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use num_bigint::BigInt;
|
||||
use pumpkin_config::{auth::TextureConfig, ADVANCED_CONFIG};
|
||||
use pumpkin_core::ProfileAction;
|
||||
use pumpkin_protocol::Property;
|
||||
use reqwest::{StatusCode, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::Deserialize;
|
||||
use thiserror::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{config::auth_config::TextureConfig, server::Server};
|
||||
use crate::server::Server;
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
#[allow(non_snake_case)]
|
||||
@@ -29,14 +31,6 @@ pub struct Texture {
|
||||
metadata: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ProfileAction {
|
||||
#[serde(rename = "FORCED_NAME_CHANGE")]
|
||||
ForcedNameChange,
|
||||
#[serde(rename = "USING_BANNED_SKIN")]
|
||||
UsingBannedSkin,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Clone, Debug)]
|
||||
pub struct GameProfile {
|
||||
pub id: Uuid,
|
||||
@@ -52,13 +46,9 @@ pub async fn authenticate(
|
||||
ip: &IpAddr,
|
||||
server: &mut Server,
|
||||
) -> Result<GameProfile, AuthError> {
|
||||
assert!(server.advanced_config.authentication.enabled);
|
||||
assert!(ADVANCED_CONFIG.authentication.enabled);
|
||||
assert!(server.auth_client.is_some());
|
||||
let address = if server
|
||||
.advanced_config
|
||||
.authentication
|
||||
.prevent_proxy_connections
|
||||
{
|
||||
let address = if ADVANCED_CONFIG.authentication.prevent_proxy_connections {
|
||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}&ip={ip}")
|
||||
} else {
|
||||
format!("https://sessionserver.mojang.com/session/minecraft/hasJoined?username={username}&serverId={server_hash}")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_protocol::{
|
||||
client::{
|
||||
@@ -81,7 +82,7 @@ impl Client {
|
||||
properties: vec![],
|
||||
profile_actions: None,
|
||||
});
|
||||
let proxy = &server.advanced_config.proxy;
|
||||
let proxy = &ADVANCED_CONFIG.proxy;
|
||||
if proxy.enabled {
|
||||
if proxy.velocity.enabled {
|
||||
velocity_login(self)
|
||||
@@ -96,7 +97,7 @@ impl Client {
|
||||
"",
|
||||
public_key_der,
|
||||
&verify_token,
|
||||
server.base_config.online_mode, // TODO
|
||||
BASIC_CONFIG.online_mode, // TODO
|
||||
);
|
||||
self.send_packet(&packet);
|
||||
}
|
||||
@@ -114,7 +115,7 @@ impl Client {
|
||||
self.enable_encryption(&shared_secret)
|
||||
.unwrap_or_else(|e| self.kick(&e.to_string()));
|
||||
|
||||
if server.base_config.online_mode {
|
||||
if BASIC_CONFIG.online_mode {
|
||||
let hash = Sha1::new()
|
||||
.chain_update(&shared_secret)
|
||||
.chain_update(&server.public_key_der)
|
||||
@@ -132,8 +133,7 @@ impl Client {
|
||||
Ok(p) => {
|
||||
// Check if player should join
|
||||
if let Some(p) = &p.profile_actions {
|
||||
if !server
|
||||
.advanced_config
|
||||
if !ADVANCED_CONFIG
|
||||
.authentication
|
||||
.player_profile
|
||||
.allow_banned_players
|
||||
@@ -142,8 +142,7 @@ impl Client {
|
||||
self.kick("Your account can't join");
|
||||
}
|
||||
} else {
|
||||
for allowed in server
|
||||
.advanced_config
|
||||
for allowed in ADVANCED_CONFIG
|
||||
.authentication
|
||||
.player_profile
|
||||
.allowed_actions
|
||||
@@ -162,16 +161,13 @@ impl Client {
|
||||
}
|
||||
for ele in self.gameprofile.as_ref().unwrap().properties.clone() {
|
||||
// todo, use this
|
||||
unpack_textures(ele, &server.advanced_config.authentication.textures);
|
||||
unpack_textures(ele, &ADVANCED_CONFIG.authentication.textures);
|
||||
}
|
||||
|
||||
// enable compression
|
||||
if server.advanced_config.packet_compression.enabled {
|
||||
let threshold = server
|
||||
.advanced_config
|
||||
.packet_compression
|
||||
.compression_threshold;
|
||||
let level = server.advanced_config.packet_compression.compression_level;
|
||||
if ADVANCED_CONFIG.packet_compression.enabled {
|
||||
let threshold = ADVANCED_CONFIG.packet_compression.compression_threshold;
|
||||
let level = ADVANCED_CONFIG.packet_compression.compression_level;
|
||||
self.send_packet(&CSetCompression::new(threshold.into()));
|
||||
self.set_compression(Some((threshold, level)));
|
||||
}
|
||||
@@ -199,7 +195,7 @@ impl Client {
|
||||
self.connection_state = ConnectionState::Config;
|
||||
server.send_brand(self);
|
||||
|
||||
let resource_config = &server.advanced_config.resource_pack;
|
||||
let resource_config = &ADVANCED_CONFIG.resource_pack;
|
||||
if resource_config.enabled {
|
||||
let prompt_message = if resource_config.prompt_message.is_empty() {
|
||||
None
|
||||
|
||||
@@ -2,12 +2,13 @@ use std::f32::consts::PI;
|
||||
|
||||
use crate::{
|
||||
commands::{handle_command, CommandSender},
|
||||
entity::player::{ChatMode, GameMode, Hand, Player},
|
||||
entity::player::{ChatMode, Hand, Player},
|
||||
server::Server,
|
||||
util::math::wrap_degrees,
|
||||
};
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_config::ADVANCED_CONFIG;
|
||||
use pumpkin_core::{text::TextComponent, GameMode};
|
||||
use pumpkin_entity::EntityId;
|
||||
use pumpkin_inventory::WindowType;
|
||||
use pumpkin_protocol::server::play::{SCloseContainer, SSetPlayerGround, SUseItem};
|
||||
@@ -313,7 +314,7 @@ impl Player {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_interact(&mut self, server: &mut Server, interact: SInteract) {
|
||||
pub async fn handle_interact(&mut self, _: &mut Server, interact: SInteract) {
|
||||
let sneaking = interact.sneaking;
|
||||
if self.sneaking != sneaking {
|
||||
self.set_sneaking(sneaking).await;
|
||||
@@ -323,7 +324,7 @@ impl Player {
|
||||
ActionType::Attack => {
|
||||
let entity_id = interact.entity_id;
|
||||
// TODO: do validation and stuff
|
||||
let config = &server.advanced_config.pvp;
|
||||
let config = &ADVANCED_CONFIG.pvp;
|
||||
if config.enabled {
|
||||
let world = self.world.clone();
|
||||
let world = world.lock().await;
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::str::FromStr;
|
||||
|
||||
use num_traits::FromPrimitive;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_core::GameMode;
|
||||
|
||||
use crate::commands::arg_player::{consume_arg_player, parse_arg_player};
|
||||
|
||||
@@ -13,7 +14,6 @@ use crate::commands::tree::{CommandTree, ConsumedArgs, RawArgs};
|
||||
use crate::commands::tree_builder::{argument, require};
|
||||
use crate::commands::CommandSender;
|
||||
use crate::commands::CommandSender::Player;
|
||||
use crate::entity::player::GameMode;
|
||||
|
||||
const NAMES: [&str; 1] = ["gamemode"];
|
||||
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
use auth_config::AuthenticationConfig;
|
||||
use proxy::ProxyConfig;
|
||||
use resource_pack::ResourcePackConfig;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{entity::player::GameMode, server::Difficulty};
|
||||
|
||||
pub mod auth_config;
|
||||
pub mod proxy;
|
||||
pub mod resource_pack;
|
||||
|
||||
/// Current Config version of the Base Config
|
||||
const CURRENT_BASE_VERSION: &str = "1.0.0";
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
/// The idea is that Pumpkin should very customizable, You can Enable or Disable Features depending on your needs.
|
||||
/// This also allows you get some Performance or Resource boosts.
|
||||
/// Important: The Configuration should match Vanilla by default
|
||||
pub struct AdvancedConfiguration {
|
||||
pub proxy: ProxyConfig,
|
||||
pub authentication: AuthenticationConfig,
|
||||
pub packet_compression: CompressionConfig,
|
||||
pub resource_pack: ResourcePackConfig,
|
||||
pub commands: CommandsConfig,
|
||||
pub rcon: RCONConfig,
|
||||
pub pvp: PVPConfig,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct RCONConfig {
|
||||
pub enabled: bool,
|
||||
pub ip: String,
|
||||
pub port: u16,
|
||||
pub password: String,
|
||||
}
|
||||
|
||||
impl Default for RCONConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: false,
|
||||
ip: "0.0.0.0".to_string(),
|
||||
port: 25575,
|
||||
password: "".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct CommandsConfig {
|
||||
/// Are commands from the Console accepted ?
|
||||
pub use_console: bool,
|
||||
// TODO: commands...
|
||||
}
|
||||
|
||||
impl Default for CommandsConfig {
|
||||
fn default() -> Self {
|
||||
Self { use_console: true }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct PVPConfig {
|
||||
/// Is PVP enabled ?
|
||||
pub enabled: bool,
|
||||
/// Do we want to have the Red hurt animation & fov bobbing
|
||||
pub hurt_animation: bool,
|
||||
/// Should players in creative be protected against PVP
|
||||
pub protect_creative: bool,
|
||||
/// Has PVP Knockback?
|
||||
pub knockback: bool,
|
||||
/// Should player swing when attacking?
|
||||
pub swing: bool,
|
||||
}
|
||||
|
||||
impl Default for PVPConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
hurt_animation: true,
|
||||
protect_creative: true,
|
||||
knockback: true,
|
||||
swing: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
// Packet compression
|
||||
pub struct CompressionConfig {
|
||||
/// Is compression enabled ?
|
||||
pub enabled: bool,
|
||||
/// The compression threshold used when compression is enabled
|
||||
pub compression_threshold: u32,
|
||||
/// A value between 0..9
|
||||
/// 1 = Optimize for the best speed of encoding.
|
||||
/// 9 = Optimize for the size of data being encoded.
|
||||
pub compression_level: u32,
|
||||
}
|
||||
|
||||
impl Default for CompressionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
enabled: true,
|
||||
compression_threshold: 256,
|
||||
compression_level: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Important: The Configuration should match Vanilla by default
|
||||
impl Default for AdvancedConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
proxy: ProxyConfig::default(),
|
||||
authentication: AuthenticationConfig::default(),
|
||||
commands: CommandsConfig::default(),
|
||||
packet_compression: CompressionConfig::default(),
|
||||
resource_pack: ResourcePackConfig::default(),
|
||||
rcon: RCONConfig::default(),
|
||||
pvp: PVPConfig::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct BasicConfiguration {
|
||||
/// A version identifier for the configuration format.
|
||||
pub config_version: String,
|
||||
/// The address to bind the server to.
|
||||
pub server_address: String,
|
||||
/// The port to listen on.
|
||||
pub server_port: u16,
|
||||
/// The seed for world generation.
|
||||
pub seed: String,
|
||||
/// The maximum number of players allowed on the server.
|
||||
pub max_players: u32,
|
||||
/// The maximum view distance for players.
|
||||
pub view_distance: u8,
|
||||
/// The maximum simulated view distance.
|
||||
pub simulation_distance: u8,
|
||||
/// The default game difficulty.
|
||||
pub default_difficulty: Difficulty,
|
||||
/// Whether the Nether dimension is enabled.
|
||||
pub allow_nether: bool,
|
||||
/// Whether the server is in hardcore mode.
|
||||
pub hardcore: bool,
|
||||
/// Whether online mode is enabled. Requires valid Minecraft accounts.
|
||||
pub online_mode: bool,
|
||||
/// Whether packet encryption is enabled. Required when online mode is enabled.
|
||||
pub encryption: bool,
|
||||
/// The server's description displayed on the status screen.
|
||||
pub motd: String,
|
||||
/// The default game mode for players.
|
||||
pub default_gamemode: GameMode,
|
||||
}
|
||||
|
||||
impl Default for BasicConfiguration {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config_version: CURRENT_BASE_VERSION.to_string(),
|
||||
server_address: "0.0.0.0".to_string(),
|
||||
server_port: 25565,
|
||||
seed: "".to_string(),
|
||||
max_players: 100000,
|
||||
view_distance: 10,
|
||||
simulation_distance: 10,
|
||||
default_difficulty: Difficulty::Normal,
|
||||
allow_nether: true,
|
||||
hardcore: false,
|
||||
online_mode: true,
|
||||
encryption: true,
|
||||
motd: "A Blazing fast Pumpkin Server!".to_string(),
|
||||
default_gamemode: GameMode::Survival,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AdvancedConfiguration {
|
||||
pub fn load<P: AsRef<Path>>(path: P) -> AdvancedConfiguration {
|
||||
if path.as_ref().exists() {
|
||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
||||
let config: AdvancedConfiguration =
|
||||
toml::from_str(toml.as_str()).expect("Couldn't parse features.toml, Probably old config, Replacing with a new one or just delete it");
|
||||
config.validate();
|
||||
config
|
||||
} else {
|
||||
let config = AdvancedConfiguration::default();
|
||||
let toml = toml::to_string(&config).expect("Couldn't create toml!");
|
||||
std::fs::write(path, toml).expect("Couldn't save configuration");
|
||||
config.validate();
|
||||
config
|
||||
}
|
||||
}
|
||||
pub fn validate(&self) {
|
||||
self.resource_pack.validate()
|
||||
}
|
||||
}
|
||||
|
||||
impl BasicConfiguration {
|
||||
pub fn load<P: AsRef<Path>>(path: P) -> BasicConfiguration {
|
||||
if path.as_ref().exists() {
|
||||
let toml = std::fs::read_to_string(path).expect("Couldn't read configuration");
|
||||
let config: BasicConfiguration = toml::from_str(toml.as_str()).expect("Couldn't parse configuration.toml, Probably old config, Replacing with a new one or just delete it");
|
||||
config.validate();
|
||||
config
|
||||
} else {
|
||||
let config = BasicConfiguration::default();
|
||||
let toml = toml::to_string(&config).expect("Couldn't create toml!");
|
||||
std::fs::write(path, toml).expect("Couldn't save configuration");
|
||||
config.validate();
|
||||
config
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate(&self) {
|
||||
assert_eq!(
|
||||
self.config_version, CURRENT_BASE_VERSION,
|
||||
"Config version does not match used Config version. Please update your config"
|
||||
);
|
||||
assert!(self.view_distance >= 2, "View distance must be at least 2");
|
||||
assert!(
|
||||
self.view_distance <= 32,
|
||||
"View distance must be less than 32"
|
||||
);
|
||||
if self.online_mode {
|
||||
assert!(
|
||||
self.encryption,
|
||||
"When Online Mode is enabled, Encryption must be enabled"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::{str::FromStr, sync::Arc};
|
||||
use std::sync::Arc;
|
||||
|
||||
use num_derive::{FromPrimitive, ToPrimitive};
|
||||
use num_derive::FromPrimitive;
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_core::text::TextComponent;
|
||||
use pumpkin_core::{text::TextComponent, GameMode};
|
||||
use pumpkin_entity::{entity_type::EntityType, pose::EntityPose, Entity, EntityId};
|
||||
use pumpkin_inventory::player::PlayerInventory;
|
||||
use pumpkin_protocol::{
|
||||
@@ -21,7 +21,6 @@ use pumpkin_protocol::{
|
||||
ConnectionState, RawPacket, ServerPacket, VarInt,
|
||||
};
|
||||
use pumpkin_world::vector3::Vector3;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
client::{authentication::GameProfile, Client},
|
||||
@@ -454,29 +453,3 @@ pub enum ChatMode {
|
||||
CommandsOnly,
|
||||
Hidden,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize, FromPrimitive, ToPrimitive)]
|
||||
pub enum GameMode {
|
||||
Undefined = -1,
|
||||
Survival,
|
||||
Creative,
|
||||
Adventure,
|
||||
Spectator,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct ParseGameModeError;
|
||||
|
||||
impl FromStr for GameMode {
|
||||
type Err = ParseGameModeError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"survival" => Ok(Self::Survival),
|
||||
"creative" => Ok(Self::Creative),
|
||||
"adventure" => Ok(Self::Adventure),
|
||||
"spectator" => Ok(Self::Spectator),
|
||||
_ => Err(ParseGameModeError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,23 +6,18 @@ compile_error!("Compiling for WASI targets is not supported!");
|
||||
|
||||
use mio::net::TcpListener;
|
||||
use mio::{Events, Interest, Poll, Token};
|
||||
use std::io::{self};
|
||||
|
||||
use client::Client;
|
||||
use commands::handle_command;
|
||||
use config::AdvancedConfiguration;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::io::{self};
|
||||
|
||||
use client::interrupted;
|
||||
use config::BasicConfiguration;
|
||||
use client::{interrupted, Client};
|
||||
use commands::handle_command;
|
||||
use server::Server;
|
||||
|
||||
// Setup some tokens to allow us to identify which event is for which socket.
|
||||
|
||||
pub mod client;
|
||||
pub mod commands;
|
||||
pub mod config;
|
||||
pub mod entity;
|
||||
pub mod proxy;
|
||||
pub mod rcon;
|
||||
@@ -34,6 +29,7 @@ fn main() -> io::Result<()> {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use entity::player::Player;
|
||||
use pumpkin_config::{ADVANCED_CONFIG, BASIC_CONFIG};
|
||||
use pumpkin_core::text::{color::NamedColor, TextComponent};
|
||||
use rcon::RCONServer;
|
||||
|
||||
@@ -64,9 +60,6 @@ fn main() -> io::Result<()> {
|
||||
use std::time::Instant;
|
||||
|
||||
let time = Instant::now();
|
||||
let basic_config = BasicConfiguration::load("configuration.toml");
|
||||
|
||||
let advanced_configuration = AdvancedConfiguration::load("features.toml");
|
||||
|
||||
// Create a poll instance.
|
||||
let mut poll = Poll::new()?;
|
||||
@@ -74,14 +67,7 @@ fn main() -> io::Result<()> {
|
||||
let mut events = Events::with_capacity(128);
|
||||
|
||||
// Setup the TCP server socket.
|
||||
|
||||
let addr = format!(
|
||||
"{}:{}",
|
||||
basic_config.server_address, basic_config.server_port
|
||||
)
|
||||
.parse()
|
||||
.unwrap();
|
||||
|
||||
let addr = BASIC_CONFIG.server_address;
|
||||
let mut listener = TcpListener::bind(addr)?;
|
||||
|
||||
// Register the server with poll we can receive events for it.
|
||||
@@ -91,16 +77,13 @@ fn main() -> io::Result<()> {
|
||||
// Unique token for each incoming connection.
|
||||
let mut unique_token = Token(SERVER.0 + 1);
|
||||
|
||||
let use_console = advanced_configuration.commands.use_console;
|
||||
let rcon = advanced_configuration.rcon.clone();
|
||||
let use_console = ADVANCED_CONFIG.commands.use_console;
|
||||
let rcon = ADVANCED_CONFIG.rcon.clone();
|
||||
|
||||
let mut clients: HashMap<Token, Client> = HashMap::new();
|
||||
let mut players: HashMap<Arc<Token>, Arc<Mutex<Player>>> = HashMap::new();
|
||||
|
||||
let server = Arc::new(tokio::sync::Mutex::new(Server::new((
|
||||
basic_config,
|
||||
advanced_configuration,
|
||||
))));
|
||||
let server = Arc::new(tokio::sync::Mutex::new(Server::new()));
|
||||
log::info!("Started Server took {}ms", time.elapsed().as_millis());
|
||||
log::info!("You now can connect to the server, Listening on {}", addr);
|
||||
|
||||
@@ -214,7 +197,7 @@ fn main() -> io::Result<()> {
|
||||
server.add_player(token.clone(), client).await;
|
||||
players.insert(token, player.clone());
|
||||
let mut world = world.lock().await;
|
||||
world.spawn_player(&server.base_config, player).await;
|
||||
world.spawn_player(&BASIC_CONFIG, player).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,12 +2,13 @@ use std::net::SocketAddr;
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use hmac::{Hmac, Mac};
|
||||
use pumpkin_config::proxy::VelocityConfig;
|
||||
use pumpkin_protocol::{
|
||||
bytebuf::ByteBuffer, client::login::CLoginPluginRequest, server::login::SLoginPluginResponse,
|
||||
};
|
||||
use sha2::Sha256;
|
||||
|
||||
use crate::{client::Client, config::proxy::VelocityConfig};
|
||||
use crate::client::Client;
|
||||
|
||||
type HmacSha256 = Hmac<Sha256>;
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ use mio::{
|
||||
Events, Interest, Poll, Token,
|
||||
};
|
||||
use packet::{Packet, PacketError, PacketType};
|
||||
use pumpkin_config::RCONConfig;
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::{commands::handle_command, config::RCONConfig, server::Server};
|
||||
use crate::{commands::handle_command, server::Server};
|
||||
|
||||
mod packet;
|
||||
|
||||
@@ -35,11 +36,8 @@ impl RCONServer {
|
||||
server: Arc<tokio::sync::Mutex<Server>>,
|
||||
) -> Result<Self, io::Error> {
|
||||
assert!(config.enabled, "RCON is not enabled");
|
||||
let addr = format!("{}:{}", config.ip, config.port)
|
||||
.parse()
|
||||
.expect("Failed to parse RCON address");
|
||||
let mut poll = Poll::new().unwrap();
|
||||
let mut listener = TcpListener::bind(addr).unwrap();
|
||||
let mut listener = TcpListener::bind(config.address).unwrap();
|
||||
|
||||
poll.registry()
|
||||
.register(&mut listener, SERVER, Interest::READABLE)
|
||||
|
||||
@@ -11,6 +11,8 @@ use std::{
|
||||
use base64::{engine::general_purpose, Engine};
|
||||
use image::GenericImageView;
|
||||
use mio::Token;
|
||||
use pumpkin_config::{BasicConfiguration, BASIC_CONFIG};
|
||||
use pumpkin_core::GameMode;
|
||||
use pumpkin_entity::EntityId;
|
||||
use pumpkin_plugin::PluginLoader;
|
||||
use pumpkin_protocol::{
|
||||
@@ -21,14 +23,8 @@ use pumpkin_world::dimension::Dimension;
|
||||
|
||||
use pumpkin_registry::Registry;
|
||||
use rsa::{traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
client::Client,
|
||||
config::{AdvancedConfiguration, BasicConfiguration},
|
||||
entity::player::{GameMode, Player},
|
||||
world::World,
|
||||
};
|
||||
use crate::{client::Client, entity::player::Player, world::World};
|
||||
|
||||
pub const CURRENT_MC_VERSION: &str = "1.21.1";
|
||||
|
||||
@@ -52,16 +48,15 @@ pub struct Server {
|
||||
pub cached_registry: Vec<Registry>,
|
||||
|
||||
entity_id: AtomicI32,
|
||||
pub base_config: BasicConfiguration,
|
||||
pub advanced_config: AdvancedConfiguration,
|
||||
|
||||
/// Used for Authentication, None is Online mode is disabled
|
||||
pub auth_client: Option<reqwest::Client>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(config: (BasicConfiguration, AdvancedConfiguration)) -> Self {
|
||||
let status_response = Self::build_response(&config.0);
|
||||
#[allow(clippy::new_without_default)]
|
||||
pub fn new() -> Self {
|
||||
let status_response = Self::build_response(&BASIC_CONFIG);
|
||||
let status_response_json = serde_json::to_string(&status_response)
|
||||
.expect("Failed to parse Status response into JSON");
|
||||
let cached_server_brand = Self::build_brand();
|
||||
@@ -75,7 +70,7 @@ impl Server {
|
||||
&private_key.e().to_bytes_be(),
|
||||
)
|
||||
.into_boxed_slice();
|
||||
let auth_client = if config.0.online_mode {
|
||||
let auth_client = if BASIC_CONFIG.online_mode {
|
||||
Some(
|
||||
reqwest::Client::builder()
|
||||
.timeout(Duration::from_millis(5000))
|
||||
@@ -106,9 +101,7 @@ impl Server {
|
||||
status_response,
|
||||
status_response_json,
|
||||
public_key_der,
|
||||
base_config: config.0,
|
||||
auth_client,
|
||||
advanced_config: config.1,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,7 +111,7 @@ impl Server {
|
||||
client: Client,
|
||||
) -> (Arc<Mutex<Player>>, Arc<tokio::sync::Mutex<World>>) {
|
||||
let entity_id = self.new_entity_id();
|
||||
let gamemode = match self.base_config.default_gamemode {
|
||||
let gamemode = match BASIC_CONFIG.default_gamemode {
|
||||
GameMode::Undefined => GameMode::Survival,
|
||||
game_mode => game_mode,
|
||||
};
|
||||
@@ -216,11 +209,3 @@ impl Server {
|
||||
(pub_key, priv_key)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Serialize, Deserialize)]
|
||||
pub enum Difficulty {
|
||||
Peaceful,
|
||||
Easy,
|
||||
Normal,
|
||||
Hard,
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use std::{
|
||||
|
||||
use mio::Token;
|
||||
use num_traits::ToPrimitive;
|
||||
use pumpkin_config::BasicConfiguration;
|
||||
use pumpkin_entity::{entity_type::EntityType, EntityId};
|
||||
use pumpkin_protocol::{
|
||||
client::play::{
|
||||
@@ -19,7 +20,7 @@ use pumpkin_protocol::{
|
||||
use pumpkin_world::{level::Level, radial_chunk_iterator::RadialIterator};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{config::BasicConfiguration, entity::player::Player};
|
||||
use crate::entity::player::Player;
|
||||
|
||||
pub struct World {
|
||||
pub level: Arc<Mutex<Level>>,
|
||||
|
||||
Reference in New Issue
Block a user