Add proxy stub
This commit is contained in:
parent
514e48c6ad
commit
7b63542810
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -66,12 +66,6 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.94"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7"
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.83"
|
||||
@ -178,7 +172,6 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
name = "composition"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"clap",
|
||||
|
12
Cargo.toml
12
Cargo.toml
@ -14,20 +14,22 @@ name = "composition"
|
||||
path = "src/main.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["server", "proxy"]
|
||||
server = ["world", "dep:tokio-util", "dep:base64"]
|
||||
proxy = ["dep:tokio-util"]
|
||||
world = ["dep:async-trait"]
|
||||
update_1_20 = []
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0.94"
|
||||
async-trait = "0.1.68"
|
||||
base64 = "0.22.1"
|
||||
async-trait = { version = "0.1.68", optional = true }
|
||||
base64 = { version = "0.22.1", optional = true }
|
||||
clap = { version = "4.5.22", features = ["derive"] }
|
||||
once_cell = "1.17.1"
|
||||
serde = { version = "1.0.160", features = ["serde_derive"] }
|
||||
serde_json = "1.0.96"
|
||||
thiserror = "2.0.4"
|
||||
tokio = { version = "1.42.0", features = ["full"] }
|
||||
tokio-util = "0.7.13"
|
||||
tokio-util = { version = "0.7.13", optional = true }
|
||||
toml = "0.8.19"
|
||||
tracing = { version = "0.1.37", features = ["log"] }
|
||||
tracing-subscriber = { version = "0.3.17", features = ["tracing-log"] }
|
||||
|
@ -1,12 +1,15 @@
|
||||
use clap::Arg;
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ffi::OsStr;
|
||||
use std::io::{Read, Write};
|
||||
use std::{fs::File, path::Path, path::PathBuf};
|
||||
use tracing::{error, trace, warn};
|
||||
use std::ffi::OsStr;
|
||||
|
||||
use crate::server::config::{ServerConfig, ServerArgs, DEFAULT_SERVER_ARGS};
|
||||
#[cfg(feature = "proxy")]
|
||||
use crate::proxy::config::{ProxyArgs, ProxyConfig};
|
||||
#[cfg(feature = "server")]
|
||||
use crate::server::config::{ServerArgs, ServerConfig};
|
||||
|
||||
/// The globally-accessible static instance of Config.
|
||||
/// On program startup, Config::load() should be called to initialize it.
|
||||
@ -35,9 +38,27 @@ pub fn read_file(path: &Path) -> std::io::Result<Vec<u8>> {
|
||||
pub struct Config {
|
||||
#[serde(rename = "composition")]
|
||||
pub global: GlobalConfig,
|
||||
#[cfg(feature = "server")]
|
||||
pub server: ServerConfig,
|
||||
#[cfg(feature = "proxy")]
|
||||
pub proxy: ProxyConfig,
|
||||
}
|
||||
impl Config {
|
||||
pub fn get_formatted_version(subcommand: Subcommand) -> String {
|
||||
format!(
|
||||
"composition{} {} ({} {})",
|
||||
match subcommand {
|
||||
Subcommand::None => "",
|
||||
#[cfg(feature = "server")]
|
||||
Subcommand::Server => "",
|
||||
#[cfg(feature = "proxy")]
|
||||
Subcommand::Proxy => "-proxy",
|
||||
},
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
&env!("GIT_HASH")[0..9],
|
||||
&env!("GIT_DATE")[0..10]
|
||||
)
|
||||
}
|
||||
pub fn instance() -> &'static Self {
|
||||
match CONFIG.get() {
|
||||
Some(a) => a,
|
||||
@ -69,13 +90,10 @@ impl Config {
|
||||
error!("Could not read configuration file, using default");
|
||||
}
|
||||
|
||||
// Load the server icon
|
||||
config.server.server_icon = args
|
||||
.server
|
||||
.as_ref()
|
||||
.map(|s| s.server_icon.clone())
|
||||
.unwrap_or(DEFAULT_SERVER_ARGS.server_icon.clone());
|
||||
config.server.load_icon();
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
config.server.load_icon();
|
||||
}
|
||||
|
||||
CONFIG.set(config).expect("could not set CONFIG");
|
||||
Self::instance()
|
||||
@ -117,12 +135,7 @@ pub struct GlobalConfig {
|
||||
impl Default for GlobalConfig {
|
||||
fn default() -> Self {
|
||||
GlobalConfig {
|
||||
version: format!(
|
||||
"composition {} ({} {})",
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
&env!("GIT_HASH")[0..9],
|
||||
&env!("GIT_DATE")[0..10]
|
||||
),
|
||||
version: Config::get_formatted_version(Subcommand::None),
|
||||
protocol_version: 762,
|
||||
game_version: "1.19.4".to_owned(),
|
||||
threads: None,
|
||||
@ -135,10 +148,14 @@ impl GlobalConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Default)]
|
||||
pub enum Subcommand {
|
||||
#[default]
|
||||
None,
|
||||
#[cfg(feature = "server")]
|
||||
Server,
|
||||
#[cfg(feature = "proxy")]
|
||||
Proxy,
|
||||
}
|
||||
|
||||
/// All of the valid command line arguments for the composition binary.
|
||||
@ -150,7 +167,10 @@ pub struct Args {
|
||||
pub log_level: Option<tracing::Level>,
|
||||
pub log_dir: PathBuf,
|
||||
pub subcommand: Subcommand,
|
||||
#[cfg(feature = "server")]
|
||||
pub server: Option<ServerArgs>,
|
||||
#[cfg(feature = "proxy")]
|
||||
pub proxy: Option<ProxyArgs>,
|
||||
}
|
||||
impl Default for Args {
|
||||
fn default() -> Self {
|
||||
@ -159,7 +179,10 @@ impl Default for Args {
|
||||
log_level: None,
|
||||
log_dir: PathBuf::from("logs"),
|
||||
subcommand: Subcommand::None,
|
||||
#[cfg(feature = "server")]
|
||||
server: None,
|
||||
#[cfg(feature = "proxy")]
|
||||
proxy: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -174,8 +197,9 @@ impl Args {
|
||||
ARGS.set(Self::parse()).expect("could not set ARGS");
|
||||
Self::instance()
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
fn command() -> clap::Command {
|
||||
clap::Command::new("composition")
|
||||
let mut cmd = clap::Command::new("composition")
|
||||
.about(env!("CARGO_PKG_DESCRIPTION"))
|
||||
.disable_version_flag(true)
|
||||
.arg(
|
||||
@ -221,9 +245,18 @@ impl Args {
|
||||
.value_name("dir")
|
||||
.value_hint(clap::ValueHint::DirPath)
|
||||
.default_value(OsStr::new(&DEFAULT_ARGS.log_dir)),
|
||||
)
|
||||
.subcommand(ServerArgs::command())
|
||||
);
|
||||
#[cfg(feature = "server")]
|
||||
{
|
||||
cmd = cmd.subcommand(ServerArgs::command());
|
||||
}
|
||||
#[cfg(feature = "proxy")]
|
||||
{
|
||||
cmd = cmd.subcommand(ProxyArgs::command());
|
||||
}
|
||||
cmd
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
fn parse() -> Self {
|
||||
let mut args = Self::default();
|
||||
let m = Self::command().get_matches();
|
||||
@ -257,10 +290,16 @@ impl Args {
|
||||
}
|
||||
|
||||
match m.subcommand() {
|
||||
#[cfg(feature = "server")]
|
||||
Some(("server", m)) => {
|
||||
args.subcommand = Subcommand::Server;
|
||||
args.server = Some(ServerArgs::parse(m.clone()))
|
||||
}
|
||||
#[cfg(feature = "proxy")]
|
||||
Some(("proxy", m)) => {
|
||||
args.subcommand = Subcommand::Proxy;
|
||||
args.proxy = Some(ProxyArgs::parse(m.clone()))
|
||||
}
|
||||
None => {
|
||||
let _ = Self::command().print_help();
|
||||
std::process::exit(0);
|
||||
|
10
src/lib.rs
10
src/lib.rs
@ -2,10 +2,15 @@
|
||||
pub mod config;
|
||||
/// The Minecraft protocol implemented in a network-agnostic way.
|
||||
pub mod protocol;
|
||||
/// A proxy server.
|
||||
#[cfg(feature = "proxy")]
|
||||
pub(crate) mod proxy;
|
||||
/// The core server implementation.
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) mod server;
|
||||
/// A Minecraft world generator implementation that allows for custom worlds.
|
||||
pub mod world;
|
||||
#[cfg(feature = "world")]
|
||||
pub(crate) mod world;
|
||||
|
||||
use config::Subcommand;
|
||||
use once_cell::sync::OnceCell;
|
||||
@ -18,7 +23,10 @@ pub static START_TIME: OnceCell<Instant> = OnceCell::new();
|
||||
|
||||
pub async fn run(command: Subcommand) {
|
||||
match command {
|
||||
#[cfg(feature = "server")]
|
||||
Subcommand::Server => server::Server::run().await,
|
||||
#[cfg(feature = "proxy")]
|
||||
Subcommand::Proxy => proxy::Proxy::run().await,
|
||||
Subcommand::None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -16,10 +16,6 @@ pub enum Error {
|
||||
/// The data was not able to be parsed.
|
||||
#[error("parsing")]
|
||||
Parsing,
|
||||
/// This error is general purpose.
|
||||
/// When possible, other error variants should be used.
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
/// Alias for a Result with the error type `composition_protocol::Error`.
|
||||
|
46
src/proxy/config.rs
Normal file
46
src/proxy/config.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use crate::config::{Args, Config};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The main server configuration struct.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(default)]
|
||||
pub struct ProxyConfig {
|
||||
#[serde(rename = "version-string")]
|
||||
pub version: String,
|
||||
pub port: u16,
|
||||
pub upstream_address: String,
|
||||
}
|
||||
impl Default for ProxyConfig {
|
||||
fn default() -> Self {
|
||||
ProxyConfig {
|
||||
version: Config::get_formatted_version(crate::config::Subcommand::Proxy),
|
||||
port: 25565,
|
||||
upstream_address: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl ProxyConfig {
|
||||
pub fn instance() -> &'static Self {
|
||||
&Config::instance().proxy
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProxyArgs {}
|
||||
impl Default for ProxyArgs {
|
||||
fn default() -> Self {
|
||||
ProxyArgs {}
|
||||
}
|
||||
}
|
||||
impl ProxyArgs {
|
||||
pub fn instance() -> Option<&'static Self> {
|
||||
Args::instance().proxy.as_ref()
|
||||
}
|
||||
pub fn command() -> clap::Command {
|
||||
clap::Command::new("proxy").about("Run composition in proxy mode")
|
||||
}
|
||||
pub fn parse(_: clap::ArgMatches) -> Self {
|
||||
ProxyArgs::default()
|
||||
}
|
||||
}
|
71
src/proxy/mod.rs
Normal file
71
src/proxy/mod.rs
Normal file
@ -0,0 +1,71 @@
|
||||
pub mod config;
|
||||
|
||||
use crate::config::Config;
|
||||
use config::ProxyConfig;
|
||||
use tokio::net::ToSocketAddrs;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tracing::{info, trace};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Proxy {}
|
||||
impl Proxy {
|
||||
/// Start the proxy.
|
||||
#[tracing::instrument]
|
||||
pub async fn run() {
|
||||
let config = Config::instance();
|
||||
info!(
|
||||
"Starting {} on port {}",
|
||||
ProxyConfig::default().version,
|
||||
config.proxy.port
|
||||
);
|
||||
let (mut proxy, running) = Self::new(format!("0.0.0.0:{}", config.proxy.port)).await;
|
||||
info!(
|
||||
"Done! Start took {:?}",
|
||||
crate::START_TIME.get().unwrap().elapsed()
|
||||
);
|
||||
|
||||
// Spawn the ctrl-c task.
|
||||
let r = running.clone();
|
||||
tokio::spawn(async move {
|
||||
tokio::signal::ctrl_c().await.unwrap();
|
||||
info!("Ctrl-C received, shutting down");
|
||||
r.cancel();
|
||||
});
|
||||
|
||||
// The main loop.
|
||||
loop {
|
||||
tokio::select! {
|
||||
_ = running.cancelled() => {
|
||||
break;
|
||||
}
|
||||
_ = proxy.update() => {}
|
||||
}
|
||||
}
|
||||
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(10), proxy.shutdown()).await {
|
||||
Ok(_) => std::process::exit(0),
|
||||
Err(_) => std::process::exit(1),
|
||||
}
|
||||
}
|
||||
#[tracing::instrument]
|
||||
async fn new<A: 'static + ToSocketAddrs + Send + std::fmt::Debug>(
|
||||
_bind_address: A,
|
||||
) -> (Proxy, CancellationToken) {
|
||||
trace!("Proxy::new()");
|
||||
|
||||
let running = CancellationToken::new();
|
||||
let proxy = Proxy {};
|
||||
|
||||
(proxy, running)
|
||||
}
|
||||
#[tracing::instrument]
|
||||
async fn update(&mut self) -> Result<(), ()> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
#[tracing::instrument]
|
||||
async fn shutdown(self) {
|
||||
trace!("Proxy.shutdown()");
|
||||
// TODO
|
||||
}
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
use crate::config::{read_file, Args, Config};
|
||||
use clap::Arg;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::ffi::OsStr;
|
||||
use std::io::Write;
|
||||
use std::{fs::File, path::Path, path::PathBuf};
|
||||
use tracing::{error, trace, warn};
|
||||
use crate::config::{Config, Args, read_file};
|
||||
use std::ffi::OsStr;
|
||||
|
||||
pub static DEFAULT_SERVER_ARGS: Lazy<ServerArgs> = Lazy::new(ServerArgs::default);
|
||||
|
||||
@ -14,6 +14,8 @@ pub static DEFAULT_SERVER_ARGS: Lazy<ServerArgs> = Lazy::new(ServerArgs::default
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(default)]
|
||||
pub struct ServerConfig {
|
||||
#[serde(rename = "version-string")]
|
||||
pub version: String,
|
||||
pub port: u16,
|
||||
pub max_players: usize,
|
||||
pub motd: String,
|
||||
@ -24,6 +26,7 @@ pub struct ServerConfig {
|
||||
impl Default for ServerConfig {
|
||||
fn default() -> Self {
|
||||
ServerConfig {
|
||||
version: Config::get_formatted_version(crate::config::Subcommand::Server),
|
||||
port: 25565,
|
||||
max_players: 20,
|
||||
motd: "Hello world!".to_owned(),
|
||||
@ -36,7 +39,13 @@ impl ServerConfig {
|
||||
pub fn instance() -> &'static Self {
|
||||
&Config::instance().server
|
||||
}
|
||||
/// Load the server icon.
|
||||
pub fn load_icon(&mut self) {
|
||||
self.server_icon = ServerArgs::instance()
|
||||
.as_ref()
|
||||
.map(|s| s.server_icon.clone())
|
||||
.unwrap_or(DEFAULT_SERVER_ARGS.server_icon.clone());
|
||||
|
||||
let server_icon_path = Path::new(&self.server_icon);
|
||||
|
||||
if server_icon_path.exists() {
|
||||
|
@ -1,12 +1,13 @@
|
||||
/// Server-specific configuration.
|
||||
pub mod config;
|
||||
/// When managing the server encounters errors.
|
||||
pub mod error;
|
||||
/// Network operations.
|
||||
pub mod net;
|
||||
/// Server-specific configuration.
|
||||
pub mod config;
|
||||
|
||||
use crate::config::Config;
|
||||
use crate::protocol::ClientState;
|
||||
use config::ServerConfig;
|
||||
use error::Result;
|
||||
use net::{NetworkClient, NetworkClientState};
|
||||
use std::sync::Arc;
|
||||
@ -26,7 +27,11 @@ impl Server {
|
||||
#[tracing::instrument]
|
||||
pub async fn run() {
|
||||
let config = Config::instance();
|
||||
info!("Starting {} on port {}", config.global.version, config.server.port);
|
||||
info!(
|
||||
"Starting {} on port {}",
|
||||
ServerConfig::default().version,
|
||||
config.server.port
|
||||
);
|
||||
let (mut server, running) = Self::new(format!("0.0.0.0:{}", config.server.port)).await;
|
||||
info!(
|
||||
"Done! Start took {:?}",
|
||||
@ -57,7 +62,7 @@ impl Server {
|
||||
}
|
||||
}
|
||||
#[tracing::instrument]
|
||||
pub async fn new<A: 'static + ToSocketAddrs + Send + std::fmt::Debug>(
|
||||
async fn new<A: 'static + ToSocketAddrs + Send + std::fmt::Debug>(
|
||||
bind_address: A,
|
||||
) -> (Server, CancellationToken) {
|
||||
trace!("Server::new()");
|
||||
|
@ -5,8 +5,6 @@ pub enum Error {
|
||||
OutOfBounds,
|
||||
#[error(transparent)]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Other(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
/// Alias for a Result with the error type `composition_world::Error`.
|
||||
|
@ -1,3 +1,5 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
/// Worlds are divided into chunks.
|
||||
pub mod chunks;
|
||||
/// When managing a `World` encounters errors.
|
||||
@ -6,11 +8,11 @@ pub mod error;
|
||||
pub mod generators;
|
||||
/// Useful re-exports.
|
||||
pub mod prelude {
|
||||
pub use super::{chunks::Chunk, World};
|
||||
// pub use super::{chunks::Chunk, World};
|
||||
}
|
||||
|
||||
pub use crate::protocol::{blocks, entities};
|
||||
pub use error::{Error, Result};
|
||||
pub use error::Result;
|
||||
|
||||
use crate::world::chunks::{Chunk, ChunkPosition};
|
||||
use blocks::{Block, BlockPosition};
|
||||
|
Loading…
x
Reference in New Issue
Block a user