This commit is contained in:
ElementG9 2020-08-17 15:55:29 -06:00
parent 25cb7d75fc
commit 7e36693485
10 changed files with 477 additions and 398 deletions

17
.vscode/tasks.json vendored Normal file
View File

@ -0,0 +1,17 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "cargo",
"subcommand": "build",
"problemMatcher": [
"$rustc"
],
"group": {
"kind": "build",
"isDefault": true
},
"label": "Rust: cargo build - Composition"
}
]
}

75
Cargo.lock generated
View File

@ -4,10 +4,13 @@
name = "Composition"
version = "0.1.0"
dependencies = [
"backtrace",
"base64",
"chrono",
"colorful",
"fern",
"lazy_static",
"log",
"mojang-api",
"ozelot",
"radix64",
@ -17,9 +20,9 @@ dependencies = [
[[package]]
name = "addr2line"
version = "0.12.2"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c"
checksum = "1b6a2d3371669ab3ca9797670853d61402b03d0b4b9ebf33d677dfa720203072"
dependencies = [
"gimli",
]
@ -30,18 +33,23 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccc9a9dd069569f212bc4330af9f17c4afb5e8ce185e83dbb14f1349dda18b10"
[[package]]
name = "adler32"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "567b077b825e468cc974f0020d4082ee6e03132512f207ef1a02fd5d00d1f32d"
[[package]]
name = "arrayref"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi 0.3.8",
]
[[package]]
name = "autocfg"
version = "1.0.0"
@ -50,14 +58,14 @@ checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
[[package]]
name = "backtrace"
version = "0.3.49"
version = "0.3.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c"
checksum = "46254cf2fdcdf1badb5934448c1bcbe046a56537b3987d96c51a7afc5d03f293"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide 0.3.7",
"miniz_oxide",
"object",
"rustc-demangle",
]
@ -118,6 +126,17 @@ dependencies = [
"time",
]
[[package]]
name = "colored"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4ffc801dacf156c5854b9df4f425a626539c3a6ef7893cc0c5084a23f0b6c59"
dependencies = [
"atty",
"lazy_static",
"winapi 0.3.8",
]
[[package]]
name = "colorful"
version = "0.2.1"
@ -204,6 +223,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "fern"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065"
dependencies = [
"colored",
"log",
]
[[package]]
name = "flate2"
version = "1.0.16"
@ -213,7 +242,7 @@ dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.0",
"miniz_oxide",
]
[[package]]
@ -318,9 +347,9 @@ dependencies = [
[[package]]
name = "gimli"
version = "0.21.0"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c"
checksum = "aaf91faf136cb47367fa430cd46e37a788775e7fa104f8b4bcb3861dc389b724"
[[package]]
name = "h2"
@ -341,6 +370,15 @@ dependencies = [
"tokio-util",
]
[[package]]
name = "hermit-abi"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
dependencies = [
"libc",
]
[[package]]
name = "http"
version = "0.2.1"
@ -531,15 +569,6 @@ dependencies = [
"unicase",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.0"

View File

@ -16,3 +16,6 @@ serde = { version = "1.0.114", features = ["serde_derive"]}
base64 = "0.12.3"
radix64 = "0.3.0"
mojang-api = "0.6.1"
log = "*"
fern = { version = "0.6", features = ["colored"] }
backtrace = "0.3.50"

175
output.log Normal file
View File

@ -0,0 +1,175 @@
2020-07-27 11:25:17 [ERROR] - panicked at 'Normal panic', src/main.rs:55:5
2020-07-27 11:25:18 [ERROR] - panicked at 'Normal panic', src/main.rs:55:5
0: backtrace::backtrace::libunwind::trace
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/libunwind.rs:95
backtrace::backtrace::trace_unsynchronized
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/mod.rs:66
1: backtrace::backtrace::trace
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/mod.rs:53
2: backtrace::capture::Backtrace::create
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/capture.rs:164
3: backtrace::capture::Backtrace::new
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/capture.rs:128
4: Composition::main::{{closure}}
at src/main.rs:52
5: std::panicking::rust_panic_with_hook
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:475
6: std::panicking::begin_panic
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:404
7: Composition::main
at src/main.rs:55
8: std::rt::lang_start::{{closure}}
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
9: std::rt::lang_start_internal::{{closure}}
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:52
std::panicking::try::do_call
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:292
10: __rust_maybe_catch_panic
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libpanic_unwind/lib.rs:78
11: std::panicking::try
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:270
std::panic::catch_unwind
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panic.rs:394
std::rt::lang_start_internal
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:51
12: std::rt::lang_start
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
13: _main
2020-07-27 11:26:38 [ERROR] - panicked at 'Normal panic', src/main.rs:54:5
0: backtrace::backtrace::libunwind::trace
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/libunwind.rs:95
backtrace::backtrace::trace_unsynchronized
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/mod.rs:66
1: backtrace::backtrace::trace
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/backtrace/mod.rs:53
2: backtrace::capture::Backtrace::create
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/capture.rs:164
3: backtrace::capture::Backtrace::new
at /Users/gt/.cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.50/src/capture.rs:128
4: Composition::main::{{closure}}
at src/main.rs:51
5: std::panicking::rust_panic_with_hook
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:475
6: std::panicking::begin_panic
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:404
7: Composition::main
at src/main.rs:54
8: std::rt::lang_start::{{closure}}
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
9: std::rt::lang_start_internal::{{closure}}
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:52
std::panicking::try::do_call
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:292
10: __rust_maybe_catch_panic
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libpanic_unwind/lib.rs:78
11: std::panicking::try
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panicking.rs:270
std::panic::catch_unwind
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/panic.rs:394
std::rt::lang_start_internal
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:51
12: std::rt::lang_start
at /rustc/5e1a799842ba6ed4a57e91f7ab9435947482f7d8/src/libstd/rt.rs:67
13: _main
2020-07-27 11:30:23 [INFO] - Starting server...
2020-07-27 11:30:46 [INFO] - Starting server...
2020-07-27 11:31:01 [INFO] - Starting server
2020-07-27 11:40:10 [INFO] - Starting server...
2020-07-27 11:40:10 [INFO] - Done! Start took 763.52µs
2020-07-27 11:40:51 [INFO] - Starting server...
2020-07-27 11:40:51 [INFO] - Done! Start took 820.196µs
2020-07-27 11:47:37 [INFO] - Starting server...
2020-07-27 11:47:37 [INFO] - Done! Start took 441.888µs
2020-07-27 12:51:18 [INFO] - Starting server...
2020-07-27 12:51:18 [INFO] - Done! Start took 929.466µs
2020-07-27 13:14:52 [INFO] - Starting server...
2020-07-27 13:14:52 [INFO] - Starting network server...
2020-07-27 13:14:52 [INFO] - Done! Start took 829.689µs
2020-07-27 17:14:52 [INFO] - Starting server...
2020-07-27 17:14:52 [INFO] - Done! Start took 920.073µs
2020-07-27 17:14:52 [INFO] - Starting network...
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:52 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:53 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:54 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:55 [INFO] - []
2020-07-27 17:14:56 [INFO] - []
2020-07-27 17:14:56 [INFO] - []
2020-07-27 17:14:56 [INFO] - []
2020-07-27 17:28:01 [INFO] - Starting server...
2020-07-27 17:28:01 [INFO] - Done! Start took 1.079011ms
2020-07-27 17:28:01 [INFO] - Starting network...
2020-07-27 17:28:32 [INFO] - Starting server...
2020-07-27 17:28:32 [INFO] - Done! Start took 891.237µs
2020-07-27 17:30:03 [INFO] - Starting server...
2020-07-27 17:30:03 [INFO] - Done! Start took 889.43µs
2020-07-27 17:30:39 [INFO] - Starting server...
2020-07-27 17:30:39 [INFO] - Done! Start took 668.524µs
2020-07-27 17:32:13 [DEBUG] - Network received client: NetworkClient { id: 0, state: Handshake, alive: true, stream: BufReader { reader: TcpStream { addr: V4(127.0.0.1:25565), peer: V4(127.0.0.1:58986), fd: 5 }, buffer: 0/8192 }, username: None, packets: [] }
2020-07-27 17:32:13 [DEBUG] - Network received client: NetworkClient { id: 1, state: Handshake, alive: true, stream: BufReader { reader: TcpStream { addr: V4(127.0.0.1:25565), peer: V4(127.0.0.1:58988), fd: 5 }, buffer: 0/8192 }, username: None, packets: [] }
2020-07-27 17:37:57 [INFO] - Starting server...
2020-07-27 17:37:57 [INFO] - Done! Start took 1.007552ms
2020-07-27 18:15:45 [INFO] - Starting server...
2020-07-27 18:15:45 [INFO] - Done! Start took 812.588µs

View File

@ -1,99 +0,0 @@
// logger.rs
// author: Garen Tyler
// description:
// A global logger for Composition.
// The Logger struct makes it easy to create useful server logs.
extern crate chrono; // Used because std::time sucks.
extern crate toml; // Colorful console logging is fun.
use chrono::prelude::*;
use colorful::{Color, Colorful};
use std::fs::File;
use std::fs::OpenOptions;
use std::io::prelude::*;
// Uses chrono::Local to get a timestamp in YYYY-MM-DD HH:MM:SS format, in local time.
pub fn get_timestring() -> String {
Local::now().format("%Y-%m-%d %H:%M:%S").to_string()
}
// Just a helper function to avoid repeating myself.
pub fn log(logtype: &str, logmessage: &str) -> String {
format!("{} [{}] - {}", get_timestring(), logtype, logmessage)
}
// So I can do logger::new("log.txt") instead of logger::Logger::new("log.txt").
pub fn new(logfile: &str) -> Logger {
Logger::new(logfile)
}
#[derive(Clone)]
pub struct Logger {
pub logfile: String,
}
impl Logger {
pub fn new(logfile: &str) -> Logger {
Logger {
logfile: logfile.to_owned(),
}
}
// For logging something important, like the server port.
pub fn important(&self, s: &str) {
let l = log("IMPORTANT", s);
println!("{}", l.clone().color(Color::Green));
self.append(&l);
}
// For everything that doesn't fit into any of the other categories.
pub fn info(&self, s: &str) {
let l = log("INFO", s);
// Not sure whether I want normal logs to be user controlled color or white.
// println!("{}", l.clone().color(Color::White));
println!("{}", l.clone());
self.append(&l);
}
// For warnings.
pub fn warn(&self, s: &str) {
let l = log("WARN", s);
println!("{}", l.clone().color(Color::LightYellow));
self.append(&l);
}
// For errors.
pub fn error(&self, s: &str) {
let l = log("ERROR", s);
println!("{}", l.clone().color(Color::LightRed));
self.append(&l);
}
// Append to the logfile.
pub fn append(&self, s: &str) {
let a = || -> std::io::Result<()> {
let mut file = OpenOptions::new()
.create(true)
.write(true)
.append(true)
.open(&self.logfile)?;
writeln!(file, "{}", s)?;
Ok(())
};
if a().is_err() {
self.logger_error(&format!("Could not write to log file {}", self.logfile));
}
}
// Clear the logfile. Adds an important note to the file that it was cleared.
pub fn clear(&self) {
if std::fs::remove_file(&self.logfile).is_err() {
self.logger_error(&format!("Could not write to log file {}", self.logfile));
}
self.important(&format!("Cleared log file {}", self.logfile));
}
// Read the logfile into a String.
pub fn read(&self) -> std::io::Result<String> {
let mut f = File::open(&self.logfile)?;
let mut buffer = String::new();
f.read_to_string(&mut buffer)?;
Ok(buffer)
}
// For a critical logger error. This doesn't append to the logfile.
pub fn logger_error(&self, error_message: &str) {
let l = log("ERROR", error_message);
println!("{}", l.clone().color(Color::LightRed));
}
}

View File

@ -1,72 +1,73 @@
// main.rs
// authors: Garen Tyler, Danton Hou
// description:
// Main Game loop, config handler.
// Initializes the server, main server loop.
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
#![allow(unused_imports)]
#[macro_use]
extern crate lazy_static;
extern crate backtrace;
extern crate fern;
extern crate log;
extern crate serde;
pub mod logger;
pub mod mctypes;
pub mod net;
pub mod protocol;
pub mod network;
pub mod server;
use backtrace::Backtrace;
use fern::colors::{Color, ColoredLevelConfig};
use log::{debug, error, info, warn};
use network::NetworkServer;
use serde::{Deserialize, Serialize};
lazy_static! {
static ref log: logger::Logger = logger::new("log.txt");
static ref config: Config = { Config::from_file("composition.toml") };
}
use server::{Server, ServerConfig};
use std::sync::mpsc::{self, Receiver, Sender};
use std::time::{Duration, Instant};
fn main() {
// Start the network thread.
std::thread::spawn(|| {
log.info("Network thread started");
net::start_listening();
});
// Loop the main thread for now.
loop {}
}
// Setup logging.
fern::Dispatch::new()
.format(move |out, message, record| {
out.finish(format_args!(
"{date} [{level}] - {message}",
date = chrono::Local::now().format("%Y-%m-%d %H:%M:%S"),
// target = record.target(),
level = record.level(),
message = message,
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("output.log").unwrap())
.apply()
.unwrap();
// Not in it's own config module because of name conflicts.
#[derive(Serialize, Deserialize, Debug)]
pub struct Config {
pub port: u16,
pub protocol_version: u16,
pub max_players: u32,
pub motd: String,
pub favicon: Option<String>,
}
impl Config {
pub fn default() -> Config {
Config {
port: 25565,
protocol_version: 578,
max_players: 250,
motd: "Hello world!".to_owned(),
favicon: None,
}
}
pub fn from_file(filename: &str) -> Config {
use std::fs::File;
use std::io::prelude::*;
let a = || -> std::io::Result<Config> {
let mut file = File::open(filename)?;
let mut configStr = String::new();
file.read_to_string(&mut configStr)?;
Ok(toml::from_str(&configStr)?)
};
if let Ok(c) = a() {
c
} else {
log.warn(&format!(
"Could not load config from {}, using default config.",
filename
));
Config::default()
}
std::panic::set_hook(Box::new(|panic_info| {
let backtrace = Backtrace::new();
error!("{}\n{:?}", panic_info.to_string(), backtrace);
}));
info!("Starting server...");
let start_time = Instant::now();
let config = ServerConfig::from_file("composition.toml");
let port = config.port;
// Create the message channels.
let (tx, rx) = mpsc::channel();
// Create the server.
let mut server = Server {
config,
receiver: rx,
network: NetworkServer::new(port),
};
info!("Done! Start took {:?}", start_time.elapsed());
// The main server loop.
loop {
server.update(); // Do the tick.
std::thread::sleep(Duration::from_millis(50));
}
}

84
src/network/mod.rs Normal file
View File

@ -0,0 +1,84 @@
// network/mod.rs
// authors: Garen Tyler
// description:
// This module contains the network logic.
pub mod packet;
use crate::server::ServerMessage;
use log::{debug, error, info, warn};
use packet::Packet;
use std::io::BufReader;
use std::net::{TcpListener, TcpStream};
use std::sync::mpsc::{self, Receiver, Sender};
pub struct NetworkServer {
receiver: Receiver<NetworkClient>,
clients: Vec<NetworkClient>,
}
impl NetworkServer {
pub fn new(port: u16) -> NetworkServer {
let (tx, rx) = mpsc::channel();
std::thread::spawn(move || NetworkServer::listen(port, tx));
NetworkServer {
receiver: rx,
clients: Vec::new(),
}
}
fn listen(port: u16, sender: Sender<NetworkClient>) {
let listener = TcpListener::bind(&format!("0.0.0.0:{}", port)).unwrap();
for (index, stream) in listener.incoming().enumerate() {
let stream = stream.unwrap();
stream.set_nonblocking(true).unwrap();
sender
.send(NetworkClient {
// The index will increment after each client making it unique. We'll just use this as the id.
id: index as u32,
stream: BufReader::new(stream),
state: NetworkClientState::Handshake,
packets: Vec::new(),
username: None,
alive: true,
})
.unwrap();
}
}
pub fn update(&mut self) {
loop {
match self.receiver.try_recv() {
Ok(client) => self.clients.push(client),
Err(mpsc::TryRecvError::Empty) => break,
Err(mpsc::TryRecvError::Disconnected) => {
panic!("Client receiver channel disconnected!")
}
}
}
// Todo: Update each client
for client in self.clients.iter_mut() {
client.update();
}
}
}
#[derive(Debug)]
pub struct NetworkClient {
id: u32,
state: NetworkClientState,
alive: bool,
stream: BufReader<TcpStream>,
username: Option<String>,
packets: Vec<Packet>,
}
impl NetworkClient {
pub fn update(&mut self) {}
}
#[derive(PartialEq, Debug)]
pub enum NetworkClientState {
Handshake,
Status,
Login,
Play,
}

13
src/network/packet/mod.rs Normal file
View File

@ -0,0 +1,13 @@
// network/packet/mod.rs
// authors: Garen Tyler
// description:
// This module contains the packet structs.
#[derive(Debug)]
pub struct Packet {
kind: PacketType,
}
#[derive(PartialEq, Debug)]
pub enum PacketType {
Handshake,
}

View File

@ -1,219 +0,0 @@
// old_net.rs
// author: Garen Tyler
// description:
// This was the original implementation of net.rs. Only use as a reference.
pub static SERVER_LISTENER_ADDRESS: &str = "127.0.0.1:25565";
pub static SOCKET_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5);
extern crate serde;
extern crate serde_json;
use crate::mctypes::*;
use serde::Serialize;
use serde_json::json;
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
use std::thread;
pub struct MCPacket {
pub id: MCVarInt,
pub data: Vec<u8>,
}
#[allow(dead_code)]
impl MCPacket {
pub fn read_header(t: &mut TcpStream) -> std::io::Result<(MCVarInt, MCVarInt)> {
let length = MCVarInt::from_stream(t)?;
let id = MCVarInt::from_stream(t)?;
Ok((length, id))
}
pub fn new(id: u8) -> MCPacket {
MCPacket {
id: MCVarInt::new(id as i32),
data: Vec::new(),
}
}
pub fn write(&mut self, v: Vec<u8>) {
for b in v {
self.data.push(b);
}
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut bytes = Vec::new();
for b in MCVarInt::new((self.id.to_bytes().len() + self.data.len()) as i32).to_bytes() {
bytes.push(b);
}
for b in self.id.to_bytes() {
bytes.push(b);
}
for b in &self.data {
bytes.push(*b);
}
bytes
}
}
#[allow(dead_code)]
#[derive(PartialEq, Debug)]
pub enum GameState {
Handshake,
Status,
Login,
Play,
Closed,
}
#[allow(dead_code)]
pub struct GameConnection {
pub stream: TcpStream,
pub state: GameState,
}
pub fn start_listener() -> std::io::Result<()> {
if crate::DEBUG_LOGGING {
println!("Started listener at {}", SERVER_LISTENER_ADDRESS);
}
let listener = TcpListener::bind(SERVER_LISTENER_ADDRESS)?;
// Spawn a new thread for each connection.
for stream in listener.incoming() {
let stream = stream?;
thread::Builder::new()
.name(format!("GameConnection {}", stream.peer_addr().unwrap()))
.spawn(move || -> std::io::Result<()> {
if crate::DEBUG_LOGGING {
println!("Client connected at {}", stream.peer_addr().unwrap());
}
stream
.set_read_timeout(Some(SOCKET_TIMEOUT))
.expect("set_read_timeout call failed");
stream
.set_write_timeout(Some(SOCKET_TIMEOUT))
.expect("set_write_timeout call failed");
handle_client(GameConnection {
stream: stream,
state: GameState::Handshake,
})?;
Ok(())
})?;
}
Ok(())
}
pub fn handle_client(mut gc: GameConnection) -> std::io::Result<()> {
loop {
let (packet_length, packet_id) = MCPacket::read_header(&mut gc.stream)?;
if crate::DEBUG_LOGGING {
println!(
"Packet Length: {}, Packet ID: {}",
packet_length.value, packet_id.value
);
}
match gc.state {
GameState::Handshake => match packet_id.value {
0x00 => {
handshake(&mut gc)?;
}
_ => {
if crate::DEBUG_LOGGING {
println!("Unknown packet id {} in Handshake", packet_id);
}
}
},
GameState::Login => match packet_id.value {
0x00 => {
login(&mut gc)?;
}
_ => {
if crate::DEBUG_LOGGING {
println!("Unknown packet id {} in Login", packet_id);
}
}
},
GameState::Status => {
match packet_id.value {
0x00 => {
// Send a response packet.
let mut packet = MCPacket::new(0x00);
let json_response = json!({
"version": {
"name": crate::SERVER_VERSION,
"protocol": crate::SERVER_PROTOCOL_VERSION
},
"players": {
"max": 100,
"online": 5,
"sample": [
{
"name": "thinkofdeath",
"id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20"
}
]
},
"description": {
"text": crate::SERVER_MOTD
}
// No favicon for now.
// "favicon": "data:image/png;base64,<data>"
})
.to_string();
packet.write(MCVarInt::new(json_response.len() as i32).to_bytes());
packet.write(MCString::from(json_response.clone()).to_bytes());
gc.stream.write(&packet.to_bytes())?;
println!("=== SENT SERVER RESPONSE ===\n{}", json_response);
}
_ => {
if crate::DEBUG_LOGGING {
println!("Unknown packet id {} in Status", packet_id);
}
}
}
}
_ => {
if crate::DEBUG_LOGGING {
println!("Unknown gamestate {:?}", gc.state);
}
}
}
}
}
pub fn handshake(gc: &mut GameConnection) -> std::io::Result<()> {
// C->S Handshake
let protocol_version = MCVarInt::from_stream(&mut gc.stream)?;
let server_address = MCString::from_stream(&mut gc.stream)?;
let server_port = MCUnsignedShort::from_stream(&mut gc.stream)?;
let next_state = match MCVarInt::from_stream(&mut gc.stream)?.value {
1 => GameState::Status,
2 => GameState::Login,
_ => {
if crate::DEBUG_LOGGING {
println!("Unknown next_state in handshake");
}
GameState::Handshake
}
};
if crate::DEBUG_LOGGING {
println!(
"Handshake: Protocol Version: {}, Server Address: {}:{}, Next State: {:?}",
protocol_version.value, server_address.value, server_port.value, next_state
);
}
gc.state = next_state;
Ok(())
}
pub fn login(gc: &mut GameConnection) -> std::io::Result<()> {
// C->S Login Start
let player_username = MCString::from_stream(&mut gc.stream)?;
if crate::DEBUG_LOGGING {
println!("Login: Player Username: {}", player_username);
}
// S->C Encryption Request
// C->S Encryption Response
// S->C Set Compression
// S->C Login Success
let mut login_success = MCPacket::new(0x02);
login_success.write(MCString::from("00000000-0000-0000-0000-000000000000").to_bytes()); // UUID
login_success.write(player_username.to_bytes());
gc.stream.write(&login_success.to_bytes())?;
// Move to Play state
gc.state = GameState::Play;
play(gc)?;
Ok(())
}
pub fn play(gc: &mut GameConnection) -> std::io::Result<()> {
Ok(())
}

75
src/server.rs Normal file
View File

@ -0,0 +1,75 @@
// server.rs
// author: Garen Tyler
// description:
// Contains the server logic.
use crate::network::NetworkServer;
use log::{debug, error, info, warn};
use serde::{Deserialize, Serialize};
use std::sync::mpsc::{self, Receiver, Sender, TryRecvError};
pub struct Server {
pub config: ServerConfig,
pub receiver: Receiver<ServerMessage>,
pub network: NetworkServer,
}
impl Server {
pub fn update(&mut self) {
// Do a tick.
while let Ok(message) = self.receiver.try_recv() {
debug!("Server received message: {:?}", message);
self.handle_message(message);
}
self.network.update();
}
fn handle_message(&self, message: ServerMessage) {}
pub fn shutdown(&self) {
unimplemented!();
}
}
#[derive(Serialize, Deserialize, Clone)]
pub struct ServerConfig {
pub port: u16,
pub protocol_version: u16,
pub max_players: u32,
pub motd: String,
pub favicon: Option<String>,
}
impl ServerConfig {
pub fn default() -> ServerConfig {
ServerConfig {
port: 25565,
protocol_version: 578,
max_players: 250,
motd: "Hello world!".to_owned(),
favicon: None,
}
}
pub fn from_file(filename: &str) -> ServerConfig {
use std::fs::File;
use std::io::prelude::*;
let a = || -> std::io::Result<ServerConfig> {
let mut file = File::open(filename)?;
let mut configStr = String::new();
file.read_to_string(&mut configStr)?;
Ok(toml::from_str(&configStr)?)
};
if let Ok(c) = a() {
c
} else {
warn!(
"Could not load config from {}, using default config.",
filename
);
ServerConfig::default()
}
}
}
#[derive(Debug)]
pub enum BroadcastMessage {
Shutdown,
}
#[derive(Debug)]
pub enum ServerMessage {}