2015-05-12 17:30:56 -06:00
|
|
|
|
using System;
|
|
|
|
|
using System.Net;
|
|
|
|
|
using System.Net.Sockets;
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
using TrueCraft.API.Networking;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using TrueCraft.Core.Networking;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using TrueCraft.Core.Networking.Packets;
|
2015-05-16 21:50:10 -06:00
|
|
|
|
using TrueCraft.Client.Events;
|
2015-05-13 16:26:23 -06:00
|
|
|
|
using TrueCraft.Core.Logic;
|
|
|
|
|
using TrueCraft.API.Entities;
|
|
|
|
|
using TrueCraft.API;
|
2015-05-14 18:47:16 -06:00
|
|
|
|
using System.ComponentModel;
|
2015-05-13 11:30:29 -06:00
|
|
|
|
|
2015-05-16 21:50:10 -06:00
|
|
|
|
namespace TrueCraft.Client
|
2015-05-12 17:30:56 -06:00
|
|
|
|
{
|
|
|
|
|
public delegate void PacketHandler(IPacket packet, MultiplayerClient client);
|
|
|
|
|
|
2015-05-14 18:47:16 -06:00
|
|
|
|
public class MultiplayerClient : IAABBEntity, INotifyPropertyChanged // TODO: Make IMultiplayerClient and so on
|
2015-05-12 17:30:56 -06:00
|
|
|
|
{
|
2015-05-13 11:30:29 -06:00
|
|
|
|
public event EventHandler<ChatMessageEventArgs> ChatMessage;
|
2015-05-13 14:20:35 -06:00
|
|
|
|
public event EventHandler<ChunkEventArgs> ChunkLoaded;
|
|
|
|
|
public event EventHandler<ChunkEventArgs> ChunkUnloaded;
|
2015-05-14 18:47:16 -06:00
|
|
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
2015-05-13 14:20:35 -06:00
|
|
|
|
|
|
|
|
|
public ReadOnlyWorld World { get; private set; }
|
2015-05-13 16:26:23 -06:00
|
|
|
|
public PhysicsEngine Physics { get; set; }
|
2015-05-17 16:18:09 -06:00
|
|
|
|
public bool LoggedIn { get; internal set; }
|
2015-05-13 11:30:29 -06:00
|
|
|
|
|
2015-05-12 17:30:56 -06:00
|
|
|
|
private TcpClient Client { get; set; }
|
|
|
|
|
private IMinecraftStream Stream { get; set; }
|
|
|
|
|
private PacketReader PacketReader { get; set; }
|
2015-05-24 17:33:43 -07:00
|
|
|
|
private BlockingCollection<IPacket> PacketQueue { get; set; }
|
2015-05-12 17:30:56 -06:00
|
|
|
|
private Thread NetworkWorker { get; set; }
|
|
|
|
|
private readonly PacketHandler[] PacketHandlers;
|
|
|
|
|
|
|
|
|
|
public MultiplayerClient()
|
|
|
|
|
{
|
|
|
|
|
Client = new TcpClient();
|
2015-05-24 17:33:43 -07:00
|
|
|
|
PacketQueue = new BlockingCollection<IPacket>(new ConcurrentQueue<IPacket>());
|
2015-05-12 17:30:56 -06:00
|
|
|
|
PacketReader = new PacketReader();
|
|
|
|
|
PacketReader.RegisterCorePackets();
|
|
|
|
|
NetworkWorker = new Thread(new ThreadStart(DoNetwork));
|
|
|
|
|
PacketHandlers = new PacketHandler[0x100];
|
|
|
|
|
Handlers.PacketHandlers.RegisterHandlers(this);
|
2015-05-13 14:20:35 -06:00
|
|
|
|
World = new ReadOnlyWorld();
|
2015-05-13 16:26:23 -06:00
|
|
|
|
var repo = new BlockRepository();
|
|
|
|
|
repo.DiscoverBlockProviders();
|
|
|
|
|
World.World.BlockRepository = repo;
|
|
|
|
|
Physics = new PhysicsEngine(World, repo);
|
2015-05-12 17:30:56 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void RegisterPacketHandler(byte packetId, PacketHandler handler)
|
|
|
|
|
{
|
|
|
|
|
PacketHandlers[packetId] = handler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Connect(IPEndPoint endPoint)
|
|
|
|
|
{
|
|
|
|
|
Client.BeginConnect(endPoint.Address, endPoint.Port, ConnectionComplete, null);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 16:26:23 -06:00
|
|
|
|
public void Disconnect()
|
|
|
|
|
{
|
|
|
|
|
NetworkWorker.Abort();
|
|
|
|
|
new DisconnectPacket("Disconnecting").WritePacket(Stream);
|
|
|
|
|
Stream.BaseStream.Flush();
|
|
|
|
|
Client.Close();
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-12 17:30:56 -06:00
|
|
|
|
public void QueuePacket(IPacket packet)
|
|
|
|
|
{
|
2015-05-24 17:33:43 -07:00
|
|
|
|
PacketQueue.Add(packet);
|
2015-05-12 17:30:56 -06:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ConnectionComplete(IAsyncResult result)
|
|
|
|
|
{
|
|
|
|
|
Client.EndConnect(result);
|
|
|
|
|
Stream = new MinecraftStream(new BufferedStream(Client.GetStream()));
|
|
|
|
|
NetworkWorker.Start();
|
2015-05-13 16:26:23 -06:00
|
|
|
|
Physics.AddEntity(this);
|
2015-05-12 17:30:56 -06:00
|
|
|
|
QueuePacket(new HandshakePacket("TestUser")); // TODO: Get username from somewhere else
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void DoNetwork()
|
|
|
|
|
{
|
|
|
|
|
bool idle = true;
|
|
|
|
|
while (true)
|
|
|
|
|
{
|
|
|
|
|
IPacket packet;
|
2015-05-24 11:07:13 -06:00
|
|
|
|
DateTime limit = DateTime.Now.AddMilliseconds(500);
|
|
|
|
|
while (Client.Available != 0 && DateTime.Now < limit)
|
2015-05-12 17:30:56 -06:00
|
|
|
|
{
|
|
|
|
|
idle = false;
|
|
|
|
|
packet = PacketReader.ReadPacket(Stream, false);
|
|
|
|
|
if (PacketHandlers[packet.ID] != null)
|
|
|
|
|
PacketHandlers[packet.ID](packet, this);
|
|
|
|
|
}
|
2015-05-24 11:07:13 -06:00
|
|
|
|
limit = DateTime.Now.AddMilliseconds(500);
|
|
|
|
|
while (PacketQueue.Any() && DateTime.Now < limit)
|
2015-05-12 17:30:56 -06:00
|
|
|
|
{
|
|
|
|
|
idle = false;
|
2015-05-24 17:33:43 -07:00
|
|
|
|
|
|
|
|
|
if (PacketQueue.TryTake(out packet, 100))
|
|
|
|
|
{
|
|
|
|
|
PacketReader.WritePacket(Stream, packet);
|
|
|
|
|
Stream.BaseStream.Flush();
|
|
|
|
|
}
|
2015-05-12 17:30:56 -06:00
|
|
|
|
}
|
|
|
|
|
if (idle)
|
|
|
|
|
Thread.Sleep(100);
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-13 11:30:29 -06:00
|
|
|
|
|
|
|
|
|
protected internal void OnChatMessage(ChatMessageEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (ChatMessage != null) ChatMessage(this, e);
|
|
|
|
|
}
|
2015-05-13 14:20:35 -06:00
|
|
|
|
|
|
|
|
|
protected internal void OnChunkLoaded(ChunkEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (ChunkLoaded != null) ChunkLoaded(this, e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected internal void OnChunkUnloaded(ChunkEventArgs e)
|
|
|
|
|
{
|
|
|
|
|
if (ChunkUnloaded != null) ChunkUnloaded(this, e);
|
|
|
|
|
}
|
2015-05-13 16:26:23 -06:00
|
|
|
|
|
|
|
|
|
#region IAABBEntity implementation
|
|
|
|
|
|
|
|
|
|
public const double Width = 0.6;
|
|
|
|
|
public const double Height = 1.62;
|
|
|
|
|
public const double Depth = 0.6;
|
|
|
|
|
|
|
|
|
|
public void TerrainCollision(Vector3 collisionPoint, Vector3 collisionDirection)
|
|
|
|
|
{
|
|
|
|
|
// This space intentionally left blank
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public BoundingBox BoundingBox
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return new BoundingBox(Position, Position + Size);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Size Size
|
|
|
|
|
{
|
|
|
|
|
get { return new Size(Width, Height, Depth); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region IPhysicsEntity implementation
|
|
|
|
|
|
|
|
|
|
public bool BeginUpdate()
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void EndUpdate(Vector3 newPosition)
|
|
|
|
|
{
|
|
|
|
|
Position = newPosition;
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-13 23:09:49 -06:00
|
|
|
|
public float Yaw { get; set; }
|
|
|
|
|
public float Pitch { get; set; }
|
|
|
|
|
|
2015-05-13 16:26:23 -06:00
|
|
|
|
internal Vector3 _Position;
|
|
|
|
|
public Vector3 Position
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return _Position;
|
|
|
|
|
}
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
if (_Position != value)
|
2015-05-14 18:47:16 -06:00
|
|
|
|
{
|
2015-05-13 23:09:49 -06:00
|
|
|
|
QueuePacket(new PlayerPositionAndLookPacket(value.X, value.Y, value.Y + Height, value.Z, Yaw, Pitch, false));
|
2015-05-14 18:47:16 -06:00
|
|
|
|
if (PropertyChanged != null)
|
|
|
|
|
PropertyChanged(this, new PropertyChangedEventArgs("Position"));
|
|
|
|
|
}
|
2015-05-13 16:26:23 -06:00
|
|
|
|
_Position = value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Vector3 Velocity { get; set; }
|
|
|
|
|
|
|
|
|
|
public float AccelerationDueToGravity
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return 0.08f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float Drag
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return 0.02f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public float TerminalVelocity
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return 3.92f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endregion
|
2015-05-12 17:30:56 -06:00
|
|
|
|
}
|
|
|
|
|
}
|