Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ development is not in lock-step with the server version. The level of
support varies, but the goal is to support major versions from 1.7.10
up to the current latest major version. Occasionally, snapshots are also supported.

Forge servers are currently supported on 1.7.10 - 1.12.2.
Forge servers are supported on 1.7.10 - 1.12.2 (FML) and 1.13.2 - 1.16.5 (FML2).

Support for older protocols will _not_ be dropped as newer protocols are added.

Expand Down
4 changes: 2 additions & 2 deletions protocol/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

118 changes: 118 additions & 0 deletions protocol/src/protocol/forge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,3 +191,121 @@ impl Serializable for FmlHs {
}
}
}

pub mod fml2 {
// https://wiki.vg/Minecraft_Forge_Handshake#FML2_protocol_.281.13_-_Current.29
use super::*;

#[derive(Clone, Default, Debug)]
pub struct Channel {
pub name: String,
pub version: String,
}

impl Serializable for Channel {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
Ok(Channel {
name: Serializable::read_from(buf)?,
version: Serializable::read_from(buf)?,
})
}

fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
self.name.write_to(buf)?;
self.version.write_to(buf)
}
}

#[derive(Clone, Default, Debug)]
pub struct Registry {
pub name: String,
pub marker: String,
}

impl Serializable for Registry {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error> {
Ok(Registry {
name: Serializable::read_from(buf)?,
marker: "".to_string(), // not in ModList
})
}

fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
self.name.write_to(buf)?;
self.marker.write_to(buf)
}
}

#[derive(Debug)]
pub enum FmlHandshake {
ModList {
mod_names: LenPrefixed<VarInt, String>,
channels: LenPrefixed<VarInt, Channel>,
registries: LenPrefixed<VarInt, Registry>,
},

ModListReply {
mod_names: LenPrefixed<VarInt, String>,
channels: LenPrefixed<VarInt, Channel>,
registries: LenPrefixed<VarInt, Registry>,
},

ServerRegistry {
name: String,
snapshot_present: bool,
snapshot: Vec<u8>,
},

ConfigurationData {
filename: String,
contents: Vec<u8>,
},

Acknowledgement,
}

impl FmlHandshake {
pub fn packet_by_id<R: io::Read>(id: i32, buf: &mut R) -> Result<Self, Error> {
Ok(match id {
1 => FmlHandshake::ModList {
mod_names: Serializable::read_from(buf)?,
channels: Serializable::read_from(buf)?,
registries: Serializable::read_from(buf)?,
},
3 => FmlHandshake::ServerRegistry {
name: Serializable::read_from(buf)?,
snapshot_present: Serializable::read_from(buf)?,
snapshot: Serializable::read_from(buf)?,
},
4 => FmlHandshake::ConfigurationData {
filename: Serializable::read_from(buf)?,
contents: Serializable::read_from(buf)?,
},
_ => unimplemented!(),
})
}
}

impl Serializable for FmlHandshake {
fn read_from<R: io::Read>(_buf: &mut R) -> Result<Self, Error> {
unimplemented!()
}

fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error> {
match self {
FmlHandshake::ModListReply {
mod_names,
channels,
registries,
} => {
VarInt(2).write_to(buf)?;
mod_names.write_to(buf)?;
channels.write_to(buf)?;
registries.write_to(buf)
}
FmlHandshake::Acknowledgement => VarInt(99).write_to(buf),
_ => unimplemented!(),
}
}
}
}
108 changes: 100 additions & 8 deletions protocol/src/protocol/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -971,7 +971,7 @@ pub enum Direction {

/// The protocol has multiple 'sub-protocols' or states which control which
/// packet an id points to.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum State {
Handshaking,
Play,
Expand Down Expand Up @@ -1036,7 +1036,7 @@ pub struct Conn {

cipher: Option<Aes128Cfb>,

compression_threshold: i32,
pub compression_threshold: i32,
}

impl Conn {
Expand Down Expand Up @@ -1099,17 +1099,91 @@ impl Conn {
}
self.write_all(&buf)?;

Result::Ok(())
Ok(())
}

pub fn read_packet(&mut self) -> Result<packet::Packet, Error> {
let len = VarInt::read_from(self)?.0 as usize;
pub fn write_plugin_message(&mut self, channel: &str, data: &[u8]) -> Result<(), Error> {
if is_network_debug() {
debug!(
"Sending plugin message: channel={}, data={:?}",
channel, data
);
}
debug_assert!(self.state == State::Play);
if self.protocol_version >= 47 {
self.write_packet(packet::play::serverbound::PluginMessageServerbound {
channel: channel.to_string(),
data: data.to_vec(),
})?;
} else {
self.write_packet(packet::play::serverbound::PluginMessageServerbound_i16 {
channel: channel.to_string(),
data: LenPrefixedBytes::<VarShort>::new(data.to_vec()),
})?;
}

Ok(())
}

pub fn write_fmlhs_plugin_message(&mut self, msg: &forge::FmlHs) -> Result<(), Error> {
let mut buf: Vec<u8> = vec![];
msg.write_to(&mut buf)?;

self.write_plugin_message("FML|HS", &buf)
}

pub fn write_login_plugin_response(
&mut self,
message_id: VarInt,
successful: bool,
data: &[u8],
) -> Result<(), Error> {
if is_network_debug() {
debug!(
"Sending login plugin message: message_id={:?}, successful={:?}, data={:?}",
message_id, successful, data,
);
}
debug_assert!(self.state == State::Login);
self.write_packet(packet::login::serverbound::LoginPluginResponse {
message_id,
successful,
data: data.to_vec(),
})
}

pub fn write_fml2_handshake_plugin_message(
&mut self,
message_id: VarInt,
msg: Option<&forge::fml2::FmlHandshake>,
) -> Result<(), Error> {
if let Some(msg) = msg {
let mut inner_buf: Vec<u8> = vec![];
msg.write_to(&mut inner_buf)?;

let mut outer_buf: Vec<u8> = vec![];
"fml:handshake".to_string().write_to(&mut outer_buf)?;
VarInt(inner_buf.len() as i32).write_to(&mut outer_buf)?;
inner_buf.write_to(&mut outer_buf)?;

self.write_login_plugin_response(message_id, true, &outer_buf)
} else {
unimplemented!() // successful: false, no payload
}
}

#[allow(clippy::type_complexity)]
pub fn read_raw_packet_from<R: io::Read>(
buf: &mut R,
compression_threshold: i32,
) -> Result<(i32, Box<io::Cursor<Vec<u8>>>), Error> {
let len = VarInt::read_from(buf)?.0 as usize;
let mut ibuf = vec![0; len];
self.read_exact(&mut ibuf)?;
buf.read_exact(&mut ibuf)?;

let mut buf = io::Cursor::new(ibuf);

if self.compression_threshold >= 0 {
if compression_threshold >= 0 {
let uncompressed_size = VarInt::read_from(&mut buf)?.0;
if uncompressed_size != 0 {
let mut new = Vec::with_capacity(uncompressed_size as usize);
Expand All @@ -1120,7 +1194,7 @@ impl Conn {
if is_network_debug() {
debug!(
"Decompressed threshold={} len={} uncompressed_size={} to {} bytes",
self.compression_threshold,
compression_threshold,
len,
uncompressed_size,
new.len()
Expand All @@ -1131,6 +1205,13 @@ impl Conn {
}
let id = VarInt::read_from(&mut buf)?.0;

Ok((id, Box::new(buf)))
}

pub fn read_packet(&mut self) -> Result<packet::Packet, Error> {
let compression_threshold = self.compression_threshold;
let (id, mut buf) = Conn::read_raw_packet_from(self, compression_threshold)?;

let dir = match self.direction {
Direction::Clientbound => Direction::Serverbound,
Direction::Serverbound => Direction::Clientbound,
Expand Down Expand Up @@ -1224,6 +1305,7 @@ impl Conn {

// For modded servers, get the list of Forge mods installed
let mut forge_mods: std::vec::Vec<crate::protocol::forge::ForgeMod> = vec![];
let mut fml_network_version: Option<i64> = None;
if let Some(modinfo) = val.get("modinfo") {
if let Some(modinfo_type) = modinfo.get("type") {
if modinfo_type == "FML" {
Expand All @@ -1240,6 +1322,7 @@ impl Conn {
.push(crate::protocol::forge::ForgeMod { modid, version });
}
}
fml_network_version = Some(1);
}
}
} else {
Expand Down Expand Up @@ -1267,6 +1350,13 @@ impl Conn {
}
}
}
fml_network_version = Some(
forge_data
.get("fmlNetworkVersion")
.unwrap()
.as_i64()
.unwrap(),
);
}

Ok((
Expand Down Expand Up @@ -1301,6 +1391,7 @@ impl Conn {
.and_then(Value::as_str)
.map(|v| v.to_owned()),
forge_mods,
fml_network_version,
},
ping,
))
Expand Down Expand Up @@ -1352,6 +1443,7 @@ pub struct Status {
pub description: format::Component,
pub favicon: Option<String>,
pub forge_mods: Vec<crate::protocol::forge::ForgeMod>,
pub fml_network_version: Option<i64>,
}

#[derive(Debug)]
Expand Down
8 changes: 8 additions & 0 deletions protocol/src/protocol/packet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3135,6 +3135,10 @@ pub enum CommandProperty {
EntitySummon,
Dimension,
UUID,
ForgeModId,
ForgeEnum {
cls: String,
},
}

impl Serializable for CommandNode {
Expand Down Expand Up @@ -3264,6 +3268,10 @@ impl Serializable for CommandNode {
"minecraft:entity_summon" => CommandProperty::EntitySummon,
"minecraft:dimension" => CommandProperty::Dimension,
"minecraft:uuid" => CommandProperty::UUID,
"forge:modid" => CommandProperty::ForgeModId,
"forge:enum" => CommandProperty::ForgeEnum {
cls: Serializable::read_from(buf)?,
},
_ => panic!("unsupported command node parser {}", parse),
})
} else {
Expand Down
11 changes: 8 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ pub struct Game {

impl Game {
pub fn connect_to(&mut self, address: &str) {
let (protocol_version, forge_mods) =
let (protocol_version, forge_mods, fml_network_version) =
match protocol::Conn::new(&address, self.default_protocol_version)
.and_then(|conn| conn.do_status())
{
Expand All @@ -100,14 +100,18 @@ impl Game {
"Detected server protocol version {}",
res.0.version.protocol
);
(res.0.version.protocol, res.0.forge_mods)
(
res.0.version.protocol,
res.0.forge_mods,
res.0.fml_network_version,
)
}
Err(err) => {
warn!(
"Error pinging server {} to get protocol version: {:?}, defaulting to {}",
address, err, self.default_protocol_version
);
(self.default_protocol_version, vec![])
(self.default_protocol_version, vec![], None)
}
};

Expand All @@ -127,6 +131,7 @@ impl Game {
&address,
protocol_version,
forge_mods,
fml_network_version,
))
.unwrap();
});
Expand Down
Loading