Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix world sending. No more delays and glitching #416

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
121 changes: 65 additions & 56 deletions src/MiNET/MiNET/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class Player : Entity, IMcpeMessageHandler

public DamageCalculator DamageCalculator { get; set; } = new DamageCalculator();

private Dictionary<Tuple<int, int>, double> _chunkOrderQueue = new Dictionary<Tuple<int, int>, double>();

public Player(MiNetServer server, IPEndPoint endPoint) : base(-1, null)
{
Expand Down Expand Up @@ -998,7 +999,7 @@ public virtual void HandleMcpeRespawn(McpeRespawn message)

SendSetTime();

MiNetServer.FastThreadPool.QueueUserWorkItem(() => ForcedSendChunks());
UpdateChunkOrderQueue(new ChunkCoordinates(KnownPosition));

//SendPlayerStatus(3);

Expand Down Expand Up @@ -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);
});
}

Expand Down Expand Up @@ -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);
});
};

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -2685,6 +2682,7 @@ public void ForcedSendChunks(Action postAction = null)

packetCount++;
}
_chunkOrderQueue.Clear();
}
finally
{
Expand All @@ -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();
Expand Down Expand Up @@ -2978,7 +2986,7 @@ public override void OnTick(Entity[] entities)
HungerManager.OnTick();

base.OnTick(entities);

CheckChunkOrderQueue();
if (LastAttackTarget != null && LastAttackTarget.HealthManager.IsDead)
{
LastAttackTarget = null;
Expand Down Expand Up @@ -3313,6 +3321,7 @@ public void CleanCache()
lock (_sendChunkSync)
{
_chunksUsed.Clear();
_chunkOrderQueue.Clear();
}
}

Expand Down
8 changes: 4 additions & 4 deletions src/MiNET/MiNET/PlayerNetworkSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
Expand Down
65 changes: 31 additions & 34 deletions src/MiNET/MiNET/Worlds/Level.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -946,49 +946,46 @@ protected virtual void BroadCastMovement(Player[] players, Entity[] entities)
}
}

public IEnumerable<McpeWrapper> GenerateChunks(ChunkCoordinates chunkPosition, Dictionary<Tuple<int, int>, McpeWrapper> chunksUsed, double radius)
public Dictionary<Tuple<int, int>, double> GetNeededChunks(ChunkCoordinates chunkPosition, Dictionary<Tuple<int, int>, McpeWrapper> chunksUsed, double radius)
{
lock (chunksUsed)
{
Dictionary<Tuple<int, int>, double> newOrders = new Dictionary<Tuple<int, int>, double>();
Dictionary<Tuple<int, int>, double> newOrders = new Dictionary<Tuple<int, int>, 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<int, int> index = new Tuple<int, int>(chunkX, chunkZ);
newOrders[index] = distance;
continue;
}
int chunkX = (int) (x + centerX);
int chunkZ = (int) (z + centerZ);
Tuple<int, int> index = new Tuple<int, int>(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<McpeWrapper> GenerateChunks(ChunkCoordinates chunkPosition, Dictionary<Tuple<int, int>, McpeWrapper> chunksUsed, double radius)
{
lock (chunksUsed)
{
Dictionary<Tuple<int, int>, double> newOrders = GetNeededChunks(chunkPosition, chunksUsed, radius);

foreach (var pair in newOrders.OrderBy(pair => pair.Value))
{
Expand Down