Skip to content

Commit 0e9858e

Browse files
fix: download AllMaps by TCP to prevent issues with large data over UDP (#85)
1 parent 04077c9 commit 0e9858e

File tree

17 files changed

+313
-201
lines changed

17 files changed

+313
-201
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3636
- fix players not disappearing when disconnecting
3737
- combat can no longer occur after player has moved away from target
3838
- fix potential server crash when calculating loop sleep duration
39-
- temporary fix - retry mechanism for client map download from server on unreliable connections
39+
- client map download now via tcp to avoid issues with large data over unreliable connections on udp
4040

4141
### 🧰 Maintenance
4242
- updated dependencies

Cargo.lock

Lines changed: 68 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rustyhack_client/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,5 +26,5 @@ serde = { version = "1.0.142", features = ["derive"] }
2626
bincode = "1.3.3"
2727
regex = "1.6.0"
2828
chrono = "0.4.20"
29-
itertools = "0.10.3"
3029
rayon = "1.5.3"
30+
message-io = { version = "0.14.6", default-features = false, features = ["tcp"] }

rustyhack_client/src/client_game.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub(super) fn run(
2525
sender: &Sender<Packet>,
2626
receiver: Receiver<SocketEvent>,
2727
server_addr: &str,
28+
server_tcp_addr: &str,
2829
client_addr: &str,
2930
player_name: &str,
3031
) {
@@ -37,13 +38,7 @@ pub(super) fn run(
3738
);
3839

3940
//get basic data from server needed to start client_game
40-
//todo fix properly - repeating the request when it fails is a temporary fix
41-
let mut all_maps_option = None;
42-
while all_maps_option.is_none() {
43-
all_maps_option =
44-
map_downloader::request_all_maps_data(sender, server_addr, &player_update_receiver);
45-
}
46-
let all_maps = all_maps_option.unwrap();
41+
let all_maps = map_downloader::request_all_maps_data(server_tcp_addr);
4742

4843
//create player
4944
let mut player = new_player::send_new_player_request(

rustyhack_client/src/client_network_messages/map_downloader.rs

Lines changed: 65 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,87 @@
11
use bincode::{deserialize, serialize};
2-
use crossbeam_channel::{Receiver, Sender};
3-
use itertools::Itertools;
4-
use laminar::Packet;
2+
use message_io::network::{NetEvent, Transport};
3+
use message_io::node;
4+
use message_io::node::NodeEvent;
55
use rustyhack_lib::background_map::AllMaps;
66
use rustyhack_lib::network::packets::{PlayerRequest, ServerMessage};
77
use std::collections::HashMap;
8-
use std::thread;
9-
use std::time::Duration;
8+
use std::process;
109

11-
pub(crate) fn request_all_maps_data(
12-
sender: &Sender<Packet>,
13-
server_addr: &str,
14-
channel_receiver: &Receiver<ServerMessage>,
15-
) -> Option<AllMaps> {
16-
let get_all_maps_request_packet = Packet::reliable_ordered(
17-
server_addr
18-
.parse()
19-
.expect("Server address format is invalid."),
20-
serialize(&PlayerRequest::GetChunkedAllMaps)
21-
.expect("Error serializing GetAllMaps request."),
22-
Some(1),
23-
);
24-
rustyhack_lib::network::send_packet(get_all_maps_request_packet, sender);
25-
info!("Requested all maps data from server.");
26-
wait_for_all_maps_response(channel_receiver)
27-
}
10+
pub(crate) fn request_all_maps_data(server_tcp_addr: &str) -> AllMaps {
11+
let mut all_maps = HashMap::new();
12+
let (handler, listener) = node::split();
2813

29-
fn wait_for_all_maps_response(channel_receiver: &Receiver<ServerMessage>) -> Option<AllMaps> {
30-
let mut all_maps_downloaded = false;
31-
let mut all_maps = None;
32-
let mut all_maps_chunks = HashMap::new();
33-
loop {
34-
let received = channel_receiver.recv();
35-
if let Ok(received_message) = received {
36-
match received_message {
37-
ServerMessage::AllMapsChunk(message) => {
38-
info!("All maps chunk received from server: {}", message.0);
39-
all_maps_chunks.insert(message.0, message.1);
40-
}
41-
ServerMessage::AllMapsChunksComplete => {
42-
info!("All maps chunks downloaded from server.");
43-
all_maps = combine_all_maps_chunks(&all_maps_chunks);
44-
all_maps_downloaded = true;
45-
}
46-
_ => {
47-
info!(
48-
"Ignoring other message types until maps downloaded. {:?}",
49-
received_message
50-
);
14+
//connect to server tcp port
15+
let (server, _) = handler
16+
.network()
17+
.connect(Transport::FramedTcp, server_tcp_addr)
18+
.unwrap_or_else(|err| {
19+
error!(
20+
"Unable to connect to server {}, error: {}",
21+
&server_tcp_addr, err
22+
);
23+
process::exit(1);
24+
});
25+
26+
//send GetAllMaps request and wait for response
27+
listener.for_each(|event| match event {
28+
NodeEvent::Network(net_event) => match net_event {
29+
NetEvent::Connected(_endpoint, _ok) => {
30+
info!("Sending GetAllMaps request to server.");
31+
handler.signals().send(PlayerRequest::GetAllMaps);
32+
}
33+
NetEvent::Accepted(_, _) => unreachable!(), // Only generated by listening
34+
NetEvent::Message(_endpoint, data) => {
35+
info!("Received raw AllMaps data from server.");
36+
let all_maps_option = deserialize_all_maps_reply(data);
37+
match all_maps_option {
38+
None => {
39+
info!("Sending GetAllMaps request to server.");
40+
handler.signals().send(PlayerRequest::GetAllMaps);
41+
}
42+
Some(all_maps_data) => {
43+
all_maps = all_maps_data;
44+
handler.stop();
45+
handler.network().remove(server.resource_id());
46+
}
5147
}
5248
}
53-
}
54-
if all_maps_downloaded {
55-
info!("Got all maps data.");
56-
break;
57-
}
58-
thread::sleep(Duration::from_millis(1));
59-
}
60-
debug!("All maps is: {:?}", all_maps);
49+
NetEvent::Disconnected(_endpoint) => {
50+
error!("Server is disconnected.");
51+
handler.stop();
52+
handler.network().remove(server.resource_id());
53+
process::exit(1);
54+
}
55+
},
56+
NodeEvent::Signal(signal) => match signal {
57+
PlayerRequest::GetAllMaps => {
58+
handler
59+
.network()
60+
.send(server, &serialize(&PlayerRequest::GetAllMaps).unwrap());
61+
}
62+
_ => {
63+
warn!("Ignoring invalid PlayerRequest on tcp channel.");
64+
}
65+
},
66+
});
6167
all_maps
6268
}
6369

64-
fn combine_all_maps_chunks(all_maps_chunks: &HashMap<usize, Vec<u8>>) -> Option<AllMaps> {
65-
deserialize_all_maps_reply(&combine_chunks(all_maps_chunks))
66-
}
67-
68-
fn combine_chunks(all_maps_chunks: &HashMap<usize, Vec<u8>>) -> Vec<u8> {
69-
let mut combined_all_maps_chunks: Vec<u8> = Vec::new();
70-
for chunk in all_maps_chunks.keys().sorted() {
71-
combined_all_maps_chunks.extend(
72-
all_maps_chunks
73-
.get(chunk)
74-
.expect("Error combining all maps chunks on chunk."),
75-
);
76-
}
77-
combined_all_maps_chunks
78-
}
79-
80-
fn deserialize_all_maps_reply(combined_chunks: &[u8]) -> Option<AllMaps> {
81-
let deserialized_chunks = deserialize::<ServerMessage>(combined_chunks);
82-
match deserialized_chunks {
70+
fn deserialize_all_maps_reply(data: &[u8]) -> Option<AllMaps> {
71+
let deserialized_data = deserialize::<ServerMessage>(data);
72+
match deserialized_data {
8373
Ok(deserialized) => {
8474
if let ServerMessage::AllMaps(all_maps) = deserialized {
75+
info!("AllMaps data downloaded successfully.");
8576
Some(all_maps)
8677
} else {
87-
warn!("Deserialized message from server was not all maps, will request again.");
78+
warn!("Deserialized message from server was not valid AllMaps data, will request again.");
8879
None
8980
}
9081
}
9182
Err(error) => {
92-
warn!(
93-
"Error deserializing all maps from server, will request again. {}",
83+
error!(
84+
"Error deserializing AllMaps from server, will request again. {}",
9485
error.to_string()
9586
);
9687
None

0 commit comments

Comments
 (0)