Optimize sending tracked entity data (#163)

This commit is contained in:
Ryan Johnson 2022-11-30 15:55:46 -08:00 committed by GitHub
parent 0e28e5ad05
commit 842ddb4127
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 55 additions and 45 deletions

3
.gitignore vendored
View file

@ -8,7 +8,8 @@ Cargo.lock
/extractor/out /extractor/out
/extractor/classes /extractor/classes
/extractor/run /extractor/run
/performance_tests/rust-mc-bot /benchmarks/rust-mc-bot
/velocity
flamegraph.svg flamegraph.svg
perf.data perf.data
perf.data.old perf.data.old

View file

@ -70,9 +70,9 @@ members = [
"valence_nbt", "valence_nbt",
"valence_protocol", "valence_protocol",
"packet_inspector", "packet_inspector",
"performance_tests/players" "benchmarks/bench_players"
] ]
exclude = ["performance_tests/rust-mc-bot"] exclude = ["benchmarks/rust-mc-bot"]
[profile.dev.package."*"] [profile.dev.package."*"]
opt-level = 3 opt-level = 3

View file

@ -3,7 +3,7 @@
Run the server Run the server
```shell ```shell
cargo r -r -p players cargo r -r -p bench_players
``` ```
In a separate terminal, start [rust-mc-bot](https://github.com/Eoghanmc22/rust-mc-bot). In a separate terminal, start [rust-mc-bot](https://github.com/Eoghanmc22/rust-mc-bot).
@ -25,7 +25,7 @@ run the server like this:
```shell ```shell
# You can also try setting the `CARGO_PROFILE_RELEASE_DEBUG` environment variable to `true`. # You can also try setting the `CARGO_PROFILE_RELEASE_DEBUG` environment variable to `true`.
cargo flamegraph -p players cargo flamegraph -p bench_players
``` ```
Run rust-mc-bot as above, and then stop the server after a few seconds. Flamegraph will generate a flamegraph.svg in the Run rust-mc-bot as above, and then stop the server after a few seconds. Flamegraph will generate a flamegraph.svg in the

View file

@ -1,5 +1,5 @@
[package] [package]
name = "players" name = "bench_players"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"

View file

@ -17,7 +17,6 @@ pub fn main() -> ShutdownResult {
} }
const WITH_PLAYER_ENTITIES: bool = true; const WITH_PLAYER_ENTITIES: bool = true;
const MAX_PLAYERS: usize = 10_000;
struct Game; struct Game;
@ -38,7 +37,7 @@ impl Config for Game {
type InventoryState = (); type InventoryState = ();
fn max_connections(&self) -> usize { fn max_connections(&self) -> usize {
MAX_PLAYERS + 64 10_000
} }
fn connection_mode(&self) -> ConnectionMode { fn connection_mode(&self) -> ConnectionMode {
@ -70,7 +69,7 @@ impl Config for Game {
online_players: -1, online_players: -1,
max_players: -1, max_players: -1,
player_sample: Default::default(), player_sample: Default::default(),
description: "Test server!".into(), description: "Player Benchmark Server".into(),
favicon_png: None, favicon_png: None,
} }
} }
@ -137,7 +136,10 @@ impl Config for Game {
.entities .entities
.insert_with_uuid(EntityKind::Player, client.uuid(), ()) .insert_with_uuid(EntityKind::Player, client.uuid(), ())
{ {
Some((id, _)) => client.state = id, Some((id, entity)) => {
entity.set_world(world_id);
client.state = id
}
None => { None => {
client.disconnect("Conflicting UUID"); client.disconnect("Conflicting UUID");
return false; return false;

View file

@ -460,33 +460,27 @@ pub fn build() -> anyhow::Result<TokenStream> {
} }
} }
pub(super) fn initial_tracked_data(&self) -> Option<Vec<u8>> { pub(super) fn write_initial_tracked_data(&self, buf: &mut Vec<u8>) {
let mut data = Vec::new(); debug_assert!(buf.is_empty());
match self { match self {
#(Self::#concrete_entity_names(e) => e.initial_tracked_data(&mut data),)* #(Self::#concrete_entity_names(e) => e.initial_tracked_data(buf),)*
} }
if data.is_empty() { if !buf.is_empty() {
None buf.push(0xff);
} else {
data.push(0xff);
Some(data)
} }
} }
pub(super) fn updated_tracked_data(&self) -> Option<Vec<u8>> { pub(super) fn write_updated_tracked_data(&self, buf: &mut Vec<u8>) {
let mut data = Vec::new(); debug_assert!(buf.is_empty());
match self { match self {
#(Self::#concrete_entity_names(e) => e.updated_tracked_data(&mut data),)* #(Self::#concrete_entity_names(e) => e.updated_tracked_data(buf),)*
} }
if data.is_empty() { if !buf.is_empty() {
None buf.push(0xff);
} else {
data.push(0xff);
Some(data)
} }
} }

View file

@ -561,14 +561,14 @@ impl<C: Config> LoadedChunk<C> {
} }
/// Queues the chunk data packet for this chunk with the given position. /// Queues the chunk data packet for this chunk with the given position.
pub(crate) fn chunk_data_packet( pub(crate) fn send_chunk_data_packet(
&self, &self,
send: &mut PlayPacketSender, send: &mut PlayPacketSender,
scratch: &mut Vec<u8>, scratch: &mut Vec<u8>,
pos: ChunkPos, pos: ChunkPos,
biome_registry_len: usize, biome_registry_len: usize,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
scratch.clear(); debug_assert!(scratch.is_empty());
for sect in self.sections.iter() { for sect in self.sections.iter() {
sect.non_air_count.encode(&mut *scratch)?; sect.non_air_count.encode(&mut *scratch)?;

View file

@ -201,6 +201,8 @@ pub struct Client<C: Config> {
/// Ensures that we don't allow more connections to the server until the /// Ensures that we don't allow more connections to the server until the
/// client is dropped. /// client is dropped.
_permit: OwnedSemaphorePermit, _permit: OwnedSemaphorePermit,
/// General purpose reusable buffer.
scratch: Vec<u8>,
username: Username<String>, username: Username<String>,
uuid: Uuid, uuid: Uuid,
ip: IpAddr, ip: IpAddr,
@ -297,6 +299,7 @@ impl<C: Config> Client<C> {
send: Some(send), send: Some(send),
recv, recv,
_permit: permit, _permit: permit,
scratch: Vec::new(),
username: ncd.username, username: ncd.username,
uuid: ncd.uuid, uuid: ncd.uuid,
ip: ncd.ip, ip: ncd.ip,
@ -979,6 +982,8 @@ impl<C: Config> Client<C> {
player_lists: &PlayerLists<C>, player_lists: &PlayerLists<C>,
inventories: &Inventories<C>, inventories: &Inventories<C>,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
debug_assert!(self.scratch.is_empty());
let world = match worlds.get(self.world) { let world = match worlds.get(self.world) {
Some(world) => world, Some(world) => world,
None => bail!("client is in an invalid world and must be disconnected"), None => bail!("client is in an invalid world and must be disconnected"),
@ -1135,13 +1140,17 @@ impl<C: Config> Client<C> {
// Load new chunks within the view distance // Load new chunks within the view distance
{ {
let mut scratch = Vec::new();
let biome_registry_len = shared.biomes().len(); let biome_registry_len = shared.biomes().len();
for pos in chunks_in_view_distance(center, self.view_distance) { for pos in chunks_in_view_distance(center, self.view_distance) {
if let Some(chunk) = world.chunks.get(pos) { if let Some(chunk) = world.chunks.get(pos) {
if self.loaded_chunks.insert(pos) { if self.loaded_chunks.insert(pos) {
chunk.chunk_data_packet(send, &mut scratch, pos, biome_registry_len)?; chunk.send_chunk_data_packet(
send,
&mut self.scratch,
pos,
biome_registry_len,
)?;
self.scratch.clear();
} }
} }
} }
@ -1167,7 +1176,8 @@ impl<C: Config> Client<C> {
if self.world == entity.world() if self.world == entity.world()
&& self.position.distance(entity.position()) <= self.view_distance as f64 * 16.0 && self.position.distance(entity.position()) <= self.view_distance as f64 * 16.0
{ {
let _ = entity.send_updated_tracked_data(send, id); let _ = entity.send_updated_tracked_data(send, &mut self.scratch, id);
self.scratch.clear();
let position_delta = entity.position() - entity.old_position(); let position_delta = entity.position() - entity.old_position();
let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0; let needs_teleport = position_delta.map(f64::abs).reduce_partial_max() >= 8.0;
@ -1244,16 +1254,16 @@ impl<C: Config> Client<C> {
} }
// Update the client's own player metadata. // Update the client's own player metadata.
let mut data = Vec::new(); self.player_data.updated_tracked_data(&mut self.scratch);
self.player_data.updated_tracked_data(&mut data); if !self.scratch.is_empty() {
self.scratch.push(0xff);
if !data.is_empty() {
data.push(0xff);
send.append_packet(&SetEntityMetadata { send.append_packet(&SetEntityMetadata {
entity_id: VarInt(0), entity_id: VarInt(0),
metadata: RawBytes(&data), metadata: RawBytes(&self.scratch),
})?; })?;
self.scratch.clear();
} }
// Spawn new entities within the view distance. // Spawn new entities within the view distance.
@ -1276,9 +1286,10 @@ impl<C: Config> Client<C> {
return Some(e); return Some(e);
} }
if let Err(e) = entity.send_initial_tracked_data(send, id) { if let Err(e) = entity.send_initial_tracked_data(send, &mut self.scratch, id) {
return Some(e); return Some(e);
} }
self.scratch.clear();
if let Err(e) = send_entity_events(send, id.to_raw(), entity.events()) { if let Err(e) = send_entity_events(send, id.to_raw(), entity.events()) {
return Some(e); return Some(e);

View file

@ -737,13 +737,14 @@ impl<C: Config> Entity<C> {
pub(crate) fn send_initial_tracked_data( pub(crate) fn send_initial_tracked_data(
&self, &self,
send: &mut PlayPacketSender, send: &mut PlayPacketSender,
scratch: &mut Vec<u8>,
this_id: EntityId, this_id: EntityId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
// TODO: cache metadata buffer? self.variants.write_initial_tracked_data(scratch);
if let Some(metadata) = self.variants.initial_tracked_data() { if !scratch.is_empty() {
send.append_packet(&SetEntityMetadata { send.append_packet(&SetEntityMetadata {
entity_id: VarInt(this_id.to_raw()), entity_id: VarInt(this_id.to_raw()),
metadata: RawBytes(&metadata), metadata: RawBytes(scratch),
})?; })?;
} }
@ -755,13 +756,14 @@ impl<C: Config> Entity<C> {
pub(crate) fn send_updated_tracked_data( pub(crate) fn send_updated_tracked_data(
&self, &self,
send: &mut PlayPacketSender, send: &mut PlayPacketSender,
scratch: &mut Vec<u8>,
this_id: EntityId, this_id: EntityId,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
// TODO: cache metadata buffer? self.variants.write_updated_tracked_data(scratch);
if let Some(metadata) = self.variants.updated_tracked_data() { if !scratch.is_empty() {
send.append_packet(&SetEntityMetadata { send.append_packet(&SetEntityMetadata {
entity_id: VarInt(this_id.to_raw()), entity_id: VarInt(this_id.to_raw()),
metadata: RawBytes(&metadata), metadata: RawBytes(scratch),
})?; })?;
} }