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; } short SelectedSlot { get; }
ItemStack SelectedItem { get; } ItemStack SelectedItem { get; }
IMultiplayerServer Server { get; } IMultiplayerServer Server { get; }
bool EnableLogging { get; set; }
void QueuePacket(IPacket packet); void QueuePacket(IPacket packet);
void SendMessage(string message); void SendMessage(string message);
void Log(string message, params object[] parameters);
} }
} }

View File

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

View File

@ -49,6 +49,33 @@ namespace TrueCraft.Core.Entities
public bool IsCrouching { get; set; } public bool IsCrouching { get; set; }
public double PositiveDeltaY { 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; protected short _SelectedSlot;
public short SelectedSlot public short SelectedSlot
{ {

View File

@ -54,7 +54,6 @@ namespace TrueCraft.Core.Logic
{ {
if (!IsSupported(descriptor, server, world)) if (!IsSupported(descriptor, server, world))
{ {
server.SendMessage(ChatColor.Red + "Removing block due to insufficient opaque support blocks");
GenerateDropEntity(descriptor, world, server); GenerateDropEntity(descriptor, world, server);
world.SetBlockID(descriptor.Coordinates, 0); 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) public override void BlockUpdate(BlockDescriptor descriptor, IMultiplayerServer server, IWorld world)
{ {
if (!ValidBedPosition(descriptor, server.BlockRepository, world)) if (!ValidBedPosition(descriptor, server.BlockRepository, world))
{
world.SetBlockID(descriptor.Coordinates, 0); world.SetBlockID(descriptor.Coordinates, 0);
server.SendMessage(ChatColor.Red + "Removing block due to insufficient neighboring bed blocks");
}
base.BlockUpdate(descriptor, server, world); base.BlockUpdate(descriptor, server, world);
} }
} }

View File

@ -25,6 +25,7 @@ namespace TrueCraft.Commands
Commands.Add(new ResendInvCommand()); Commands.Add(new ResendInvCommand());
Commands.Add(new PositionCommand()); Commands.Add(new PositionCommand());
Commands.Add(new TimeCommand()); Commands.Add(new TimeCommand());
Commands.Add(new LogCommand());
} }
/// <summary> /// <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 class TimeCommand : Command
{ {
public override string Name public override string Name

View File

@ -28,8 +28,6 @@ namespace TrueCraft
private object EntityLock = new object(); private object EntityLock = new object();
private ConcurrentBag<IEntity> PendingDespawns { get; set; } private ConcurrentBag<IEntity> PendingDespawns { get; set; }
private static readonly int MaxClientDistance = 4;
public EntityManager(IMultiplayerServer server, IWorld world) public EntityManager(IMultiplayerServer server, IWorld world)
{ {
Server = server; Server = server;
@ -67,12 +65,60 @@ namespace TrueCraft
switch (property) switch (property)
{ {
case "Position": case "Position":
// TODO: Only trigger this if the player crosses a chunk boundary if ((int)(entity.Position.X) >> 4 != (int)(entity.OldPosition.X) >> 4 ||
Task.Factory.StartNew(client.UpdateChunks); (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; 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) private void PropegateEntityPositionUpdates(IEntity entity)
{ {
for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++) for (int i = 0, ServerClientsCount = Server.Clients.Count; i < ServerClientsCount; i++)
@ -107,6 +153,7 @@ namespace TrueCraft
private void SendEntityToClient(RemoteClient client, IEntity entity) 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; RemoteClient spawnedClient = null;
if (entity is PlayerEntity) if (entity is PlayerEntity)
spawnedClient = (RemoteClient)GetClientForEntity(entity as PlayerEntity); spawnedClient = (RemoteClient)GetClientForEntity(entity as PlayerEntity);
@ -153,7 +200,7 @@ namespace TrueCraft
{ {
Entities.Add(entity); 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) if (clientEntity != entity && clientEntity is PlayerEntity)
{ {
@ -204,6 +251,8 @@ namespace TrueCraft
if (client.KnownEntities.Contains(entity)) if (client.KnownEntities.Contains(entity))
{ {
client.QueuePacket(new DestroyEntityPacket(entity.EntityID)); client.QueuePacket(new DestroyEntityPacket(entity.EntityID));
client.KnownEntities.Remove(entity);
client.Log("Destroying entity {0} ({1})", entity.EntityID, entity.GetType().Name);
} }
} }
lock (EntityLock) lock (EntityLock)
@ -219,7 +268,7 @@ namespace TrueCraft
public void SendEntitiesToClient(IRemoteClient _client) public void SendEntitiesToClient(IRemoteClient _client)
{ {
var client = _client as RemoteClient; 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) if (entity != client.Entity)
SendEntityToClient(client, entity); SendEntityToClient(client, entity);

View File

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

View File

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

View File

@ -36,6 +36,7 @@ namespace TrueCraft
ItemStaging = ItemStack.EmptyStack; ItemStaging = ItemStack.EmptyStack;
KnownEntities = new List<IEntity>(); KnownEntities = new List<IEntity>();
Disconnected = false; Disconnected = false;
EnableLogging = server.EnableClientLogging;
} }
/// <summary> /// <summary>
@ -55,6 +56,7 @@ namespace TrueCraft
public short SelectedSlot { get; internal set; } public short SelectedSlot { get; internal set; }
public ItemStack ItemStaging { get; set; } public ItemStack ItemStaging { get; set; }
public IWindow CurrentWindow { get; set; } public IWindow CurrentWindow { get; set; }
public bool EnableLogging { get; set; }
private IEntity _Entity; private IEntity _Entity;
public 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) public void QueuePacket(IPacket packet)
{ {
PacketQueue.Enqueue(packet); PacketQueue.Enqueue(packet);
@ -166,6 +174,7 @@ namespace TrueCraft
LoadChunk(chunk); LoadChunk(chunk);
} }
} }
((EntityManager)Server.GetEntityManagerForWorld(World)).UpdateClientEntities(this);
} }
internal void UnloadAllChunks() internal void UnloadAllChunks()