The server successfully responds to status pings.
commit
172796420a
|
@ -0,0 +1,11 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
|
@ -0,0 +1,19 @@
|
|||
name: Rust
|
||||
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install latest nightly
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
override: true
|
||||
|
||||
- name: Build
|
||||
run: cargo build --locked
|
|
@ -0,0 +1 @@
|
|||
/target
|
|
@ -0,0 +1,405 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[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.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "0.5.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.33.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfa80d47f954d53a35a64987ca1422f495b8d6483c0fe9f7117b36c2a792129"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
"yaml-rust",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"fuchsia-zircon-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fuchsia-zircon-sys"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.1.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iovec"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6"
|
||||
|
||||
[[package]]
|
||||
name = "kernel32-sys"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bd7d4bd64732af4bf3a67f367c27df8520ad7e230c5817b8ff485864d80242b9"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400"
|
||||
|
||||
[[package]]
|
||||
name = "mio"
|
||||
version = "0.6.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fce347092656428bc8eaf6201042cb551b8d67855af7374542a92a0fbfcac430"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fuchsia-zircon",
|
||||
"fuchsia-zircon-sys",
|
||||
"iovec",
|
||||
"kernel32-sys",
|
||||
"libc",
|
||||
"log",
|
||||
"miow",
|
||||
"net2",
|
||||
"slab",
|
||||
"winapi 0.2.8",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mio-uds"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0"
|
||||
dependencies = [
|
||||
"iovec",
|
||||
"libc",
|
||||
"mio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919"
|
||||
dependencies = [
|
||||
"kernel32-sys",
|
||||
"net2",
|
||||
"winapi 0.2.8",
|
||||
"ws2_32-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "net2"
|
||||
version = "0.2.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ba7c918ac76704fb42afcbbb43891e72731f3dcca3bef2a19786297baf14af7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282adbf10f2698a7a77f8e983a74b2d18176c19a7fd32a45446139ae7b02b715"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3433e879a558dde8b5e8feb2a04899cf34fdde1fafb894687e52105fc1162ac3"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fb7f4c519df8c117855e19dd8cc851e89eb746fe7a73f0157e0d95fdec5369b0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "textwrap"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tmd"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tokio",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "0.2.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d34ca54d84bf2b5b4d7d31e901a8464f7b60ac145a284fba25ceb801f2ddccd"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"iovec",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"memchr",
|
||||
"mio",
|
||||
"mio-uds",
|
||||
"num_cpus",
|
||||
"pin-project-lite",
|
||||
"tokio-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-build"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "ws2_32-sys"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
dependencies = [
|
||||
"winapi 0.2.8",
|
||||
"winapi-build",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"
|
|
@ -0,0 +1,14 @@
|
|||
[package]
|
||||
name = "tmd"
|
||||
version = "0.1.0"
|
||||
authors = ["James Martin <james@jtmar.me>"]
|
||||
edition = "2018"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "2.33.1", features = ["yaml"] }
|
||||
serde = { version = "1.0.114", features = ["derive"] }
|
||||
serde_json = "1.0.56"
|
||||
tokio = { version = "0.2.22", features = ["io-util", "macros", "net", "tcp", "rt-threaded"] }
|
||||
uuid = { version = "0.8", features = ["serde"] }
|
|
@ -0,0 +1,9 @@
|
|||
name: TMD
|
||||
version: "1.0"
|
||||
author: James Martin
|
||||
about: A Minecraft protocol-compatible server written in Rust.
|
||||
args:
|
||||
- host:
|
||||
help: The IP address the server will use to listen for connections. Defaults to any address (`::`).
|
||||
- port:
|
||||
help: The port the server will accept connections from. Defaults to 25565.
|
|
@ -0,0 +1,106 @@
|
|||
mod net;
|
||||
|
||||
use crate::net::chat::Chat;
|
||||
use crate::net::source;
|
||||
use crate::net::source::{PacketError, PacketSource};
|
||||
use tokio::io::BufWriter;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::net::tcp::WriteHalf;
|
||||
use std::io;
|
||||
use std::net::IpAddr;
|
||||
|
||||
use PacketError::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
let yaml = clap::load_yaml!("cli.yml");
|
||||
let args = clap::App::from_yaml(yaml).get_matches();
|
||||
let host: IpAddr = args.value_of("host").unwrap_or("::").parse()
|
||||
.expect("Invalid host IP address.");
|
||||
let port: u16 = args.value_of("port").unwrap_or("25565").parse()
|
||||
.expect("Port must be an integer between 1 an 65535.");
|
||||
|
||||
let listener = TcpListener::bind((host, port)).await
|
||||
.expect(&format!("Failed to bind to {}:{}.", host, port));
|
||||
|
||||
listen(listener).await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn listen(mut listener: TcpListener) {
|
||||
loop {
|
||||
let (socket, _) = match listener.accept().await {
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
eprintln!("Failed to accept client: {:?}", e);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
tokio::spawn(accept_connection(socket));
|
||||
}
|
||||
}
|
||||
|
||||
async fn accept_connection(mut socket: TcpStream) {
|
||||
let (mut read, write) = socket.split();
|
||||
let mut source = PacketSource::new(&mut read);
|
||||
let mut dest = BufWriter::new(write);
|
||||
|
||||
eprintln!("Client connected.");
|
||||
match interact_handshake(&mut source, &mut dest).await {
|
||||
Err(err) => { eprintln!("Client disconnected with error: {:?}", err); },
|
||||
Ok(_) => { eprintln!("Client disconnected without error."); }
|
||||
}
|
||||
}
|
||||
|
||||
async fn interact_handshake(source: &mut PacketSource<'_>, dest: &mut BufWriter<WriteHalf<'_>>) -> source::Result<()> {
|
||||
use crate::net::packet::handshake::*;
|
||||
use PacketHandshakeServerbound::*;
|
||||
|
||||
match read_packet_handshake(source).await? {
|
||||
Handshake(pkt) => {
|
||||
if pkt.next_state == HandshakeNextState::Status {
|
||||
interact_status(source, dest).await
|
||||
} else {
|
||||
Err(PktError("We do not support client log-in yet.".to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn interact_status(source: &mut PacketSource<'_>, dest: &mut BufWriter<WriteHalf<'_>>) -> source::Result<()> {
|
||||
use crate::net::packet::status::*;
|
||||
use PacketStatusClientbound::*;
|
||||
use PacketStatusServerbound::*;
|
||||
|
||||
loop {
|
||||
match read_packet_status(source).await? {
|
||||
Request => {
|
||||
match write_packet_status(dest, Response(PacketResponse {
|
||||
version: PacketResponseVersion {
|
||||
name: "1.16.1".to_string(),
|
||||
protocol: 736,
|
||||
},
|
||||
players: PacketResponsePlayers {
|
||||
max: 255,
|
||||
online: 0,
|
||||
sample: Vec::new(),
|
||||
},
|
||||
description: Chat { text: "Hello, world!".to_string() },
|
||||
favicon: None,
|
||||
})).await {
|
||||
Ok(_) => {},
|
||||
Err(err) => return Err(IoError(err))
|
||||
}
|
||||
},
|
||||
Ping(payload) => {
|
||||
match write_packet_status(dest, Pong(payload)).await {
|
||||
Ok(_) => {},
|
||||
Err(err) => return Err(IoError(err))
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// TODO: Support more features.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Chat {
|
||||
pub text: String,
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
pub mod chat;
|
||||
pub mod packet;
|
||||
pub mod source;
|
|
@ -0,0 +1,2 @@
|
|||
pub mod handshake;
|
||||
pub mod status;
|
|
@ -0,0 +1,47 @@
|
|||
use crate::net::source::{PacketError, PacketSource, Result};
|
||||
use PacketError::PktError;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum HandshakeNextState {
|
||||
Status,
|
||||
Login,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PacketHandshake {
|
||||
pub protocol_version: i32,
|
||||
pub server_address: String,
|
||||
pub server_port: u16,
|
||||
pub next_state: HandshakeNextState,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PacketHandshakeServerbound {
|
||||
Handshake(PacketHandshake),
|
||||
}
|
||||
|
||||
pub async fn read_packet_handshake(source: &mut PacketSource<'_>) -> Result<PacketHandshakeServerbound> {
|
||||
use PacketHandshakeServerbound::*;
|
||||
|
||||
let _length = source.read_varint().await?;
|
||||
let id = source.read_varint().await?;
|
||||
match id {
|
||||
0x00 => {
|
||||
let protocol_version = source.read_varint().await?;
|
||||
let server_address = source.read_string().await?;
|
||||
let server_port = source.read_u16().await?;
|
||||
let next_state = match source.read_varint().await? {
|
||||
1 => HandshakeNextState::Status,
|
||||
2 => HandshakeNextState::Login,
|
||||
n => return Err(PktError(format!("Invalid next protocol state in handshake: {}", n)))
|
||||
};
|
||||
Ok(Handshake(PacketHandshake {
|
||||
protocol_version: protocol_version,
|
||||
server_address: server_address,
|
||||
server_port: server_port,
|
||||
next_state: next_state,
|
||||
}))
|
||||
},
|
||||
id => Err(PktError(format!("Invalid handshake packet id: {}", id)))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
use crate::net::chat::Chat;
|
||||
use crate::net::source::{PacketError, PacketSource, Result};
|
||||
use serde::Serialize;
|
||||
use std::convert::TryInto;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::io::BufWriter;
|
||||
use tokio::net::tcp::WriteHalf;
|
||||
use uuid::Uuid;
|
||||
|
||||
use PacketError::PktError;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PacketResponseVersion {
|
||||
pub name: String,
|
||||
pub protocol: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PacketResponsePlayersSample {
|
||||
pub name: String,
|
||||
pub id: Uuid,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PacketResponsePlayers {
|
||||
pub max: u32,
|
||||
pub online: u32,
|
||||
pub sample: Vec<PacketResponsePlayersSample>
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct PacketResponse {
|
||||
pub version: PacketResponseVersion,
|
||||
pub players: PacketResponsePlayers,
|
||||
pub description: Chat,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub favicon: Option<String>,
|
||||
}
|
||||
|
||||
pub enum PacketStatusClientbound {
|
||||
Response(PacketResponse),
|
||||
Pong([u8; 8]),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PacketStatusServerbound {
|
||||
Request,
|
||||
Ping([u8; 8]),
|
||||
}
|
||||
|
||||
pub async fn read_packet_status(source: &mut PacketSource<'_>) -> Result<PacketStatusServerbound> {
|
||||
use PacketStatusServerbound::*;
|
||||
|
||||
let _length = source.read_varint().await?;
|
||||
let id = source.read_varint().await?;
|
||||
|
||||
match id {
|
||||
0 => Ok(Request),
|
||||
1 => {
|
||||
let mut buf = [0; 8];
|
||||
source.read_exact(&mut buf).await?;
|
||||
Ok(Ping(buf.try_into().unwrap()))
|
||||
}
|
||||
id => Err(PktError(format!("Invalid status packet id: {}", id)))
|
||||
}
|
||||
}
|
||||
|
||||
fn write_varint(dest: &mut Vec<u8>, mut value: i32) {
|
||||
loop {
|
||||
let mut temp = (value & 0b01111111) as u8;
|
||||
value = value >> 7;
|
||||
if value != 0 {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
dest.push(temp);
|
||||
|
||||
if value == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_slice(dest: &mut Vec<u8>, bytes: &[u8]) {
|
||||
write_varint(dest, bytes.len() as i32);
|
||||
dest.extend_from_slice(bytes);
|
||||
}
|
||||
|
||||
fn write_string(dest: &mut Vec<u8>, value: &str) {
|
||||
write_slice(dest, value.as_bytes());
|
||||
}
|
||||
|
||||
pub async fn write_packet_status(dest: &mut BufWriter<WriteHalf<'_>>,
|
||||
packet: PacketStatusClientbound) -> std::io::Result<()> {
|
||||
use PacketStatusClientbound::*;
|
||||
|
||||
let mut data = Vec::new();
|
||||
|
||||
match packet {
|
||||
Response(response) => {
|
||||
write_varint(&mut data, 0x00);
|
||||
write_slice(&mut data, serde_json::to_vec(&response).unwrap().as_slice());
|
||||
},
|
||||
Pong(payload) => {
|
||||
write_varint(&mut data, 0x01);
|
||||
data.extend_from_slice(&payload);
|
||||
}
|
||||
}
|
||||
|
||||
let mut packet_length_buf = Vec::new();
|
||||
write_varint(&mut packet_length_buf, data.len() as i32);
|
||||
|
||||
dest.write_all(packet_length_buf.as_slice()).await?;
|
||||
dest.write_all(data.as_slice()).await?;
|
||||
dest.flush().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
use tokio::io::AsyncReadExt;
|
||||
use tokio::net::tcp::ReadHalf;
|
||||
|
||||
const MAX_CLIENT_PACKET_SIZE: usize = 32767;
|
||||
|
||||
pub struct PacketSource<'a> {
|
||||
source: &'a mut ReadHalf<'a>,
|
||||
buf: [u8; MAX_CLIENT_PACKET_SIZE],
|
||||
index: usize,
|
||||
used: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum PacketError {
|
||||
IoError(std::io::Error),
|
||||
PktError(String),
|
||||
}
|
||||
|
||||
use PacketError::*;
|
||||
|
||||
pub type Result<A> = std::result::Result<A, PacketError>;
|
||||
|
||||
impl PacketSource<'_> {
|
||||
pub fn new<'a>(source: &'a mut ReadHalf<'a>) -> PacketSource<'a> {
|
||||
PacketSource {
|
||||
source: source,
|
||||
buf: [0; MAX_CLIENT_PACKET_SIZE],
|
||||
index: 0,
|
||||
used: 0,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Come up with a more efficient way of buffering.
|
||||
pub async fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
|
||||
let mut index = 0;
|
||||
while buf.len() > index {
|
||||
let bytes_needed = buf.len() - index;
|
||||
// If we've already read as many bytes as we need, return.
|
||||
if bytes_needed == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let bytes_remaining = self.used - self.index;
|
||||
// If we're out of bytes to read, read some more.
|
||||
if bytes_remaining == 0 {
|
||||
// If we've already used up the entire buffer, restart from the beginning.
|
||||
let space_remaining = self.buf.len() - self.used;
|
||||
if space_remaining < 1 {
|
||||
self.used = 0;
|
||||
self.index = 0;
|
||||
}
|
||||
|
||||
let len = self.buf.len();
|
||||
let read = match self.source.read(&mut self.buf[self.index..len]).await {
|
||||
Ok(read) => read,
|
||||
Err(err) => return Err(IoError(err))
|
||||
};
|
||||
|
||||
self.used += read;
|
||||
continue;
|
||||
}
|
||||
|
||||
let bytes_to_copy = bytes_remaining.min(bytes_needed);
|
||||
|
||||
buf[index..index + bytes_to_copy]
|
||||
.copy_from_slice(&self.buf[self.index..self.index + bytes_to_copy]);
|
||||
index += bytes_to_copy;
|
||||
self.index += bytes_to_copy;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn read_u8(&mut self) -> Result<u8> {
|
||||
let mut buf = [0; 1];
|
||||
self.read_exact(&mut buf).await?;
|
||||
Ok(buf[0])
|
||||
}
|
||||
|
||||
pub async fn read_u16(&mut self) -> Result<u16> {
|
||||
let mut buf = [0; 2];
|
||||
self.read_exact(&mut buf).await?;
|
||||
Ok(u16::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
pub async fn read_i64(&mut self) -> Result<i64> {
|
||||
let mut buf = [0; 8];
|
||||
self.read_exact(&mut buf).await?;
|
||||
Ok(i64::from_le_bytes(buf))
|
||||
}
|
||||
|
||||
pub async fn read_varint(&mut self) -> Result<i32> {
|
||||
let mut length = 1;
|
||||
let mut acc = 0;
|
||||
// VarInts must not be longer than 5 bytes.
|
||||
while length <= 5 {
|
||||
// If the highest bit is set, there are further bytes to be read from this VarInt;
|
||||
// the rest of the bits are the actual data in the VarInt.
|
||||
let read = self.read_u8().await?;
|
||||
acc |= (read & 0b01111111) as i32;
|
||||
|
||||
// There are no mo
|
||||
if (read & 0b10000000) == 0 {
|
||||
return Ok(acc);
|
||||
}
|
||||
|
||||
// Make space for the rest of the bits.
|
||||
acc = acc << 7;
|
||||
length += 1;
|
||||
}
|
||||
|
||||
// The VarInt was too long!
|
||||
Err(PktError("VarInt was more than 5 bytes.".to_string()))
|
||||
}
|
||||
|
||||
pub async fn read_string(&mut self) -> Result<String> {
|
||||
let length = self.read_varint().await?;
|
||||
if length < 0 {
|
||||
return Err(PktError("String length cannot be negative.".to_string()));
|
||||
}
|
||||
|
||||
let length = length as usize;
|
||||
if length > MAX_CLIENT_PACKET_SIZE {
|
||||
return Err(PktError("String was too long.".to_string()));
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
buf.resize(length, 0);
|
||||
self.read_exact(buf.as_mut_slice()).await?;
|
||||
String::from_utf8(buf).map_err(|_| PktError("String was invalid UTF-8.".to_string()))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue