reorganize into different crates

Should help to get a more clear view and simplify things
This commit is contained in:
Snowiiii 2024-08-02 10:57:38 +02:00
parent 5a1d333f02
commit fd9344a512
34 changed files with 258 additions and 170 deletions

32
Cargo.lock generated
View File

@ -521,17 +521,14 @@ dependencies = [
name = "pumpkin"
version = "0.1.0"
dependencies = [
"aes",
"base64",
"bytes",
"cfb8",
"crossbeam-channel",
"fastnbt",
"fastsnbt",
"flate2",
"image",
"log",
"mio",
"pumpkin-protocol",
"pumpkin-registry",
"rand",
"rsa",
"rsa-der",
@ -543,6 +540,31 @@ dependencies = [
"uuid",
]
[[package]]
name = "pumpkin-protocol"
version = "0.1.0"
dependencies = [
"aes",
"bytes",
"cfb8",
"log",
"serde",
"serde_json",
"thiserror",
"uuid",
]
[[package]]
name = "pumpkin-registry"
version = "0.1.0"
dependencies = [
"fastnbt",
"fastsnbt",
"pumpkin-protocol",
"serde",
"serde_json",
]
[[package]]
name = "quote"
version = "1.0.36"

View File

@ -1,7 +1,6 @@
[workspace]
resolver = "2"
members = [
"pumpkin/"]
members = [ "pumpkin-protocol/", "pumpkin-registry/", "pumpkin/"]
[workspace.package]
version = "0.1.0"

View File

@ -0,0 +1,20 @@
[package]
name = "pumpkin-protocol"
version.workspace = true
edition.workspace = true
[dependencies]
bytes = "1.7"
uuid = "1.10"
serde = { version = "1.0", features = ["derive"] }
# to parse strings to json responses
serde_json = "1.0"
thiserror = "1.0.63"
log = "0.4"
# encryption
aes = "0.8.4"
cfb8 = "0.8.1"

View File

@ -0,0 +1,32 @@
### Pumpkin Protocol
Contains all Serverbound(Client->Server) and Clientbound(Server->Client) Packets.
The packets are sorted by their folder architecture you find:
Serverbound Packets under:
`server`
Clientbound Packets under:
`client`
Than they both have folders for packets for all their diffrent states:
### States:
**Handshake**: Always the first packet being send from the Client. This begins also determins the next state, usally to indicate if the player thans perform a Status Request, Join the Server or wants to be transfered.
**Status**: Indicates the Client wants to see a Status response (MOTD).
**Login**: The Login sequence. Indicates the Client wants to join to the Server
**Config**: A sequence of Configuration packets beining mostly send from the Server to the Client. (Features, Resource Pack, Server Links...)
**Play**: The final state which indicate the Player is now ready to Join in also used to handle all other Gameplay packets.
### How to add a Packet ?
You can find all Minecraft Java packets at https://wiki.vg/Protocol. There you also can see in which [State](State) they are.
You also can see all the information the Packets has which we can either Write or Read depending if its Serverbound or Clientbound
#### Adding a Serverbound Packet
Serverbound Packets do use the trait `ServerPacket` which has an packet id (use hex) and can read incoming Client packets.
https://wiki.vg/Protocol gives you the Packet structure which you can than read. Feel free to orientate on already existing Packets.
Please use a name structure like `SPacketName` for the struct, The 'S' representing its Serverbound
#### Adding a Clientbound Packet
Clientbound Packets do use the trait `ClientPacket` which has an packet id (use hex) and can write outgoining Server packets.
https://wiki.vg/Protocol gives you the Packet structure which you can than write. Feel free to orientate on already existing Packets.
Please use a name structure like `CPacketName` for the struct, The 'C' representing its Clientbound

View File

@ -3,7 +3,7 @@ use std::io::{self, Error, ErrorKind};
use bytes::{Buf, BufMut, BytesMut};
use crate::protocol::{VarInt, VarLong};
use crate::{VarInt, VarLong};
const SEGMENT_BITS: u8 = 0x7F;
const CONTINUE_BIT: u8 = 0x80;

View File

@ -1,11 +1,11 @@
use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, KnownPack, VarInt};
use crate::{bytebuf::ByteBuffer, ClientPacket, KnownPack, VarInt};
pub struct CCookieRequest {
// TODO
}
impl ClientPacket for CCookieRequest {
const PACKET_ID: crate::protocol::VarInt = 0x00;
const PACKET_ID: VarInt = 0x00;
fn write(&self, bytebuf: &mut ByteBuffer) {}
}
@ -41,7 +41,7 @@ impl<'a> CConfigDisconnect<'a> {
}
impl<'a> ClientPacket for CConfigDisconnect<'a> {
const PACKET_ID: crate::protocol::VarInt = 0x02;
const PACKET_ID: VarInt = 0x02;
fn write(&self, bytebuf: &mut ByteBuffer) {
bytebuf.put_string(self.reason);
@ -63,7 +63,7 @@ impl CFinishConfig {
}
impl ClientPacket for CFinishConfig {
const PACKET_ID: crate::protocol::VarInt = 0x03;
const PACKET_ID: VarInt = 0x03;
fn write(&self, _bytebuf: &mut ByteBuffer) {}
}

View File

@ -1,4 +1,4 @@
use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt};
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
pub struct CLoginDisconnect<'a> {
reason: &'a str,

View File

@ -1,7 +1,4 @@
use crate::{
entity::player::GameMode,
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt},
};
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
pub struct SetHeldItem {
slot: i8,
@ -80,8 +77,8 @@ pub struct CLogin {
dimension_type: VarInt,
dimension_name: String,
hashed_seed: i64,
game_mode: GameMode,
previous_gamemode: GameMode,
game_mode: u8,
previous_gamemode: i8,
debug: bool,
is_flat: bool,
has_death_loc: bool,
@ -107,8 +104,8 @@ impl CLogin {
dimension_type: VarInt,
dimension_name: String,
hashed_seed: i64,
game_mode: GameMode,
previous_gamemode: GameMode,
game_mode: u8,
previous_gamemode: i8,
debug: bool,
is_flat: bool,
has_death_loc: bool,
@ -161,8 +158,8 @@ impl ClientPacket for CLogin {
bytebuf.put_var_int(self.dimension_type);
bytebuf.put_string(&self.dimension_name);
bytebuf.put_i64(self.hashed_seed);
bytebuf.put_u8(self.game_mode.to_byte() as u8);
bytebuf.put_i8(self.previous_gamemode.to_byte());
bytebuf.put_u8(self.game_mode);
bytebuf.put_i8(self.previous_gamemode);
bytebuf.put_bool(self.debug);
bytebuf.put_bool(self.is_flat);
bytebuf.put_bool(self.has_death_loc);

View File

@ -1,4 +1,4 @@
use crate::protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt};
use crate::{bytebuf::ByteBuffer, ClientPacket, VarInt};
pub struct CPingResponse {
payload: i64, // must responde with the same as in `SPingRequest`

View File

@ -6,12 +6,16 @@ use serde::Serialize;
use thiserror::Error;
pub mod bytebuf;
pub mod registry;
pub mod client;
pub mod server;
pub mod packet_decoder;
pub mod packet_encoder;
pub const MAX_PACKET_SIZE: i32 = 2097152;
pub type VarInt = i32;
pub type VarLong = i64;
pub struct VarInt32(pub i32);
@ -87,7 +91,23 @@ pub enum VarIntDecodeError {
TooLarge,
}
pub type VarLong = i64;
#[derive(Error, Debug)]
pub enum PacketError {
#[error("failed to decode packet ID")]
DecodeID,
#[error("failed to encode packet ID")]
EncodeID,
#[error("failed to write encoded packet")]
EncodeFailedWrite,
#[error("failed to write encoded packet to connection")]
ConnectionWrite,
#[error("packet exceeds maximum length")]
TooLong,
#[error("packet length is out of bounds")]
OutOfBounds,
#[error("malformed packet length VarInt")]
MailformedLength,
}
#[derive(Debug, PartialEq)]
pub enum ConnectionState {
@ -125,6 +145,12 @@ pub trait ClientPacket {
fn write(&self, bytebuf: &mut ByteBuffer);
}
pub trait ServerPacket {
const PACKET_ID: VarInt;
fn read(bytebuf: &mut ByteBuffer) -> Self;
}
#[derive(Serialize)]
pub struct StatusResponse {
pub version: Version,

View File

@ -2,8 +2,7 @@ use aes::cipher::{generic_array::GenericArray, BlockDecryptMut, BlockSizeUser, K
use bytes::{Buf, BytesMut};
use crate::{
client::{PacketError, MAX_PACKET_SIZE},
protocol::{bytebuf::ByteBuffer, RawPacket, VarInt32, VarIntDecodeError},
bytebuf::ByteBuffer, PacketError, RawPacket, VarInt32, VarIntDecodeError, MAX_PACKET_SIZE,
};
type Cipher = cfb8::Decryptor<aes::Aes128>;

View File

@ -4,12 +4,10 @@ use aes::cipher::{generic_array::GenericArray, BlockEncryptMut, BlockSizeUser, K
use bytes::{BufMut, BytesMut};
use crate::{
client::MAX_PACKET_SIZE,
protocol::{bytebuf::ByteBuffer, ClientPacket, VarInt32},
bytebuf::ByteBuffer, ClientPacket, PacketError, VarInt32,
MAX_PACKET_SIZE,
};
use super::PacketError;
type Cipher = cfb8::Encryptor<aes::Aes128>;
// Encoder: Server -> Client

View File

@ -1,30 +1,27 @@
use crate::{
entity::player::{ChatMode, Hand},
protocol::{bytebuf::ByteBuffer, VarInt},
};
use crate::{bytebuf::ByteBuffer, ServerPacket, VarInt};
pub struct SClientInformation {
pub locale: String, // 16
pub view_distance: i8,
pub chat_mode: ChatMode, // Varint
pub chat_mode: VarInt, // Varint
pub chat_colors: bool,
pub skin_parts: u8,
pub main_hand: Hand,
pub main_hand: VarInt,
pub text_filtering: bool,
pub server_listing: bool,
}
impl SClientInformation {
pub const PACKET_ID: VarInt = 0x00;
impl ServerPacket for SClientInformation {
const PACKET_ID: VarInt = 0x00;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
locale: bytebuf.get_string_len(16).unwrap(),
view_distance: bytebuf.get_i8(),
chat_mode: ChatMode::from_varint(bytebuf.get_var_int()),
chat_mode: bytebuf.get_var_int(),
chat_colors: bytebuf.get_bool(),
skin_parts: bytebuf.get_u8(),
main_hand: Hand::from_varint(bytebuf.get_var_int()),
main_hand: bytebuf.get_var_int(),
text_filtering: bytebuf.get_bool(),
server_listing: bytebuf.get_bool(),
}
@ -33,10 +30,10 @@ impl SClientInformation {
pub struct SAcknowledgeFinishConfig {}
impl SAcknowledgeFinishConfig {
pub const PACKET_ID: VarInt = 0x03;
impl ServerPacket for SAcknowledgeFinishConfig {
const PACKET_ID: VarInt = 0x03;
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {}
}
}
@ -46,10 +43,10 @@ pub struct SKnownPacks {
// known_packs: &'a [KnownPack]
}
impl SKnownPacks {
pub const PACKET_ID: VarInt = 0x07;
impl ServerPacket for SKnownPacks {
const PACKET_ID: VarInt = 0x07;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
known_pack_count: bytebuf.get_var_int(),
}

View File

@ -1,4 +1,4 @@
use crate::protocol::{bytebuf::ByteBuffer, ConnectionState, VarInt};
use crate::{bytebuf::ByteBuffer, ConnectionState, ServerPacket, VarInt};
pub struct SHandShake {
pub protocol_version: VarInt,
@ -7,10 +7,10 @@ pub struct SHandShake {
pub next_state: ConnectionState,
}
impl SHandShake {
pub const PACKET_ID: VarInt = 0x00;
impl ServerPacket for SHandShake {
const PACKET_ID: VarInt = 0x00;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
protocol_version: bytebuf.get_var_int(),
server_address: bytebuf.get_string_len(255).unwrap(),

View File

@ -1,14 +1,14 @@
use crate::protocol::{bytebuf::ByteBuffer, VarInt};
use crate::{bytebuf::ByteBuffer, ServerPacket, VarInt};
pub struct SLoginStart {
pub name: String, // 16
pub uuid: uuid::Uuid,
}
impl SLoginStart {
pub const PACKET_ID: VarInt = 0x00;
impl ServerPacket for SLoginStart {
const PACKET_ID: VarInt = 0x00;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
name: bytebuf.get_string_len(16).unwrap(),
uuid: bytebuf.get_uuid(),
@ -23,10 +23,10 @@ pub struct SEncryptionResponse {
pub verify_token: Vec<u8>,
}
impl SEncryptionResponse {
pub const PACKET_ID: VarInt = 0x01;
impl ServerPacket for SEncryptionResponse {
const PACKET_ID: VarInt = 0x01;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
let shared_secret_length = bytebuf.get_var_int();
let shared_secret = bytebuf.copy_to_bytes(shared_secret_length as usize);
let verify_token_length = bytebuf.get_var_int();
@ -46,10 +46,10 @@ pub struct SLoginPluginResponse<'a> {
data: Option<&'a [u8]>,
}
impl<'a> SLoginPluginResponse<'a> {
pub const PACKET_ID: VarInt = 0x02;
impl<'a> ServerPacket for SLoginPluginResponse<'a> {
const PACKET_ID: VarInt = 0x02;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
message_id: bytebuf.get_var_int(),
successful: bytebuf.get_bool(),
@ -63,10 +63,10 @@ pub struct SLoginAcknowledged {
// empty
}
impl SLoginAcknowledged {
pub const PACKET_ID: VarInt = 0x03;
impl ServerPacket for SLoginAcknowledged {
const PACKET_ID: VarInt = 0x03;
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {}
}
}

View File

@ -0,0 +1,27 @@
use crate::{bytebuf::ByteBuffer, ServerPacket, VarInt};
pub struct SStatusRequest {
// empty
}
impl ServerPacket for SStatusRequest {
const PACKET_ID: VarInt = 0x00;
fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {}
}
}
pub struct SPingRequest {
pub payload: i64,
}
impl ServerPacket for SPingRequest {
const PACKET_ID: VarInt = 0x01;
fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
payload: bytebuf.get_i64(),
}
}
}

View File

@ -0,0 +1,13 @@
[package]
name = "pumpkin-registry"
version.workspace = true
edition.workspace = true
[dependencies]
pumpkin-protocol = { path = "../pumpkin-protocol"}
# nbt
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"

View File

@ -0,0 +1,5 @@
### Pumpkin Registry
Here you find all the registry data we have.
Registries are repositories of data that contain entries pertaining to certain aspects of the game, such as the world, the player, among others.
Registery data usally send by the Clientbound Registery data Packet.
A list of Registry entries can be found at https://wiki.vg/Registry_Data

View File

@ -1,6 +1,5 @@
use crate::protocol::VarInt;
use super::RegistryValue;
use pumpkin_protocol::VarInt;
use serde::Serialize;
#[derive(Debug, Clone, Serialize)]

View File

@ -1,10 +1,9 @@
use dimensions::Dimension;
use fastnbt::SerOpts;
use pumpkin_protocol::bytebuf::ByteBuffer;
use serde::Serialize;
use wolf::WolfVariant;
use super::bytebuf::ByteBuffer;
mod biomes;
mod chat_type;
mod damage_type;

View File

@ -4,32 +4,36 @@ version = "0.1.0"
edition = "2021"
[dependencies]
pumpkin-protocol = { path = "../pumpkin-protocol"}
pumpkin-registry = { path = "../pumpkin-registry"}
# config
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
rand = "0.8.5"
# encryption
rsa = "0.9.6"
rsa-der = "0.3.0"
aes = "0.8.4"
cfb8 = "0.8.1"
flate2 = "1.0.30"
bytes = "1.7"
thiserror = "1.0.63"
# icon loading
base64 = "0.22.1"
image = { version = "0.25", default-features = false, features = ["png"]}
simple_logger = "5.0.0"
log = "0.4"
# networking
mio = { version = "1.0.1", features = ["os-poll", "net"]}
crossbeam-channel = "0.5.13"
uuid = "1.10"
toml = "0.8.17"
fastnbt = { git = "https://github.com/owengage/fastnbt.git" }
fastsnbt = "0.2"
toml = "0.8.19"

View File

@ -1,11 +1,9 @@
use crate::{
protocol::{
use pumpkin_protocol::{
client::{
config::{CFinishConfig, CKnownPacks, CRegistryData, Entry},
login::{CEncryptionRequest, CLoginSuccess},
status::{CPingResponse, CStatusResponse},
},
registry::{DimensionCodec, WolfCodec},
server::{
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
handshake::SHandShake,
@ -13,7 +11,11 @@ use crate::{
status::{SPingRequest, SStatusRequest},
},
ConnectionState, KnownPack,
},
};
use pumpkin_registry::{DimensionCodec, WolfCodec};
use crate::{
entity::player::{ChatMode, Hand},
server::Server,
};
@ -150,10 +152,10 @@ impl ClientPacketProcessor for Client {
self.config = Some(PlayerConfig {
locale: client_information.locale,
view_distance: client_information.view_distance,
chat_mode: client_information.chat_mode,
chat_mode: ChatMode::from_varint(client_information.chat_mode),
chat_colors: client_information.chat_colors,
skin_parts: client_information.skin_parts,
main_hand: client_information.main_hand,
main_hand: Hand::from_varint(client_information.main_hand),
text_filtering: client_information.text_filtering,
server_listing: client_information.server_listing,
});

View File

@ -6,23 +6,23 @@ use std::{
use crate::{
entity::player::{ChatMode, Hand, Player},
protocol::{
server::Server,
};
use mio::{event::Event, net::TcpStream, Token};
use pumpkin_protocol::{
client::{config::CConfigDisconnect, login::CLoginDisconnect},
packet_decoder::PacketDecoder,
packet_encoder::PacketEncoder,
server::{
config::{SAcknowledgeFinishConfig, SClientInformation, SKnownPacks},
handshake::SHandShake,
login::{SEncryptionResponse, SLoginAcknowledged, SLoginPluginResponse, SLoginStart},
status::{SPingRequest, SStatusRequest},
},
ClientPacket, RawPacket,
},
server::Server,
ClientPacket, ConnectionState, PacketError, RawPacket, ServerPacket,
};
use crate::protocol::ConnectionState;
use mio::{event::Event, net::TcpStream, Token};
use packet_decoder::PacketDecoder;
use packet_encoder::PacketEncoder;
use rsa::Pkcs1v15Encrypt;
use std::io::Read;
use thiserror::Error;
@ -30,13 +30,8 @@ use thiserror::Error;
mod client_packet;
mod player_packet;
mod packet_decoder;
mod packet_encoder;
use client_packet::ClientPacketProcessor;
pub const MAX_PACKET_SIZE: i32 = 2097152;
pub struct PlayerConfig {
locale: String, // 16
view_distance: i8,
@ -133,14 +128,14 @@ impl Client {
dbg!("Handling packet");
let bytebuf = &mut packet.bytebuf;
match self.connection_state {
crate::protocol::ConnectionState::HandShake => match packet.id {
pumpkin_protocol::ConnectionState::HandShake => match packet.id {
SHandShake::PACKET_ID => self.handle_handshake(server, SHandShake::read(bytebuf)),
_ => log::error!(
"Failed to handle packet id {} while in Handshake state",
packet.id
),
},
crate::protocol::ConnectionState::Status => match packet.id {
pumpkin_protocol::ConnectionState::Status => match packet.id {
SStatusRequest::PACKET_ID => {
self.handle_status_request(server, SStatusRequest::read(bytebuf))
}
@ -152,7 +147,7 @@ impl Client {
packet.id
),
},
crate::protocol::ConnectionState::Login => match packet.id {
pumpkin_protocol::ConnectionState::Login => match packet.id {
SLoginStart::PACKET_ID => {
self.handle_login_start(server, SLoginStart::read(bytebuf))
}
@ -170,7 +165,7 @@ impl Client {
packet.id
),
},
crate::protocol::ConnectionState::Config => match packet.id {
pumpkin_protocol::ConnectionState::Config => match packet.id {
SClientInformation::PACKET_ID => {
self.handle_client_information(server, SClientInformation::read(bytebuf))
}
@ -270,24 +265,6 @@ pub enum EncryptionError {
SharedWrongLength,
}
#[derive(Error, Debug)]
pub enum PacketError {
#[error("failed to decode packet ID")]
DecodeID,
#[error("failed to encode packet ID")]
EncodeID,
#[error("failed to write encoded packet")]
EncodeFailedWrite,
#[error("failed to write encoded packet to connection")]
ConnectionWrite,
#[error("packet exceeds maximum length")]
TooLong,
#[error("packet length is out of bounds")]
OutOfBounds,
#[error("malformed packet length VarInt")]
MailformedLength,
}
fn would_block(err: &io::Error) -> bool {
err.kind() == io::ErrorKind::WouldBlock
}

View File

@ -1,4 +1,4 @@
use crate::protocol::VarInt;
use pumpkin_protocol::VarInt;
use super::{Entity, EntityId};

View File

@ -12,7 +12,6 @@ const SERVER: Token = Token(0);
pub mod client;
pub mod configuration;
pub mod entity;
pub mod protocol;
pub mod server;
pub mod util;
pub mod world;

View File

@ -1,27 +0,0 @@
use crate::protocol::{bytebuf::ByteBuffer, VarInt};
pub struct SStatusRequest {
// empty
}
impl SStatusRequest {
pub const PACKET_ID: VarInt = 0x00;
pub fn read(_bytebuf: &mut ByteBuffer) -> Self {
Self {}
}
}
pub struct SPingRequest {
pub payload: i64,
}
impl SPingRequest {
pub const PACKET_ID: VarInt = 0x01;
pub fn read(bytebuf: &mut ByteBuffer) -> Self {
Self {
payload: bytebuf.get_i64(),
}
}
}

View File

@ -5,20 +5,20 @@ use std::{
use base64::{engine::general_purpose, Engine};
use mio::{event::Event, Poll};
use pumpkin_protocol::{
client::{config::CPluginMessage, play::CLogin},
PacketError, Players, Sample, StatusResponse, VarInt, VarInt32, Version,
};
use rsa::{rand_core::OsRng, traits::PublicKeyParts, RsaPrivateKey, RsaPublicKey};
use serde::{Deserialize, Serialize};
use crate::{
client::{Client, PacketError},
client::Client,
configuration::{AdvancedConfiguration, BasicConfiguration},
entity::{
player::{GameMode, Player},
Entity, EntityId,
},
protocol::{
client::{config::CPluginMessage, play::CLogin},
Players, Sample, StatusResponse, VarInt, VarInt32, Version,
},
world::World,
};
@ -117,8 +117,8 @@ impl Server {
1,
"minecraft:overworld".into(),
0, // seed
GameMode::Survival,
GameMode::Undefined,
GameMode::Survival.to_byte() as u8,
GameMode::Undefined.to_byte(),
false,
false,
false, // deth loc