From bcdfa76b4dbbcd1ddad6d3e4eda0b9128c30bdf4 Mon Sep 17 00:00:00 2001 From: Sam H Date: Sat, 15 Sep 2018 19:11:14 +0100 Subject: [PATCH] Fix world sending. No more delays and glitching --- src/MiNET/MiNET/Player.cs | 121 +++++++++++++----------- src/MiNET/MiNET/PlayerNetworkSession.cs | 8 +- src/MiNET/MiNET/Worlds/Level.cs | 65 ++++++------- 3 files changed, 100 insertions(+), 94 deletions(-) diff --git a/src/MiNET/MiNET/Player.cs b/src/MiNET/MiNET/Player.cs index 10b74a386..b29b1e7a6 100644 --- a/src/MiNET/MiNET/Player.cs +++ b/src/MiNET/MiNET/Player.cs @@ -101,6 +101,7 @@ public class Player : Entity, IMcpeMessageHandler public DamageCalculator DamageCalculator { get; set; } = new DamageCalculator(); + private Dictionary, double> _chunkOrderQueue = new Dictionary, double>(); public Player(MiNetServer server, IPEndPoint endPoint) : base(-1, null) { @@ -998,7 +999,7 @@ public virtual void HandleMcpeRespawn(McpeRespawn message) SendSetTime(); - MiNetServer.FastThreadPool.QueueUserWorkItem(() => ForcedSendChunks()); + UpdateChunkOrderQueue(new ChunkCoordinates(KnownPosition)); //SendPlayerStatus(3); @@ -1194,14 +1195,10 @@ public virtual void ChangeDimension(Level toLevel, PlayerLocation spawnPoint, Di MiNetServer.FastThreadPool.QueueUserWorkItem(() => { - Level.AddPlayer(this, true); - - ForcedSendChunks(() => - { - Log.WarnFormat("Respawn player {0} on level {1}", Username, Level.LevelId); - - SendSetTime(); - }); + Level.AddPlayer(this, true); + UpdateChunkOrderQueue(new ChunkCoordinates(KnownPosition)); + SendSetTime(); + Log.WarnFormat("Respawn player {0} on level {1}", Username, Level.LevelId); }); } @@ -1510,15 +1507,10 @@ public virtual void SpawnLevel(Level toLevel, PlayerLocation spawnPoint, bool us MiNetServer.FastThreadPool.QueueUserWorkItem(() => { Level.AddPlayer(this, true); - - ForcedSendChunks(() => - { - Log.InfoFormat("Respawn player {0} on level {1}", Username, Level.LevelId); - - SendSetTime(); - - postSpawnAction?.Invoke(); - }); + UpdateChunkOrderQueue(new ChunkCoordinates(KnownPosition)); + SendSetTime(); + postSpawnAction?.Invoke(); + Log.InfoFormat("Respawn player {0} on level {1}", Username, Level.LevelId); }); }; @@ -2629,6 +2621,11 @@ private void ForcedSendChunk(PlayerLocation position) _chunksUsed.Add(key, chunk); } + if (_chunkOrderQueue.ContainsKey(key)) + { + _chunkOrderQueue.Remove(key); + } + if (chunk != null) { SendPacket(chunk); @@ -2685,6 +2682,7 @@ public void ForcedSendChunks(Action postAction = null) packetCount++; } + _chunkOrderQueue.Clear(); } finally { @@ -2697,59 +2695,69 @@ public void ForcedSendChunks(Action postAction = null) } } - private void SendChunksForKnownPosition() + private void CheckChunkOrderQueue() { - if (!Monitor.TryEnter(_sendChunkSync)) return; + if (_chunkOrderQueue.Count <= 0 || Level == null || Level.WorldProvider == null) return; - Log.Debug($"Send chunks: {KnownPosition}"); - - try + lock (_chunksUsed) { - if (ChunkRadius <= 0) return; - - - var chunkPosition = new ChunkCoordinates(KnownPosition); - if (IsSpawned && _currentChunkPosition == chunkPosition) return; - - if (IsSpawned && _currentChunkPosition.DistanceTo(chunkPosition) < MoveRenderDistance) + int chunkCount = 0; + foreach (var pair in _chunkOrderQueue.OrderBy(pair => pair.Value)) { - return; - } - - _currentChunkPosition = chunkPosition; - - int packetCount = 0; - - if (Level == null) return; - - foreach (McpeWrapper chunk in Level.GenerateChunks(_currentChunkPosition, _chunksUsed, ChunkRadius)) - { - if (chunk != null) SendPacket(chunk); - Thread.Sleep(5); - - if (!IsSpawned) + if (IsSpawned && chunkCount > 5) { - if (packetCount++ == 56) - { - InitializePlayer(); - } + return; } - else + if (_chunksUsed.ContainsKey(pair.Key)) + { + _chunkOrderQueue.Remove(pair.Key); + continue; + } + + ChunkColumn chunkColumn = Level.GetChunk(new ChunkCoordinates(pair.Key.Item1, pair.Key.Item2)); + McpeWrapper chunk = null; + if (chunkColumn != null) + { + chunk = chunkColumn.GetBatch(); + } + chunkCount++; + if (chunk != null) SendPacket(chunk); + + if (!IsSpawned && chunkCount == 56) { - //if (packetCount++ > 56) Thread.Sleep(1); + InitializePlayer(); } + _chunksUsed.Add(pair.Key, chunk); + _chunkOrderQueue.Remove(pair.Key); + } } - catch (Exception e) + } + + private void SendChunksForKnownPosition() + { + Log.Debug($"Send chunks: {KnownPosition}"); + if (ChunkRadius <= 0 || Level == null) return; + var chunkPosition = new ChunkCoordinates(KnownPosition); + if (IsSpawned && _currentChunkPosition == chunkPosition) return; + if (IsSpawned && _currentChunkPosition.DistanceTo(chunkPosition) < MoveRenderDistance) { - Log.Error($"Failed sending chunks for {KnownPosition}", e); + return; } - finally + UpdateChunkOrderQueue(chunkPosition); + if (!IsSpawned) { - Monitor.Exit(_sendChunkSync); + CheckChunkOrderQueue(); } } + private void UpdateChunkOrderQueue(ChunkCoordinates chunkPosition) + { + if (ChunkRadius <= 0 || Level == null) return; + _currentChunkPosition = chunkPosition; + _chunkOrderQueue = Level.GetNeededChunks(_currentChunkPosition, _chunksUsed, ChunkRadius); + } + public virtual void SendUpdateAttributes() { var attributes = new PlayerAttributes(); @@ -2978,7 +2986,7 @@ public override void OnTick(Entity[] entities) HungerManager.OnTick(); base.OnTick(entities); - + CheckChunkOrderQueue(); if (LastAttackTarget != null && LastAttackTarget.HealthManager.IsDead) { LastAttackTarget = null; @@ -3313,6 +3321,7 @@ public void CleanCache() lock (_sendChunkSync) { _chunksUsed.Clear(); + _chunkOrderQueue.Clear(); } } diff --git a/src/MiNET/MiNET/PlayerNetworkSession.cs b/src/MiNET/MiNET/PlayerNetworkSession.cs index c9d42768e..d7100c3d2 100644 --- a/src/MiNET/MiNET/PlayerNetworkSession.cs +++ b/src/MiNET/MiNET/PlayerNetworkSession.cs @@ -928,7 +928,7 @@ private void SendAckQueue() Interlocked.Increment(ref Server.ServerInfo.NumberOfAckSent); acks.acks.Add(ack); } - + if (acks.acks.Count > 0) { byte[] data = acks.Encode(); @@ -984,14 +984,14 @@ public void SendQueue() Server.SendPacket(this, packet); } else if (packet is McpeWrapper) - { + { SendBuffered(messageCount, memStream); messageCount = 0; Server.SendPacket(this, packet); - Thread.Sleep(1); // Really important to slow down speed a bit + Thread.Sleep(0); // Really important to slow down speed a bit } else if (packet.NoBatch) - { + { SendBuffered(messageCount, memStream); messageCount = 0; Server.SendPacket(this, packet); diff --git a/src/MiNET/MiNET/Worlds/Level.cs b/src/MiNET/MiNET/Worlds/Level.cs index 907f57267..b70a12fdd 100644 --- a/src/MiNET/MiNET/Worlds/Level.cs +++ b/src/MiNET/MiNET/Worlds/Level.cs @@ -462,7 +462,7 @@ private void WorldTick(object sender) // return; //} - if (Log.IsDebugEnabled && _tickTimer.ElapsedMilliseconds >= 65) Log.Warn($"Time between world tick too long: {_tickTimer.ElapsedMilliseconds} ms. Last processing time={LastTickProcessingTime}, Avarage={AvarageTickProcessingTime}"); + if (Log.IsDebugEnabled && _tickTimer.ElapsedMilliseconds >= 65) Log.Warn($"Time between world tick too long: {_tickTimer.ElapsedMilliseconds} ms. Last processing time={LastTickProcessingTime}, Avarage={AvarageTickProcessingTime}"); Measurement worldTickMeasurement = _profiler.Begin("World tick"); @@ -946,49 +946,46 @@ protected virtual void BroadCastMovement(Player[] players, Entity[] entities) } } - public IEnumerable GenerateChunks(ChunkCoordinates chunkPosition, Dictionary, McpeWrapper> chunksUsed, double radius) + public Dictionary, double> GetNeededChunks(ChunkCoordinates chunkPosition, Dictionary, McpeWrapper> chunksUsed, double radius) { - lock (chunksUsed) - { - Dictionary, double> newOrders = new Dictionary, double>(); + Dictionary, double> newOrders = new Dictionary, double>(); - double radiusSquared = Math.Pow(radius, 2); + double radiusSquared = Math.Pow(radius, 2); - int centerX = chunkPosition.X; - int centerZ = chunkPosition.Z; + int centerX = chunkPosition.X; + int centerZ = chunkPosition.Z; - for (double x = -radius; x <= radius; ++x) + for (double x = -radius; x <= radius; ++x) + { + for (double z = -radius; z <= radius; ++z) { - for (double z = -radius; z <= radius; ++z) + var distance = (x*x) + (z*z); + if (distance > radiusSquared) { - var distance = (x*x) + (z*z); - if (distance > radiusSquared) - { - continue; - } - int chunkX = (int) (x + centerX); - int chunkZ = (int) (z + centerZ); - Tuple index = new Tuple(chunkX, chunkZ); - newOrders[index] = distance; + continue; } + int chunkX = (int) (x + centerX); + int chunkZ = (int) (z + centerZ); + Tuple index = new Tuple(chunkX, chunkZ); + newOrders[index] = distance; } - - //if (newOrders.Count > viewArea) - //{ - // foreach (var pair in newOrders.OrderByDescending(pair => pair.Value)) - // { - // if (newOrders.Count <= viewArea) break; - // newOrders.Remove(pair.Key); - // } - //} - - foreach (var chunkKey in chunksUsed.Keys.ToArray()) + } + foreach (var chunkKey in chunksUsed.Keys.ToArray()) + { + if (!newOrders.ContainsKey(chunkKey)) { - if (!newOrders.ContainsKey(chunkKey)) - { - chunksUsed.Remove(chunkKey); - } + chunksUsed.Remove(chunkKey); } + } + + return newOrders; + } + + public IEnumerable GenerateChunks(ChunkCoordinates chunkPosition, Dictionary, McpeWrapper> chunksUsed, double radius) + { + lock (chunksUsed) + { + Dictionary, double> newOrders = GetNeededChunks(chunkPosition, chunksUsed, radius); foreach (var pair in newOrders.OrderBy(pair => pair.Value)) {