diff --git a/Cargo.lock b/Cargo.lock index ff98de2..97a6033 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6,14 +6,77 @@ version = "0.1.0" dependencies = [ "chrono", "colorful", + "lazy_static", + "ozelot", + "serde", + "toml", ] +[[package]] +name = "addr2line" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602d785912f476e480434627e8732e6766b760c045bbf897d9dfaa9f4fbd399c" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +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 = "autocfg" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" +[[package]] +name = "backtrace" +version = "0.3.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05100821de9e028f12ae3d189176b41ee198341eb8f369956407fea2f5cc666c" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide 0.3.7", + "object", + "rustc-demangle", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cc" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fde55d2a2bfaa4c9668bbc63f531fbdeee3ffe188f4662511ce2c22b3eedebe" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "chrono" version = "0.4.13" @@ -31,12 +94,142 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bca1619ff57dd7a56b58a8e25ef4199f123e78e503fe1653410350a1b98ae65" +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "curl" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0447a642435be046540f042950d874a4907f9fee28c0513a0beb3ba89f91eb7" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "winapi", +] + +[[package]] +name = "curl-sys" +version = "0.4.32+curl-7.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "834425a2f22fdd621434196965bf99fbfd9eaed96348488e27b7ac40736c560b" +dependencies = [ + "cc", + "libc", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "winapi", +] + +[[package]] +name = "error-chain" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d371106cc88ffdfb1eabd7111e432da544f16f3e2d7bf1dfe8bf575f1df045cd" +dependencies = [ + "backtrace", + "version_check", +] + +[[package]] +name = "flate2" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68c90b0fc46cf89d227cc78b40e494ff81287a92dd07631e5af0d06fe3cf885e" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide 0.4.0", +] + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "gimli" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc8e0c9bce37868955864dbecd2b1ab2bdf967e6f28066d65aaac620444b65c" + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[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.70" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f" +[[package]] +name = "libz-sys" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be0f75932c1f6cfae3c04000e40114adf955636e19040f9c0a2c380702aa1c7f" +dependencies = [ + "adler", +] + +[[package]] +name = "netbuf" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f41e95a536af3c11c6dcf16f30b8319aad291049e67c8aa4701f69f11761a9db" + [[package]] name = "num-integer" version = "0.1.43" @@ -56,6 +249,168 @@ dependencies = [ "autocfg", ] +[[package]] +name = "object" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ab52be62400ca80aa00285d25253d7f7c437b7375c4de678f5405d3afe82ca5" + +[[package]] +name = "openssl" +version = "0.10.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d575eff3665419f9b83678ff2815858ad9d11567e082f5ac1814baba4e2bcb4" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "lazy_static", + "libc", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" + +[[package]] +name = "openssl-sys" +version = "0.9.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a842db4709b604f0fe5d1170ae3565899be2ad3d9cbc72dedc789ac0511f78de" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "ozelot" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea00748638603838e4c5907d8d10449ee9a7e548667af7c20c684888006f6657" +dependencies = [ + "byteorder", + "curl", + "error-chain", + "flate2", + "netbuf", + "openssl", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "pkg-config" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677" + +[[package]] +name = "proc-macro2" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa" +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 = "redox_syscall" +version = "0.1.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" + +[[package]] +name = "rustc-demangle" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + +[[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 = "socket2" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03088793f677dce356f3ccc2edb1b314ad191ab702a5de3faf49304f7e104918" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "time" version = "0.1.43" @@ -66,6 +421,33 @@ dependencies = [ "winapi", ] +[[package]] +name = "toml" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" +dependencies = [ + "serde", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + [[package]] name = "winapi" version = "0.3.8" diff --git a/Cargo.toml b/Cargo.toml index 7625db3..b10a932 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,7 @@ edition = "2018" [dependencies] chrono = "0.4.13" colorful = "0.2.1" +lazy_static = "1.4.0" +ozelot = "0.9.0" # Ozelot 0.9.0 supports protocol version 578 (1.15.2) +toml = "0.5.6" +serde = { version = "1.0.114", features = ["serde_derive"]} diff --git a/composition.toml b/composition.toml new file mode 100644 index 0000000..7fe2589 --- /dev/null +++ b/composition.toml @@ -0,0 +1,4 @@ +port = 25565 +protocol_version = 578 +max_players = 420 +motd = ligma balls lol diff --git a/log.txt b/log.txt index 1d028cb..7b0d106 100644 --- a/log.txt +++ b/log.txt @@ -1,5 +1,222 @@ -2020-07-05 21:37:41 [IMPORTANT] - Cleared log file log.txt -2020-07-05 21:37:41 [IMPORTANT] - This is important information -2020-07-05 21:37:41 [INFO] - This is information -2020-07-05 21:37:41 [WARN] - This is a warning -2020-07-05 21:37:41 [ERROR] - This is an error +2020-07-06 10:40:25 [IMPORTANT] - Cleared log file log.txt +2020-07-06 10:40:25 [INFO] - gamer +2020-07-06 10:40:25 [INFO] - balls +2020-07-06 10:41:04 [INFO] - Network thread started +2020-07-06 10:41:04 [INFO] - balls +2020-07-06 10:42:10 [INFO] - Network thread started +2020-07-06 10:42:10 [INFO] - balls +2020-07-06 10:44:02 [INFO] - Network thread started +2020-07-06 10:44:02 [INFO] - balls +2020-07-06 10:44:15 [INFO] - Network thread started +2020-07-06 10:44:15 [INFO] - log from network thread +2020-07-06 10:49:45 [INFO] - Network thread started +2020-07-06 10:49:59 [INFO] - Got a client! +2020-07-06 11:00:42 [INFO] - Network thread started +2020-07-06 11:04:04 [INFO] - Network thread started +2020-07-06 11:04:04 [IMPORTANT] - Starting server on 127.0.0.1:25565 +2020-07-06 11:04:24 [INFO] - Network thread started +2020-07-06 11:04:24 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:04:43 [INFO] - Got a client! +2020-07-06 11:22:16 [INFO] - Network thread started +2020-07-06 11:22:16 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:22:22 [INFO] - Got a client! +2020-07-06 11:25:16 [INFO] - Network thread started +2020-07-06 11:25:16 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:25:20 [INFO] - Got a client! +2020-07-06 11:26:01 [INFO] - Network thread started +2020-07-06 11:26:01 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:26:04 [INFO] - Got a client! +2020-07-06 11:27:38 [INFO] - Network thread started +2020-07-06 11:27:38 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:27:47 [INFO] - Got a client! +2020-07-06 11:29:02 [INFO] - Network thread started +2020-07-06 11:29:02 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 11:29:05 [INFO] - Got a client! +2020-07-06 13:06:10 [INFO] - Network thread started +2020-07-06 13:06:10 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-06 13:06:14 [INFO] - Got a client! +2020-07-07 12:51:20 [INFO] - Network thread started +2020-07-07 12:51:20 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-07 12:51:45 [INFO] - Network thread started +2020-07-07 12:51:45 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-07 12:57:26 [INFO] - Network thread started +2020-07-07 12:57:26 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:08:57 [INFO] - Got a client! +2020-07-08 19:08:57 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 19:17:12 [INFO] - Network thread started +2020-07-08 19:17:12 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:40:27 [INFO] - Network thread started +2020-07-08 19:40:27 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:40:38 [INFO] - Network thread started +2020-07-08 19:40:38 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:41:43 [INFO] - 12345 +2020-07-08 19:41:43 [INFO] - Network thread started +2020-07-08 19:41:43 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:41:55 [INFO] - 25565 +2020-07-08 19:41:55 [INFO] - Network thread started +2020-07-08 19:41:55 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:43:03 [INFO] - 12345 +2020-07-08 19:43:03 [INFO] - Network thread started +2020-07-08 19:43:03 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:43:12 [WARN] - Could not load config from composition.toml, using default config. +2020-07-08 19:43:12 [INFO] - 25565 +2020-07-08 19:43:12 [INFO] - Network thread started +2020-07-08 19:43:12 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:56:14 [INFO] - 25565 +2020-07-08 19:56:14 [INFO] - Network thread started +2020-07-08 19:56:14 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:58:29 [INFO] - Network thread started +2020-07-08 19:58:29 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 19:58:43 [INFO] - Network thread started +2020-07-08 19:58:43 [IMPORTANT] - Started server on 127.0.0.1:12345 +2020-07-08 19:58:52 [INFO] - Network thread started +2020-07-08 19:58:52 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:03:06 [INFO] - Network thread started +2020-07-08 20:03:06 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:03:12 [INFO] - Got a client! +2020-07-08 20:03:12 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:05:29 [INFO] - Network thread started +2020-07-08 20:05:29 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:08:08 [INFO] - Network thread started +2020-07-08 20:08:08 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:10:04 [INFO] - Network thread started +2020-07-08 20:10:04 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:10:08 [INFO] - Got a client! +2020-07-08 20:10:09 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:10:09 [INFO] - Client and server run same version of Minecraft. +2020-07-08 20:11:50 [INFO] - Got a client! +2020-07-08 20:11:50 [INFO] - Handshake { protocol_version: MCVarInt { value: 736 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:11:50 [INFO] - Client and server don't run same version of Minecraft. +2020-07-08 20:38:15 [INFO] - Network thread started +2020-07-08 20:38:15 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:38:30 [INFO] - Got a client! +2020-07-08 20:38:30 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:38:32 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:40:27 [INFO] - Network thread started +2020-07-08 20:40:27 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:40:30 [INFO] - Got a client! +2020-07-08 20:40:30 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:40:30 [INFO] - Next state: Login +2020-07-08 20:40:33 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:48:19 [INFO] - Network thread started +2020-07-08 20:48:19 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:48:25 [INFO] - Got a client! +2020-07-08 20:48:25 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:48:25 [INFO] - Next state: Login +2020-07-08 20:48:25 [INFO] - LoginStart { username: MCString { value: "ElementG9" } } +2020-07-08 20:48:25 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:51:41 [INFO] - Network thread started +2020-07-08 20:51:41 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:52:00 [INFO] - Got a client! +2020-07-08 20:52:00 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 20:52:00 [INFO] - Next state: Status +2020-07-08 20:52:30 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:52:30 [INFO] - Got a client! +2020-07-08 20:52:30 [INFO] - Handshake { protocol_version: MCVarInt { value: 11 }, server_address: MCString { value: "" }, server_port: MCUnsignedShort { value: 19712 }, next_state: MCVarInt { value: 67 } } +2020-07-08 20:52:30 [INFO] - Next state: Closed +2020-07-08 20:52:30 [INFO] - Client at 127.0.0.1:52064 closed connection +2020-07-08 20:55:47 [INFO] - Network thread started +2020-07-08 20:55:47 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 20:55:53 [INFO] - Got a client! +2020-07-08 20:55:53 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "localhost" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 2 } } +2020-07-08 20:55:53 [INFO] - Next state: Login +2020-07-08 20:55:53 [INFO] - LoginStart { username: MCString { value: "ElementG9" } } +2020-07-08 20:55:54 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:56:00 [INFO] - Got a client! +2020-07-08 20:56:00 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 20:56:00 [INFO] - Next state: Status +2020-07-08 20:56:30 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 20:56:30 [INFO] - Got a client! +2020-07-08 20:56:30 [INFO] - Handshake { protocol_version: MCVarInt { value: 11 }, server_address: MCString { value: "" }, server_port: MCUnsignedShort { value: 19712 }, next_state: MCVarInt { value: 67 } } +2020-07-08 20:56:30 [INFO] - Next state: Closed +2020-07-08 20:56:30 [INFO] - Client at 127.0.0.1:52096 closed connection +2020-07-08 21:04:04 [INFO] - Network thread started +2020-07-08 21:04:04 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:04:17 [INFO] - Network thread started +2020-07-08 21:04:17 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:04:22 [INFO] - Got a client! +2020-07-08 21:04:22 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:04:22 [INFO] - Next state: Status +2020-07-08 21:04:52 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 21:04:52 [INFO] - Got a client! +2020-07-08 21:04:52 [INFO] - Handshake { protocol_version: MCVarInt { value: 11 }, server_address: MCString { value: "" }, server_port: MCUnsignedShort { value: 19712 }, next_state: MCVarInt { value: 67 } } +2020-07-08 21:04:52 [INFO] - Next state: Closed +2020-07-08 21:04:52 [INFO] - Client at 127.0.0.1:52301 closed connection +2020-07-08 21:09:13 [INFO] - Network thread started +2020-07-08 21:09:13 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:09:26 [INFO] - Got a client! +2020-07-08 21:09:26 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:09:26 [INFO] - Next state: Status +2020-07-08 21:09:56 [ERROR] - Error when handling client: Connection reset by peer (os error 54) +2020-07-08 21:09:56 [INFO] - Got a client! +2020-07-08 21:09:56 [INFO] - Handshake { protocol_version: MCVarInt { value: 11 }, server_address: MCString { value: "" }, server_port: MCUnsignedShort { value: 19712 }, next_state: MCVarInt { value: 67 } } +2020-07-08 21:09:56 [INFO] - Next state: Closed +2020-07-08 21:09:56 [INFO] - Client at 127.0.0.1:52998 closed connection +2020-07-08 21:10:42 [INFO] - Network thread started +2020-07-08 21:10:42 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:10:44 [INFO] - Got a client! +2020-07-08 21:10:44 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:10:44 [INFO] - Next state: Status +2020-07-08 21:10:44 [INFO] - Ping number: MCLong { value: 6738900 } +2020-07-08 21:10:44 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 21:11:20 [INFO] - Network thread started +2020-07-08 21:11:20 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:11:25 [INFO] - Got a client! +2020-07-08 21:11:25 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:11:25 [INFO] - Next state: Status +2020-07-08 21:11:25 [INFO] - Ping number: MCLong { value: 6779912 } +2020-07-08 21:11:25 [INFO] - Client at 127.0.0.1:53056 closed connection +2020-07-08 21:16:51 [INFO] - Network thread started +2020-07-08 21:16:51 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:16:58 [INFO] - Got a client! +2020-07-08 21:16:58 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:16:58 [INFO] - Next state: Status +2020-07-08 21:16:58 [ERROR] - Error when handling client: failed to fill whole buffer +2020-07-08 21:16:58 [INFO] - Got a client! +2020-07-08 21:16:58 [INFO] - Handshake { protocol_version: MCVarInt { value: 11 }, server_address: MCString { value: "" }, server_port: MCUnsignedShort { value: 19712 }, next_state: MCVarInt { value: 67 } } +2020-07-08 21:16:58 [INFO] - Next state: Closed +2020-07-08 21:16:58 [INFO] - Client at 127.0.0.1:53607 closed connection +2020-07-08 21:17:32 [INFO] - Network thread started +2020-07-08 21:17:32 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:17:36 [INFO] - Got a client! +2020-07-08 21:17:36 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:17:36 [INFO] - Next state: Status +2020-07-08 21:17:36 [INFO] - Ping number: MCLong { value: 7150256 } +2020-07-08 21:17:36 [INFO] - Client at 127.0.0.1:53687 closed connection +2020-07-08 21:18:54 [INFO] - Network thread started +2020-07-08 21:18:54 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:18:58 [INFO] - Got a client! +2020-07-08 21:18:58 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:18:58 [INFO] - Next state: Status +2020-07-08 21:18:58 [INFO] - Ping number: MCLong { value: 7232924 } +2020-07-08 21:18:58 [INFO] - Client at 127.0.0.1:53774 closed connection +2020-07-08 21:19:42 [INFO] - Network thread started +2020-07-08 21:19:42 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:19:45 [INFO] - Got a client! +2020-07-08 21:19:45 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:19:45 [INFO] - Next state: Status +2020-07-08 21:19:45 [INFO] - Ping number: MCLong { value: 7279794 } +2020-07-08 21:19:45 [INFO] - Client at 127.0.0.1:53784 closed connection +2020-07-08 21:20:07 [INFO] - Network thread started +2020-07-08 21:20:07 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:20:12 [INFO] - Got a client! +2020-07-08 21:20:12 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:20:12 [INFO] - Next state: Status +2020-07-08 21:20:12 [INFO] - Ping number: MCLong { value: 7306496 } +2020-07-08 21:20:12 [INFO] - Client at 127.0.0.1:54216 closed connection +2020-07-08 21:22:22 [INFO] - Network thread started +2020-07-08 21:22:22 [WARN] - Could not load config from composition.toml, using default config. +2020-07-08 21:22:22 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:22:26 [INFO] - Got a client! +2020-07-08 21:22:26 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:22:26 [INFO] - Next state: Status +2020-07-08 21:22:26 [INFO] - Ping number: MCLong { value: 7440272 } +2020-07-08 21:22:26 [INFO] - Client at 127.0.0.1:55792 closed connection +2020-07-08 21:23:10 [INFO] - Network thread started +2020-07-08 21:23:10 [WARN] - Could not load config from composition.toml, using default config. +2020-07-08 21:23:10 [IMPORTANT] - Started server on 127.0.0.1:25565 +2020-07-08 21:23:13 [INFO] - Got a client! +2020-07-08 21:23:13 [INFO] - Handshake { protocol_version: MCVarInt { value: 578 }, server_address: MCString { value: "127.0.0.1" }, server_port: MCUnsignedShort { value: 25565 }, next_state: MCVarInt { value: 1 } } +2020-07-08 21:23:13 [INFO] - Next state: Status +2020-07-08 21:23:13 [INFO] - Ping number: MCLong { value: 7487517 } +2020-07-08 21:23:13 [INFO] - Client at 127.0.0.1:55872 closed connection diff --git a/src/logger.rs b/src/logger.rs index c6316bd..880f0dc 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -18,75 +18,55 @@ pub fn new(logfile: &str) -> Logger { Logger::new(logfile) } +#[derive(Clone)] pub struct Logger { pub logfile: String, - pub info_written: u32, - pub warn_written: u32, - pub error_written: u32, - pub important_written: u32, - pub write_enabled: bool, } impl Logger { pub fn new(logfile: &str) -> Logger { Logger { logfile: logfile.to_owned(), - info_written: 0, - warn_written: 0, - error_written: 0, - important_written: 0, - write_enabled: true, } } - pub fn important(&mut self, s: &str) { + pub fn important(&self, s: &str) { let l = log("IMPORTANT", s); - println!("{}", l.clone().color(Color::Cyan)); + println!("{}", l.clone().color(Color::Green)); self.append(&l); - self.important_written += 1; } - pub fn info(&mut self, s: &str) { + pub fn info(&self, s: &str) { let l = log("INFO", s); - println!("{}", l.clone().color(Color::White)); + // println!("{}", l.clone().color(Color::White)); + println!("{}", l.clone()); self.append(&l); - self.info_written += 1; } - pub fn warn(&mut self, s: &str) { + pub fn warn(&self, s: &str) { let l = log("WARN", s); - println!("{}", l.clone().color(Color::Yellow)); + println!("{}", l.clone().color(Color::LightYellow)); self.append(&l); - self.warn_written += 1; } - pub fn error(&mut self, s: &str) { + pub fn error(&self, s: &str) { let l = log("ERROR", s); - println!("{}", l.clone().color(Color::Red)); + println!("{}", l.clone().color(Color::LightRed)); self.append(&l); - self.error_written += 1; } - pub fn append(&mut self, s: &str) { - if self.write_enabled { - 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)); - } + 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)); } } - pub fn clear(&mut self) { - if self.write_enabled { - if std::fs::remove_file(&self.logfile).is_err() { - self.logger_error(&format!("Could not write to log file {}", self.logfile)); - } + 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.info_written = 0; - self.warn_written = 0; - self.error_written = 0; - self.important_written = 0; self.important(&format!("Cleared log file {}", self.logfile)); } pub fn read(&self) -> std::io::Result { @@ -95,8 +75,8 @@ impl Logger { f.read_to_string(&mut buffer)?; Ok(buffer) } - pub fn logger_error(&mut self, error_message: &str) { - self.write_enabled = false; - self.error(error_message); + pub fn logger_error(&self, error_message: &str) { + let l = log("ERROR", error_message); + println!("{}", l.clone().color(Color::LightRed)); } } diff --git a/src/main.rs b/src/main.rs index 8530094..24afce5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,63 @@ #![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#[macro_use] +extern crate lazy_static; +extern crate ozelot; +extern crate serde; +extern crate toml; pub mod logger; +pub mod mctypes; +pub mod net; +pub mod protocol; + +use serde::{Deserialize, Serialize}; + +lazy_static! { + static ref log: logger::Logger = logger::new("log.txt"); + static ref config: Config = { Config::from_file("composition.toml") }; +} fn main() { - let mut log = logger::new("log.txt"); - log.clear(); - log.important("This is important information"); - log.info("This is information"); - log.warn("This is a warning"); - log.error("This is an error"); + // Start the network thread. + std::thread::spawn(|| { + log.info("Network thread started"); + net::start_listening(); + }); + + // Loop the main thread for now. + loop {} +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Config { + pub port: u16, + pub protocol_version: u16, +} +impl Config { + pub fn default() -> Config { + Config { + port: 25565, + protocol_version: 578, + } + } + pub fn from_file(filename: &str) -> Config { + use std::fs::File; + use std::io::prelude::*; + let a = || -> std::io::Result { + 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() + } + } } diff --git a/src/mctypes.rs b/src/mctypes.rs index 7ab84c6..e698da4 100644 --- a/src/mctypes.rs +++ b/src/mctypes.rs @@ -6,6 +6,10 @@ pub fn read_byte(t: &mut TcpStream) -> std::io::Result { t.read_exact(&mut buffer)?; Ok(buffer[0]) } +pub fn write_byte(t: &mut TcpStream, value: u8) -> std::io::Result<()> { + t.write(&[value])?; + Ok(()) +} pub fn get_bytes(v: Vec, l: usize) -> Box<[u8]> { use std::collections::VecDeque; let mut v = VecDeque::from(v); diff --git a/src/net.rs b/src/net.rs index df7b052..d18f431 100644 --- a/src/net.rs +++ b/src/net.rs @@ -1,50 +1,169 @@ -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 crate::{config, log}; use std::net::{TcpListener, TcpStream}; -use std::thread; -pub struct MCPacket { - pub id: MCVarInt, - pub data: Vec, -} -#[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(), +use crate::mctypes::*; +use crate::protocol::*; + +pub fn start_listening() { + let server_address: &str = &format!("127.0.0.1:{}", config.port); + let listener = TcpListener::bind(server_address); + if listener.is_err() { + log.error("Could not start listener"); + } else { + log.important(&format!("Started server on {}", server_address)); + for stream in listener.unwrap().incoming() { + if stream.is_err() { + log.error("Could not connect to client"); + } else { + std::thread::spawn(move || { + if let Err(e) = handle_client(stream.unwrap()) { + log.error(&format!("Error when handling client: {}", e)); + } + }); + } } } - pub fn write(&mut self, v: Vec) { - for b in v { - self.data.push(b); - } - } - pub fn to_bytes(&self) -> Vec { - 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 - } } +fn handle_client(t: TcpStream) -> std::io::Result<()> { + log.info("Got a client!"); + let mut gc = GameConnection { + stream: t, + state: GameState::Handshake, + protocol_version: 0, + }; + + 'main: loop { + match gc.state { + GameState::Handshake => { + // Read the handshake packet. + let (_packet_len, packet_id) = read_packet_header(&mut gc.stream)?; + let handshake = Handshake::read(&mut gc.stream)?; + log.info(&format!("{:?}", handshake)); + gc.state = if handshake.protocol_version.value != config.protocol_version as i32 { + GameState::Closed + } else { + match handshake.next_state.value { + 1 => GameState::Status, + 2 => GameState::Login, + _ => GameState::Closed, + } + }; + log.info(&format!("Next state: {:?}", gc.state)); + gc.protocol_version = handshake.protocol_version.value as u16; + } + GameState::Status => { + // Read the request packet. + let (_request_packet_len, _request_packet_id) = read_packet_header(&mut gc.stream)?; + // Send the response packet. + let response = MCString::from( + r#"{ + "version": { + "name": "1.15.2", + "protocol": 578 + }, + "players": { + "max": 2147483647, + "online": 69, + "sample": [ + { + "name": "thinkofdeath", + "id": "4566e69f-c907-48ee-8d71-d7ba5aa00d20" + } + ] + }, + "description": { + "text": "ligma balls lol" + }, + "favicon": "data:image/png;base64,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" +}"#, + ); + let packet_id = MCVarInt::from(0x00); + let packet_len = MCVarInt::from( + packet_id.to_bytes().len() as i32 + response.to_bytes().len() as i32, + ); + for b in packet_len.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + for b in packet_id.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + for b in response.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + // Read the ping packet. + let (_ping_packet_len, _ping_packet_id) = read_packet_header(&mut gc.stream)?; + let num = MCLong::from_stream(&mut gc.stream)?; + log.info(&format!("Ping number: {:?}", num)); + // Send the pong packet. + let packet_id = MCVarInt::from(0x01); + let packet_len = MCVarInt::from(packet_id.to_bytes().len() as i32 + 8i32); + for b in packet_len.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + for b in packet_id.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + for b in num.to_bytes() { + write_byte(&mut gc.stream, b)?; + } + gc.state = GameState::Closed; + } + GameState::Login => { + // Read the login start packet. + let (_packet_len, packet_id) = read_packet_header(&mut gc.stream)?; + let login = LoginStart::read(&mut gc.stream)?; + log.info(&format!("{:?}", login)); + } + GameState::Play => {} + GameState::Closed => { + log.info(&format!( + "Client at {} closed connection", + gc.stream.peer_addr().unwrap() + )); + break 'main; + } + } + } + + Ok(()) +} + +// pub struct MCPacket { +// pub id: MCVarInt, +// pub data: Vec, +// } +// #[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) { +// for b in v { +// self.data.push(b); +// } +// } +// pub fn to_bytes(&self) -> Vec { +// 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 { @@ -58,157 +177,5 @@ pub enum GameState { 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," - }) - .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(()) + pub protocol_version: u16, } diff --git a/src/old_net.rs b/src/old_net.rs new file mode 100644 index 0000000..df7b052 --- /dev/null +++ b/src/old_net.rs @@ -0,0 +1,214 @@ +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, +} +#[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) { + for b in v { + self.data.push(b); + } + } + pub fn to_bytes(&self) -> Vec { + 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," + }) + .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(()) +} diff --git a/src/protocol.rs b/src/protocol.rs new file mode 100644 index 0000000..74d5081 --- /dev/null +++ b/src/protocol.rs @@ -0,0 +1,75 @@ +use crate::mctypes::*; +use std::net::TcpStream; + +pub fn read_packet_header(t: &mut TcpStream) -> std::io::Result<(MCVarInt, MCVarInt)> { + let length = MCVarInt::from_stream(t)?; + let id = MCVarInt::from_stream(t)?; + Ok((length, id)) +} + +#[derive(Debug)] +pub struct Handshake { + pub protocol_version: MCVarInt, + pub server_address: MCString, + pub server_port: MCUnsignedShort, + pub next_state: MCVarInt, +} +impl Handshake { + pub fn new( + protocol_version: MCVarInt, + server_address: MCString, + server_port: MCUnsignedShort, + next_state: MCVarInt, + ) -> Handshake { + Handshake { + protocol_version, + server_address, + server_port, + next_state, + } + } + pub fn read(t: &mut TcpStream) -> std::io::Result { + let protocol_version = MCVarInt::from_stream(t)?; + let server_address = MCString::from_stream(t)?; + let server_port = MCUnsignedShort::from_stream(t)?; + let next_state = MCVarInt::from_stream(t)?; + Ok(Handshake::new( + protocol_version, + server_address, + server_port, + next_state, + )) + } + pub fn to_bytes(&self) -> Vec { + let mut bytes = Vec::new(); + for b in self.protocol_version.to_bytes() { + bytes.push(b); + } + for b in self.server_address.to_bytes() { + bytes.push(b); + } + for b in self.server_port.to_bytes() { + bytes.push(b); + } + for b in self.next_state.to_bytes() { + bytes.push(b); + } + bytes + } +} + +#[derive(Debug)] +pub struct LoginStart { + pub username: MCString, +} +impl LoginStart { + pub fn new(username: MCString) -> LoginStart { + LoginStart { username } + } + pub fn read(t: &mut TcpStream) -> std::io::Result { + Ok(LoginStart::new(MCString::from_stream(t)?)) + } + pub fn to_bytes(&self) -> Vec { + self.username.to_bytes() + } +}