Flesh out EntityManager to sync entities w/clients

This makes sure that you get new entities as you move to chunks that
have them, and that entities are despawned as you move further away from
them.
This commit is contained in:
Drew DeVault 2015-02-01 21:43:25 -07:00
parent eda5dd2f82
commit 079f8b9188
11 changed files with 131 additions and 10 deletions

View File

@ -17,8 +17,10 @@ namespace TrueCraft.API.Networking
short SelectedSlot { get; }
ItemStack SelectedItem { get; }
IMultiplayerServer Server { get; }
bool EnableLogging { get; set; }
void QueuePacket(IPacket packet);
void SendMessage(string message);
void Log(string message, params object[] parameters);
}
}

View File

@ -26,6 +26,7 @@ namespace TrueCraft.API.Server
IBlockRepository BlockRepository { get; }
IItemRepository ItemRepository { get; }
bool BlockUpdatesEnabled { get; set; }
bool EnableClientLogging { get; set; }
void Start(IPEndPoint endPoint);
void RegisterPacketHandler(byte packetId, PacketHandler handler);

View File

@ -49,6 +49,33 @@ namespace TrueCraft.Core.Entities
public bool IsCrouching { get; set; }
public double PositiveDeltaY { get; set; }
private Vector3 _OldPosition;
public Vector3 OldPosition
{
get
{
return _OldPosition;
}
private set
{
_OldPosition = value;
}
}
public override Vector3 Position
{
get
{
return _Position;
}
set
{
_OldPosition = _Position;
_Position = value;
OnPropertyChanged("Position");
}
}
protected short _SelectedSlot;
public short SelectedSlot
{

View File

@ -54,7 +54,6 @@ namespace TrueCraft.Core.Logic
{
if (!IsSupported(descriptor, server, world))
{
server.SendMessage(ChatColor.Red + "Removing block due to insufficient opaque support blocks");
GenerateDropEntity(descriptor, world, server);
world.SetBlockID(descriptor.Coordinates, 0);
}

View File

@ -86,10 +86,7 @@ namespace TrueCraft.Core.Logic.Blocks
public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
{
if (!ValidBedPosition(descriptor, server.BlockRepository, world))
{
world.SetBlockID(descriptor.Coordinates, 0);
server.SendMessage(ChatColor.Red + "Removing block due to insufficient neighboring bed blocks");
}
base.BlockUpdate(descriptor, server, world);
}
}

View File

@ -25,6 +25,7 @@ namespace TrueCraft.Commands
Commands.Add(new ResendInvCommand());
Commands.Add(new PositionCommand());
Commands.Add(new TimeCommand());
Commands.Add(new LogCommand());
}
/// <summary>

View File

@ -42,6 +42,39 @@ namespace TrueCraft.Commands
}
}
public class LogCommand : Command
{
public override string Name
{
get { return "log"; }
}
public override string Description
{
get { return "Toggles client logging."; }
}
public override string[] Aliases
{
get { return new string[0]; }
}
public override void Handle(IRemoteClient Client, string Alias, string[] Arguments)
{
if (Arguments.Length != 0)
{
Help(Client, Alias, Arguments);
return;
}
Client.EnableLogging = !Client.EnableLogging;
}
public override void Help(IRemoteClient Client, string Alias, string[] Arguments)
{
Client.SendMessage("/pos: Toggles client logging.");
}
}
public class TimeCommand : Command
{
public override string Name

View File

@ -28,8 +28,6 @@ namespace TrueCraft
private object EntityLock = new object();
private ConcurrentBag<IEntity> PendingDespawns { get; set; }
private static readonly int MaxClientDistance = 4;
public EntityManager(IMultiplayerServer server, IWorld world)
{
Server = server;
@ -67,12 +65,60 @@ namespace TrueCraft
switch (property)
{
case "Position":
// TODO: Only trigger this if the player crosses a chunk boundary
Task.Factory.StartNew(client.UpdateChunks);
if ((int)(entity.Position.X) >> 4 != (int)(entity.OldPosition.X) >> 4 ||
(int)(entity.Position.Z) >> 4 != (int)(entity.OldPosition.Z) >> 4)
{
client.Log("Passed chunk boundary at {0}, {1}", (int)(entity.Position.X) >> 4, (int)(entity.Position.Z) >> 4);
Task.Factory.StartNew(client.UpdateChunks);
UpdateClientEntities(client);
}
break;
}
}
internal void UpdateClientEntities(RemoteClient client)
{
var entity = client.Entity;
// Calculate entities you shouldn't know about anymore
for (int i = 0; i < client.KnownEntities.Count; i++)
{
var knownEntity = client.KnownEntities[i];
if (knownEntity.Position.DistanceTo(entity.Position) > client.ChunkRadius * Chunk.Depth)
{
client.QueuePacket(new DestroyEntityPacket(knownEntity.EntityID));
client.KnownEntities.Remove(knownEntity);
i--;
if (knownEntity is PlayerEntity)
{
var c = (RemoteClient)GetClientForEntity(knownEntity as PlayerEntity);
if (c.KnownEntities.Contains(entity))
{
c.KnownEntities.Remove(entity);
c.QueuePacket(new DestroyEntityPacket(entity.EntityID));
c.Log("Destroying entity {0} ({1})", knownEntity.EntityID, knownEntity.GetType().Name);
}
}
client.Log("Destroying entity {0} ({1})", knownEntity.EntityID, knownEntity.GetType().Name);
}
}
// Calculate entities you should now know about
var toSpawn = GetEntitiesInRange(entity, client.ChunkRadius);
foreach (var e in toSpawn)
{
if (e != entity && !client.KnownEntities.Contains(e))
{
SendEntityToClient(client, e);
// Make sure other players know about you since you've moved
if (e is PlayerEntity)
{
var c = (RemoteClient)GetClientForEntity(e as PlayerEntity);
if (!c.KnownEntities.Contains(entity))
SendEntityToClient(c, entity);
}
}
}
}
private void PropegateEntityPositionUpdates(IEntity entity)
{
for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++)
@ -107,6 +153,7 @@ namespace TrueCraft
private void SendEntityToClient(RemoteClient client, IEntity entity)
{
client.Log("Spawning entity {0} ({1}) at {2}", entity.EntityID, entity.GetType().Name, (Coordinates3D)entity.Position);
RemoteClient spawnedClient = null;
if (entity is PlayerEntity)
spawnedClient = (RemoteClient)GetClientForEntity(entity as PlayerEntity);
@ -153,7 +200,7 @@ namespace TrueCraft
{
Entities.Add(entity);
}
foreach (var clientEntity in GetEntitiesInRange(entity, MaxClientDistance))
foreach (var clientEntity in GetEntitiesInRange(entity, 8)) // Note: 8 is pretty arbitrary here
{
if (clientEntity != entity && clientEntity is PlayerEntity)
{
@ -204,6 +251,8 @@ namespace TrueCraft
if (client.KnownEntities.Contains(entity))
{
client.QueuePacket(new DestroyEntityPacket(entity.EntityID));
client.KnownEntities.Remove(entity);
client.Log("Destroying entity {0} ({1})", entity.EntityID, entity.GetType().Name);
}
}
lock (EntityLock)
@ -219,7 +268,7 @@ namespace TrueCraft
public void SendEntitiesToClient(IRemoteClient _client)
{
var client = _client as RemoteClient;
foreach (var entity in GetEntitiesInRange(client.Entity, MaxClientDistance))
foreach (var entity in GetEntitiesInRange(client.Entity, client.ChunkRadius))
{
if (entity != client.Entity)
SendEntityToClient(client, entity);

View File

@ -28,6 +28,7 @@ namespace TrueCraft
public IEventScheduler Scheduler { get; private set; }
public IBlockRepository BlockRepository { get; private set; }
public IItemRepository ItemRepository { get; private set; }
public bool EnableClientLogging { get; set; }
private bool _BlockUpdatesEnabled = true;
private struct BlockUpdate
@ -78,6 +79,7 @@ namespace TrueCraft
itemRepository.DiscoverItemProviders();
ItemRepository = itemRepository;
PendingBlockUpdates = new Queue<BlockUpdate>();
EnableClientLogging = false;
reader.RegisterCorePackets();
Handlers.PacketHandlers.RegisterHandlers(this);

View File

@ -24,6 +24,7 @@ namespace TrueCraft
server.AddLogProvider(new ConsoleLogProvider(LogCategory.Notice | LogCategory.Warning | LogCategory.Error | LogCategory.Debug));
#if DEBUG
server.AddLogProvider(new FileLogProvider(new StreamWriter("packets.log", false), LogCategory.Packets));
server.EnableClientLogging = true;
#endif
CommandManager = new CommandManager();
server.ChatMessageReceived += HandleChatMessageReceived;

View File

@ -36,6 +36,7 @@ namespace TrueCraft
ItemStaging = ItemStack.EmptyStack;
KnownEntities = new List<IEntity>();
Disconnected = false;
EnableLogging = server.EnableClientLogging;
}
/// <summary>
@ -55,6 +56,7 @@ namespace TrueCraft
public short SelectedSlot { get; internal set; }
public ItemStack ItemStaging { get; set; }
public IWindow CurrentWindow { get; set; }
public bool EnableLogging { get; set; }
private IEntity _Entity;
public IEntity Entity
@ -112,6 +114,12 @@ namespace TrueCraft
}
}
public void Log(string message, params object[] parameters)
{
if (EnableLogging)
SendMessage(ChatColor.Gray + string.Format("[" + DateTime.Now.ToShortTimeString() + "] " + message, parameters));
}
public void QueuePacket(IPacket packet)
{
PacketQueue.Enqueue(packet);
@ -166,6 +174,7 @@ namespace TrueCraft
LoadChunk(chunk);
}
}
((EntityManager)Server.GetEntityManagerForWorld(World)).UpdateClientEntities(this);
}
internal void UnloadAllChunks()