Multiprotocol support: 1.12.2 and 1.11.2 (#54)

Adds support for connecting to both 1.12.2 and 1.11.2 (protocols 340 and 316) servers

https://github.com/iceiix/steven/issues/18 Enhance protocol support
Closes https://github.com/iceiix/steven/pull/48 1.11.2 protocol support (316)

* Restore create_ids!() macro in packet identifiers

* Add translate_packet_id() function to map external 1.12.2 packet ids to internal sequential ids

* Implement translate_internal_packet_id() from a new protocol_packet_ids! macro

* Move packet IDs to separate file, v1_12_2.rs

* Change supported protocols constant to an array

* Add v1_11_2 protocol packet IDs (from https://github.com/iceiix/steven/pull/48)

* Add keep alive packet variants: _i64 (>=1.12.2) and _VarInt (<=1.11.2)

* Abstract protocol versions, can now connect to both 1.12.2 and 1.11.2

* Send protocol version in handshake packet

* Restore 1.11 (315) protocol support as in original (https://github.com/thinkofname/steven) Steven
This commit is contained in:
iceiix 2018-12-03 14:22:47 -08:00 committed by GitHub
parent be6e1f79f1
commit d03f102bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 404 additions and 145 deletions

View File

@ -96,8 +96,8 @@ impl Game {
if let Some(v) = server_versions_info.get(address) {
v.as_i64().unwrap() as i32
} else {
warn!("Server protocol version not known for {} (no ping response?), defaulting to {}", address, protocol::SUPPORTED_PROTOCOL);
protocol::SUPPORTED_PROTOCOL
warn!("Server protocol version not known for {} (no ping response?), defaulting to {}", address, protocol::SUPPORTED_PROTOCOLS[0]);
protocol::SUPPORTED_PROTOCOLS[0]
}
};
@ -230,7 +230,7 @@ fn main() {
should_close: false,
chunk_builder: chunk_builder::ChunkBuilder::new(resource_manager, textures),
connect_reply: None,
protocol_version: protocol::SUPPORTED_PROTOCOL,
protocol_version: protocol::SUPPORTED_PROTOCOLS[0],
dpi_factor,
last_mouse_x: 0.0,
last_mouse_y: 0.0,

View File

@ -13,6 +13,7 @@
// limitations under the License.
#![allow(dead_code)]
#![allow(non_camel_case_types)]
use aes::Aes128;
use cfb8::Cfb8;
@ -36,7 +37,7 @@ use flate2::Compression;
use std::time::{Instant, Duration};
use crate::shared::Position;
pub const SUPPORTED_PROTOCOL: i32 = 340;
pub const SUPPORTED_PROTOCOLS: [i32; 3] = [340, 316, 315];
/// Helper macro for defining packets
@ -83,130 +84,7 @@ macro_rules! state_packets {
#[allow(non_upper_case_globals)]
pub mod internal_ids {
pub const Handshake: i32 = 0x00;
pub const TeleportConfirm: i32 = 0x00;
pub const TabComplete: i32 = 0x01;
pub const ChatMessage: i32 = 0x02;
pub const ClientStatus: i32 = 0x03;
pub const ClientSettings: i32 = 0x04;
pub const ConfirmTransactionServerbound: i32 = 0x05;
pub const EnchantItem: i32 = 0x06;
pub const ClickWindow: i32 = 0x07;
pub const CloseWindow: i32 = 0x08;
pub const PluginMessageServerbound: i32 = 0x09;
pub const UseEntity: i32 = 0x0a;
pub const KeepAliveServerbound: i32 = 0x0b;
pub const Player: i32 = 0x0c;
pub const PlayerPosition: i32 = 0x0d;
pub const PlayerPositionLook: i32 = 0x0e;
pub const PlayerLook: i32 = 0x0f;
pub const VehicleMove: i32 = 0x10;
pub const SteerBoat: i32 = 0x11;
pub const CraftRecipeRequest: i32 = 0x12;
pub const ClientAbilities: i32 = 0x13;
pub const PlayerDigging: i32 = 0x14;
pub const PlayerAction: i32 = 0x15;
pub const SteerVehicle: i32 = 0x16;
pub const CraftingBookData: i32 = 0x17;
pub const ResourcePackStatus: i32 = 0x18;
pub const AdvancementTab: i32 = 0x19;
pub const HeldItemChange: i32 = 0x1a;
pub const CreativeInventoryAction: i32 = 0x1b;
pub const SetSign: i32 = 0x1c;
pub const ArmSwing: i32 = 0x1d;
pub const SpectateTeleport: i32 = 0x1e;
pub const PlayerBlockPlacement: i32 = 0x1f;
pub const UseItem: i32 = 0x20;
pub const SpawnObject: i32 = 0x00;
pub const SpawnExperienceOrb: i32 = 0x01;
pub const SpawnGlobalEntity: i32 = 0x02;
pub const SpawnMob: i32 = 0x03;
pub const SpawnPainting: i32 = 0x04;
pub const SpawnPlayer: i32 = 0x05;
pub const Animation: i32 = 0x06;
pub const Statistics: i32 = 0x07;
pub const BlockBreakAnimation: i32 = 0x08;
pub const UpdateBlockEntity: i32 = 0x09;
pub const BlockAction: i32 = 0x0a;
pub const BlockChange: i32 = 0x0b;
pub const BossBar: i32 = 0x0c;
pub const ServerDifficulty: i32 = 0x0d;
pub const TabCompleteReply: i32 = 0x0e;
pub const ServerMessage: i32 = 0x0f;
pub const MultiBlockChange: i32 = 0x10;
pub const ConfirmTransaction: i32 = 0x11;
pub const WindowClose: i32 = 0x12;
pub const WindowOpen: i32 = 0x13;
pub const WindowItems: i32 = 0x14;
pub const WindowProperty: i32 = 0x15;
pub const WindowSetSlot: i32 = 0x16;
pub const SetCooldown: i32 = 0x17;
pub const PluginMessageClientbound: i32 = 0x18;
pub const NamedSoundEffect: i32 = 0x19;
pub const Disconnect: i32 = 0x1a;
pub const EntityAction: i32 = 0x1b;
pub const Explosion: i32 = 0x1c;
pub const ChunkUnload: i32 = 0x1d;
pub const ChangeGameState: i32 = 0x1e;
pub const KeepAliveClientbound: i32 = 0x1f;
pub const ChunkData: i32 = 0x20;
pub const Effect: i32 = 0x21;
pub const Particle: i32 = 0x22;
pub const JoinGame: i32 = 0x23;
pub const Maps: i32 = 0x24;
pub const Entity: i32 = 0x25;
pub const EntityMove: i32 = 0x26;
pub const EntityLookAndMove: i32 = 0x27;
pub const EntityLook: i32 = 0x28;
pub const VehicleTeleport: i32 = 0x29;
pub const SignEditorOpen: i32 = 0x2a;
pub const CraftRecipeResponse: i32 = 0x2b;
pub const PlayerAbilities: i32 = 0x2c;
pub const CombatEvent: i32 = 0x2d;
pub const PlayerInfo: i32 = 0x2e;
pub const TeleportPlayer: i32 = 0x2f;
pub const EntityUsedBed: i32 = 0x30;
pub const UnlockRecipes: i32 = 0x31;
pub const EntityDestroy: i32 = 0x32;
pub const EntityRemoveEffect: i32 = 0x33;
pub const ResourcePackSend: i32 = 0x34;
pub const Respawn: i32 = 0x35;
pub const EntityHeadLook: i32 = 0x36;
pub const SelectAdvancementTab: i32 = 0x37;
pub const WorldBorder: i32 = 0x38;
pub const Camera: i32 = 0x39;
pub const SetCurrentHotbarSlot: i32 = 0x3a;
pub const ScoreboardDisplay: i32 = 0x3b;
pub const EntityMetadata: i32 = 0x3c;
pub const EntityAttach: i32 = 0x3d;
pub const EntityVelocity: i32 = 0x3e;
pub const EntityEquipment: i32 = 0x3f;
pub const SetExperience: i32 = 0x40;
pub const UpdateHealth: i32 = 0x41;
pub const ScoreboardObjective: i32 = 0x42;
pub const SetPassengers: i32 = 0x43;
pub const Teams: i32 = 0x44;
pub const UpdateScore: i32 = 0x45;
pub const SpawnPosition: i32 = 0x46;
pub const TimeUpdate: i32 = 0x47;
pub const Title: i32 = 0x48;
pub const SoundEffect: i32 = 0x49;
pub const PlayerListHeaderFooter: i32 = 0x4a;
pub const CollectItem: i32 = 0x4b;
pub const EntityTeleport: i32 = 0x4c;
pub const Advancements: i32 = 0x4d;
pub const EntityProperties: i32 = 0x4e;
pub const EntityEffect: i32 = 0x4f;
pub const LoginStart: i32 = 0x00;
pub const EncryptionResponse: i32 = 0x01;
pub const LoginDisconnect: i32 = 0x00;
pub const EncryptionRequest: i32 = 0x01;
pub const LoginSuccess: i32 = 0x02;
pub const SetInitialCompression: i32 = 0x03;
pub const StatusRequest: i32 = 0x00;
pub const StatusPing: i32 = 0x01;
pub const StatusResponse: i32 = 0x00;
pub const StatusPong: i32 = 0x01;
create_ids!(i32, $($name),*);
}
$(
@ -217,7 +95,9 @@ pub const StatusPong: i32 = 0x01;
impl PacketType for $name {
fn packet_id(&self) -> i32 { internal_ids::$name }
fn packet_id(&self, version: i32) -> i32 {
packet::versions::translate_internal_packet_id_for_version(version, State::$stateName, Direction::$dirName, internal_ids::$name, false)
}
fn write<W: io::Write>(self, buf: &mut W) -> Result<(), Error> {
$(
@ -237,14 +117,15 @@ pub const StatusPong: i32 = 0x01;
/// Returns the packet for the given state, direction and id after parsing the fields
/// from the buffer.
pub fn packet_by_id<R: io::Read>(state: State, dir: Direction, id: i32, mut buf: &mut R) -> Result<Option<Packet>, Error> {
pub fn packet_by_id<R: io::Read>(version: i32, state: State, dir: Direction, id: i32, mut buf: &mut R) -> Result<Option<Packet>, Error> {
match state {
$(
State::$stateName => {
match dir {
$(
Direction::$dirName => {
match id {
let internal_id = packet::versions::translate_internal_packet_id_for_version(version, state, dir, id, true);
match internal_id {
$(
self::$state::$dir::internal_ids::$name => {
use self::$state::$dir::$name;
@ -269,8 +150,52 @@ pub const StatusPong: i32 = 0x01;
}
}
pub mod packet;
#[macro_export]
macro_rules! protocol_packet_ids {
($($state:ident $stateName:ident {
$($dir:ident $dirName:ident {
$(
$(#[$attr:meta])*
$id:expr => $name:ident
)*
})+
})+) => {
use crate::protocol::*;
pub fn translate_internal_packet_id(state: State, dir: Direction, id: i32, to_internal: bool) -> i32 {
match state {
$(
State::$stateName => {
match dir {
$(
Direction::$dirName => {
if to_internal {
match id {
$(
$id => crate::protocol::packet::$state::$dir::internal_ids::$name,
)*
_ => panic!("bad packet id $id in $dir $state"),
}
} else {
match id {
$(
crate::protocol::packet::$state::$dir::internal_ids::$name => $id,
)*
_ => panic!("bad packet internal id $id in $dir $state"),
}
}
}
)*
}
}
)*
}
}
}
}
pub mod packet;
pub mod versions;
pub trait Serializable: Sized {
fn read_from<R: io::Read>(buf: &mut R) -> Result<Self, Error>;
fn write_to<W: io::Write>(&self, buf: &mut W) -> Result<(), Error>;
@ -868,6 +793,7 @@ pub struct Conn {
pub host: String,
pub port: u16,
direction: Direction,
pub protocol_version: i32,
pub state: State,
cipher: Option<Aes128Cfb>,
@ -878,7 +804,7 @@ pub struct Conn {
}
impl Conn {
pub fn new(target: &str) -> Result<Conn, Error> {
pub fn new(target: &str, protocol_version: i32) -> Result<Conn, Error> {
// TODO SRV record support
let mut parts = target.split(':').collect::<Vec<&str>>();
let address = if parts.len() == 1 {
@ -894,6 +820,7 @@ impl Conn {
port: parts[1].parse().unwrap(),
direction: Direction::Serverbound,
state: State::Handshaking,
protocol_version,
cipher: Option::None,
compression_threshold: -1,
compression_read: Option::None,
@ -903,7 +830,7 @@ impl Conn {
pub fn write_packet<T: PacketType>(&mut self, packet: T) -> Result<(), Error> {
let mut buf = Vec::new();
VarInt(packet.packet_id()).write_to(&mut buf)?;
VarInt(packet.packet_id(self.protocol_version)).write_to(&mut buf)?;
packet.write(&mut buf)?;
let mut extra = if self.compression_threshold >= 0 {
@ -963,7 +890,7 @@ impl Conn {
Direction::Serverbound => Direction::Clientbound,
};
let packet = packet::packet_by_id(self.state, dir, id, &mut buf)?;
let packet = packet::packet_by_id(self.protocol_version, self.state, dir, id, &mut buf)?;
match packet {
Some(val) => {
@ -998,7 +925,7 @@ impl Conn {
let host = self.host.clone();
let port = self.port;
self.write_packet(Handshake {
protocol_version: VarInt(SUPPORTED_PROTOCOL),
protocol_version: VarInt(self.protocol_version),
host,
port,
next: VarInt(1),
@ -1131,6 +1058,7 @@ impl Clone for Conn {
port: self.port,
direction: self.direction,
state: self.state,
protocol_version: self.protocol_version,
cipher: Option::None,
compression_threshold: self.compression_threshold,
compression_read: Option::None,
@ -1140,7 +1068,7 @@ impl Clone for Conn {
}
pub trait PacketType {
fn packet_id(&self) -> i32;
fn packet_id(&self, protocol_version: i32) -> i32;
fn write<W: io::Write>(self, buf: &mut W) -> Result<(), Error>;
}

View File

@ -123,9 +123,12 @@ state_packets!(
/// KeepAliveServerbound is sent by a client as a response to a
/// KeepAliveClientbound. If the client doesn't reply the server
/// may disconnect the client.
packet KeepAliveServerbound {
packet KeepAliveServerbound_i64 {
field id: i64 =,
}
packet KeepAliveServerbound_VarInt {
field id: VarInt =,
}
/// PlayerPosition is used to update the player's position.
packet PlayerPosition {
field x: f64 =,
@ -505,9 +508,12 @@ state_packets!(
/// client is still responding and keep the connection open.
/// The client should reply with the KeepAliveServerbound
/// packet setting ID to the same as this one.
packet KeepAliveClientbound {
packet KeepAliveClientbound_i64 {
field id: i64 =,
}
packet KeepAliveClientbound_VarInt {
field id: VarInt =,
}
/// ChunkData sends or updates a single chunk on the client. If New is set
/// then biome data should be sent too.
packet ChunkData {

21
src/protocol/versions.rs Normal file
View File

@ -0,0 +1,21 @@
use crate::protocol::*;
mod v1_12_2;
mod v1_11_2;
pub fn translate_internal_packet_id_for_version(version: i32, state: State, dir: Direction, id: i32, to_internal: bool) -> i32 {
match version {
// https://wiki.vg/Protocol_History
// https://wiki.vg/Protocol_version_numbers#Versions_after_the_Netty_rewrite
// 1.12.2
340 => v1_12_2::translate_internal_packet_id(state, dir, id, to_internal),
// 1.11.2
316 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal),
// 1.11
315 => v1_11_2::translate_internal_packet_id(state, dir, id, to_internal),
_ => panic!("unsupported protocol version"),
}
}

View File

@ -0,0 +1,145 @@
protocol_packet_ids!(
handshake Handshaking {
serverbound Serverbound {
0x00 => Handshake
}
clientbound Clientbound {
}
}
play Play {
serverbound Serverbound {
0x00 => TeleportConfirm
0x01 => TabComplete
0x02 => ChatMessage
0x03 => ClientStatus
0x04 => ClientSettings
0x05 => ConfirmTransactionServerbound
0x06 => EnchantItem
0x07 => ClickWindow
0x08 => CloseWindow
0x09 => PluginMessageServerbound
0x0a => UseEntity
0x0b => KeepAliveServerbound_VarInt
0x0c => PlayerPosition
0x0d => PlayerPositionLook
0x0e => PlayerLook
0x0f => Player
0x10 => VehicleMove
0x11 => SteerBoat
0x12 => ClientAbilities
0x13 => PlayerDigging
0x14 => PlayerAction
0x15 => SteerVehicle
0x16 => ResourcePackStatus
0x17 => HeldItemChange
0x18 => CreativeInventoryAction
0x19 => SetSign
0x1a => ArmSwing
0x1b => SpectateTeleport
0x1c => PlayerBlockPlacement
0x1d => UseItem
}
clientbound Clientbound {
0x00 => SpawnObject
0x01 => SpawnExperienceOrb
0x02 => SpawnGlobalEntity
0x03 => SpawnMob
0x04 => SpawnPainting
0x05 => SpawnPlayer
0x06 => Animation
0x07 => Statistics
0x08 => BlockBreakAnimation
0x09 => UpdateBlockEntity
0x0a => BlockAction
0x0b => BlockChange
0x0c => BossBar
0x0d => ServerDifficulty
0x0e => TabCompleteReply
0x0f => ServerMessage
0x10 => MultiBlockChange
0x11 => ConfirmTransaction
0x12 => WindowClose
0x13 => WindowOpen
0x14 => WindowItems
0x15 => WindowProperty
0x16 => WindowSetSlot
0x17 => SetCooldown
0x18 => PluginMessageClientbound
0x19 => NamedSoundEffect
0x1a => Disconnect
0x1b => EntityAction
0x1c => Explosion
0x1d => ChunkUnload
0x1e => ChangeGameState
0x1f => KeepAliveClientbound_VarInt
0x20 => ChunkData
0x21 => Effect
0x22 => Particle
0x23 => JoinGame
0x24 => Maps
0x25 => EntityMove
0x26 => EntityLookAndMove
0x27 => EntityLook
0x28 => Entity
0x29 => VehicleTeleport
0x2a => SignEditorOpen
0x2b => PlayerAbilities
0x2c => CombatEvent
0x2d => PlayerInfo
0x2e => TeleportPlayer
0x2f => EntityUsedBed
0x30 => EntityDestroy
0x31 => EntityRemoveEffect
0x32 => ResourcePackSend
0x33 => Respawn
0x34 => EntityHeadLook
0x35 => WorldBorder
0x36 => Camera
0x37 => SetCurrentHotbarSlot
0x38 => ScoreboardDisplay
0x39 => EntityMetadata
0x3a => EntityAttach
0x3b => EntityVelocity
0x3c => EntityEquipment
0x3d => SetExperience
0x3e => UpdateHealth
0x3f => ScoreboardObjective
0x40 => SetPassengers
0x41 => Teams
0x42 => UpdateScore
0x43 => SpawnPosition
0x44 => TimeUpdate
0x45 => Title
0x46 => SoundEffect
0x47 => PlayerListHeaderFooter
0x48 => CollectItem
0x49 => EntityTeleport
0x4a => EntityProperties
0x4b => EntityEffect
}
}
login Login {
serverbound Serverbound {
0x00 => LoginStart
0x01 => EncryptionResponse
}
clientbound Clientbound {
0x00 => LoginDisconnect
0x01 => EncryptionRequest
0x02 => LoginSuccess
0x03 => SetInitialCompression
}
}
status Status {
serverbound Serverbound {
0x00 => StatusRequest
0x01 => StatusPing
}
clientbound Clientbound {
0x00 => StatusResponse
0x01 => StatusPong
}
}
);

View File

@ -0,0 +1,152 @@
protocol_packet_ids!(
handshake Handshaking {
serverbound Serverbound {
0x00 => Handshake
}
clientbound Clientbound {
}
}
play Play {
serverbound Serverbound {
0x00 => TeleportConfirm
0x01 => TabComplete
0x02 => ChatMessage
0x03 => ClientStatus
0x04 => ClientSettings
0x05 => ConfirmTransactionServerbound
0x06 => EnchantItem
0x07 => ClickWindow
0x08 => CloseWindow
0x09 => PluginMessageServerbound
0x0a => UseEntity
0x0b => KeepAliveServerbound_i64
0x0c => Player
0x0d => PlayerPosition
0x0e => PlayerPositionLook
0x0f => PlayerLook
0x10 => VehicleMove
0x11 => SteerBoat
0x12 => CraftRecipeRequest
0x13 => ClientAbilities
0x14 => PlayerDigging
0x15 => PlayerAction
0x16 => SteerVehicle
0x17 => CraftingBookData
0x18 => ResourcePackStatus
0x19 => AdvancementTab
0x1a => HeldItemChange
0x1b => CreativeInventoryAction
0x1c => SetSign
0x1d => ArmSwing
0x1e => SpectateTeleport
0x1f => PlayerBlockPlacement
0x20 => UseItem
}
clientbound Clientbound {
0x00 => SpawnObject
0x01 => SpawnExperienceOrb
0x02 => SpawnGlobalEntity
0x03 => SpawnMob
0x04 => SpawnPainting
0x05 => SpawnPlayer
0x06 => Animation
0x07 => Statistics
0x08 => BlockBreakAnimation
0x09 => UpdateBlockEntity
0x0a => BlockAction
0x0b => BlockChange
0x0c => BossBar
0x0d => ServerDifficulty
0x0e => TabCompleteReply
0x0f => ServerMessage
0x10 => MultiBlockChange
0x11 => ConfirmTransaction
0x12 => WindowClose
0x13 => WindowOpen
0x14 => WindowItems
0x15 => WindowProperty
0x16 => WindowSetSlot
0x17 => SetCooldown
0x18 => PluginMessageClientbound
0x19 => NamedSoundEffect
0x1a => Disconnect
0x1b => EntityAction
0x1c => Explosion
0x1d => ChunkUnload
0x1e => ChangeGameState
0x1f => KeepAliveClientbound_i64
0x20 => ChunkData
0x21 => Effect
0x22 => Particle
0x23 => JoinGame
0x24 => Maps
0x25 => Entity
0x26 => EntityMove
0x27 => EntityLookAndMove
0x28 => EntityLook
0x29 => VehicleTeleport
0x2a => SignEditorOpen
0x2b => CraftRecipeResponse
0x2c => PlayerAbilities
0x2d => CombatEvent
0x2e => PlayerInfo
0x2f => TeleportPlayer
0x30 => EntityUsedBed
0x31 => UnlockRecipes
0x32 => EntityDestroy
0x33 => EntityRemoveEffect
0x34 => ResourcePackSend
0x35 => Respawn
0x36 => EntityHeadLook
0x37 => SelectAdvancementTab
0x38 => WorldBorder
0x39 => Camera
0x3a => SetCurrentHotbarSlot
0x3b => ScoreboardDisplay
0x3c => EntityMetadata
0x3d => EntityAttach
0x3e => EntityVelocity
0x3f => EntityEquipment
0x40 => SetExperience
0x41 => UpdateHealth
0x42 => ScoreboardObjective
0x43 => SetPassengers
0x44 => Teams
0x45 => UpdateScore
0x46 => SpawnPosition
0x47 => TimeUpdate
0x48 => Title
0x49 => SoundEffect
0x4a => PlayerListHeaderFooter
0x4b => CollectItem
0x4c => EntityTeleport
0x4d => Advancements
0x4e => EntityProperties
0x4f => EntityEffect
}
}
login Login {
serverbound Serverbound {
0x00 => LoginStart
0x01 => EncryptionResponse
}
clientbound Clientbound {
0x00 => LoginDisconnect
0x01 => EncryptionRequest
0x02 => LoginSuccess
0x03 => SetInitialCompression
}
}
status Status {
serverbound Serverbound {
0x00 => StatusRequest
0x01 => StatusPing
}
clientbound Clientbound {
0x00 => StatusResponse
0x01 => StatusPong
}
}
);

View File

@ -263,7 +263,7 @@ impl ServerList {
// Don't block the main thread whilst pinging the server
thread::spawn(move || {
match protocol::Conn::new(&address).and_then(|conn| conn.do_status()) {
match protocol::Conn::new(&address, protocol::SUPPORTED_PROTOCOLS[0]).and_then(|conn| conn.do_status()) {
Ok(res) => {
let mut desc = res.0.description;
format::convert_legacy(&mut desc);
@ -484,7 +484,7 @@ impl super::Screen for ServerList {
if res.exists {
{
let mut players = s.players.borrow_mut();
let txt = if res.protocol_version == protocol::SUPPORTED_PROTOCOL {
let txt = if protocol::SUPPORTED_PROTOCOLS.contains(&res.protocol_version) {
players.colour.1 = 255;
players.colour.2 = 255;
format!("{}/{}", res.online, res.max)

View File

@ -104,7 +104,7 @@ macro_rules! handle_packet {
impl Server {
pub fn connect(resources: Arc<RwLock<resources::Manager>>, profile: mojang::Profile, address: &str, protocol_version: i32) -> Result<Server, protocol::Error> {
let mut conn = protocol::Conn::new(address)?;
let mut conn = protocol::Conn::new(address, protocol_version)?;
let host = conn.host.clone();
let port = conn.port;
@ -374,7 +374,8 @@ impl Server {
self pck {
JoinGame => on_game_join,
Respawn => on_respawn,
KeepAliveClientbound => on_keep_alive,
KeepAliveClientbound_i64 => on_keep_alive_i64,
KeepAliveClientbound_VarInt => on_keep_alive_varint,
ChunkData => on_chunk_data,
ChunkUnload => on_chunk_unload,
BlockChange => on_block_change,
@ -536,8 +537,14 @@ impl Server {
let _ = self.conn.as_mut().unwrap().write_packet(p); // TODO handle errors
}
fn on_keep_alive(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound) {
self.write_packet(packet::play::serverbound::KeepAliveServerbound {
fn on_keep_alive_i64(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound_i64) {
self.write_packet(packet::play::serverbound::KeepAliveServerbound_i64 {
id: keep_alive.id,
});
}
fn on_keep_alive_varint(&mut self, keep_alive: packet::play::clientbound::KeepAliveClientbound_VarInt) {
self.write_packet(packet::play::serverbound::KeepAliveServerbound_VarInt {
id: keep_alive.id,
});
}