2017-05-08 23:14:30 +02:00
|
|
|
|
using Cyber.Console;
|
2017-05-09 05:13:30 +02:00
|
|
|
|
using Cyber.Entities;
|
2017-05-10 15:05:02 +02:00
|
|
|
|
using Cyber.Entities.SyncBases;
|
2017-05-14 23:35:44 +02:00
|
|
|
|
using Cyber.Items;
|
2017-05-08 23:14:30 +02:00
|
|
|
|
using Cyber.Networking.Messages;
|
2017-05-09 03:25:04 +02:00
|
|
|
|
using System.Collections.Generic;
|
2017-05-08 23:06:18 +02:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
using UnityEngine.Networking;
|
|
|
|
|
|
|
|
|
|
namespace Cyber.Networking.Clientside {
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Client-class used to connecting to a server and communicating to it.
|
|
|
|
|
/// Also handles all events incoming from the server and forwards them where they should be handled.
|
|
|
|
|
/// </summary>
|
2017-05-09 03:25:04 +02:00
|
|
|
|
/// <remarks>
|
|
|
|
|
/// The pipeline of requests sent by the client:
|
|
|
|
|
/// 1. Player sends command to server (e.g. player presses 'w') and then processes it on the client.
|
|
|
|
|
/// 2. Server receives the command and determines weather it's possible
|
|
|
|
|
/// 3. Server then sends the results(along with a timestamp) to the clients
|
|
|
|
|
/// 4. Clients receive their results and if the action was possible, starts the action and catches up to the server with the timestamp
|
|
|
|
|
/// </remarks>
|
2017-05-08 23:06:18 +02:00
|
|
|
|
public class Client : MonoBehaviour {
|
|
|
|
|
|
|
|
|
|
private NetworkClient NetClient;
|
|
|
|
|
private bool Running = false;
|
|
|
|
|
|
|
|
|
|
private static Client Singleton;
|
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
private Spawner Spawner;
|
|
|
|
|
|
2017-05-09 03:25:04 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The player of this client
|
|
|
|
|
/// </summary>
|
|
|
|
|
private CConnectedPlayer Player;
|
2017-05-09 05:13:30 +02:00
|
|
|
|
private Dictionary<int, CConnectedPlayer> Players = new Dictionary<int, CConnectedPlayer>();
|
2017-05-09 03:25:04 +02:00
|
|
|
|
|
2017-05-16 23:25:29 +02:00
|
|
|
|
private SyncHandler SyncHandler;
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates the client and sets it as the signleton.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Client() {
|
|
|
|
|
Singleton = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Static interface for usage outside of Client
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Will launch the client and attempt to connect to the server.
|
|
|
|
|
/// Returns false if client is already running, otherwise true.
|
|
|
|
|
///
|
|
|
|
|
/// Generally instead of this you should use <see cref="NetworkEstablisher.StartClient(string, int)"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="ip"></param>
|
|
|
|
|
/// <param name="port"></param>
|
|
|
|
|
/// <returns>Weather the launch was successful or not.</returns>
|
|
|
|
|
public static bool Launch(string ip, int port) {
|
|
|
|
|
return Singleton.LaunchClient(ip, port);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Send messages to the server if the connection is active.
|
|
|
|
|
/// If client is not active, this will return false, otherwise true.
|
|
|
|
|
/// </summary>
|
2017-05-11 21:20:10 +02:00
|
|
|
|
/// <param name="msgType">The message type.</param>
|
|
|
|
|
/// <param name="message">The message contents.</param>
|
2017-05-08 23:06:18 +02:00
|
|
|
|
/// <returns>Weather the send was successful or not.</returns>
|
|
|
|
|
public static bool Send(short msgType, MessageBase message) {
|
|
|
|
|
if (Singleton.Running) {
|
|
|
|
|
Singleton.NetClient.Send(msgType, message);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-11 21:20:10 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends messages to the server by a specified channel.
|
|
|
|
|
/// If the client is not active, this will return false, otherwise true.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="msgType">The message type.</param>
|
|
|
|
|
/// <param name="message">The message contents.</param>
|
|
|
|
|
/// <param name="channelID">The ID of the channel to be used.</param>
|
|
|
|
|
/// <returns></returns>
|
2017-05-11 21:00:52 +02:00
|
|
|
|
public static bool SendByChannel(short msgType, MessageBase message, byte channelID) {
|
|
|
|
|
if (Singleton.Running) {
|
|
|
|
|
Singleton.NetClient.SendByChannel(msgType, message, channelID);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if the client is running or not.
|
|
|
|
|
/// This is independant weather the client is connected or not.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Weather the client is running or not</returns>
|
|
|
|
|
public static bool IsRunning() {
|
2017-05-12 03:59:58 +02:00
|
|
|
|
return !(Singleton == null || !Singleton.Running);
|
2017-05-08 23:06:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Returns if the client is connected or not.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Weather the client is connected or not.</returns>
|
|
|
|
|
public static bool IsConnected() {
|
|
|
|
|
return Singleton.NetClient.isConnected;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 18:42:28 +02:00
|
|
|
|
/// <summary>
|
2017-05-16 10:36:31 +02:00
|
|
|
|
/// Returns the connected player, or null if no client is active.
|
2017-05-09 18:42:28 +02:00
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>The connected player.</returns>
|
|
|
|
|
public static CConnectedPlayer GetConnectedPlayer() {
|
2017-05-16 10:36:31 +02:00
|
|
|
|
if (IsRunning()) {
|
|
|
|
|
return Singleton.Player;
|
|
|
|
|
}
|
|
|
|
|
return null;
|
2017-05-09 18:42:28 +02:00
|
|
|
|
}
|
2017-05-09 05:13:30 +02:00
|
|
|
|
|
2017-05-11 23:35:53 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Properly shut down the client.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>True if the client was active, false if not.</returns>
|
|
|
|
|
public static bool Shutdown() {
|
|
|
|
|
if (IsRunning()) {
|
|
|
|
|
Send(PktType.Disconnect, new DisconnectPkt());
|
|
|
|
|
Singleton.NetClient.Shutdown();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 23:25:29 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Get the Client <see cref="SyncHandler"/>, null if client is not active.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static SyncHandler GetSyncHandler() {
|
|
|
|
|
return Singleton.SyncHandler;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
private void Start() {
|
|
|
|
|
Spawner = GetComponent<Spawner>();
|
2017-05-09 16:15:21 +02:00
|
|
|
|
SyncHandler = new SyncHandler(Spawner.SyncDB);
|
2017-05-09 05:13:30 +02:00
|
|
|
|
}
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
private bool LaunchClient(string ip, int port) {
|
|
|
|
|
if (Running) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ConnectionConfig Config = new ConnectionConfig();
|
2017-05-09 16:15:21 +02:00
|
|
|
|
NetworkChannelID.ReliableSequenced = Config.AddChannel(QosType.ReliableSequenced);
|
|
|
|
|
NetworkChannelID.UnreliableSequenced = Config.AddChannel(QosType.UnreliableSequenced);
|
|
|
|
|
NetworkChannelID.Unreliable = Config.AddChannel(QosType.Unreliable);
|
2017-05-08 23:06:18 +02:00
|
|
|
|
NetworkServer.Configure(Config, 10);
|
|
|
|
|
|
|
|
|
|
NetClient = new NetworkClient();
|
|
|
|
|
NetClient.Configure(Config, 10);
|
|
|
|
|
|
|
|
|
|
Running = true;
|
|
|
|
|
|
|
|
|
|
NetClient.RegisterHandler(PktType.TextMessage, HandlePacket);
|
2017-05-09 03:25:04 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.Identity, HandlePacket);
|
|
|
|
|
NetClient.RegisterHandler(PktType.MassIdentity, HandlePacket);
|
2017-05-09 05:13:30 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.SpawnEntity, HandlePacket);
|
2017-05-09 06:15:34 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.MoveCreature, HandlePacket);
|
2017-05-11 21:00:52 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.Sync, HandlePacket);
|
|
|
|
|
NetClient.RegisterHandler(PktType.Interact, HandlePacket);
|
|
|
|
|
NetClient.RegisterHandler(PktType.StaticObjectIds, HandlePacket);
|
2017-05-11 23:35:53 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.Disconnect, HandlePacket);
|
2017-05-14 23:35:44 +02:00
|
|
|
|
NetClient.RegisterHandler(PktType.InventoryAction, HandlePacket);
|
2017-05-08 23:06:18 +02:00
|
|
|
|
|
|
|
|
|
NetClient.RegisterHandler(MsgType.Connect, OnConnected);
|
|
|
|
|
NetClient.RegisterHandler(MsgType.Disconnect, OnDisconnected);
|
|
|
|
|
NetClient.RegisterHandler(MsgType.Error, OnError);
|
|
|
|
|
|
|
|
|
|
NetClient.Connect(ip, port);
|
|
|
|
|
|
2017-05-10 16:34:16 +02:00
|
|
|
|
Debug.Log("Connecting..");
|
|
|
|
|
Term.Println("Connecting..");
|
2017-05-08 23:06:18 +02:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void HandlePacket(NetworkMessage msg) {
|
|
|
|
|
switch (msg.msgType) {
|
2017-05-09 16:15:21 +02:00
|
|
|
|
case (PktType.TextMessage):
|
|
|
|
|
TextMessagePkt TextMsg = new TextMessagePkt();
|
|
|
|
|
TextMsg.Deserialize(msg.reader);
|
|
|
|
|
Term.Println(TextMsg.Message);
|
|
|
|
|
break;
|
|
|
|
|
case (PktType.Identity):
|
|
|
|
|
IdentityPkt Identity = new IdentityPkt();
|
|
|
|
|
Identity.Deserialize(msg.reader);
|
|
|
|
|
var Conn = new CConnectedPlayer(Identity.ConnectionID);
|
|
|
|
|
if (Identity.Owned) {
|
|
|
|
|
Player = Conn;
|
|
|
|
|
} else {
|
|
|
|
|
Debug.Log(Conn.ConnectionID + " connected!");
|
|
|
|
|
Term.Println(Conn.ConnectionID + " connected!");
|
|
|
|
|
}
|
|
|
|
|
Players.Add(Conn.ConnectionID, Conn);
|
|
|
|
|
break;
|
|
|
|
|
case (PktType.MassIdentity):
|
2017-05-11 03:01:33 +02:00
|
|
|
|
IntListPkt Identities = new IntListPkt();
|
2017-05-09 16:15:21 +02:00
|
|
|
|
Identities.Deserialize(msg.reader);
|
2017-05-16 23:41:03 +02:00
|
|
|
|
foreach (int currId in Identities.IntList) {
|
2017-05-09 16:15:21 +02:00
|
|
|
|
Players.Add(currId, new CConnectedPlayer(currId));
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case (PktType.SpawnEntity):
|
|
|
|
|
SpawnEntityPkt SpawnPkt = new SpawnEntityPkt();
|
|
|
|
|
SpawnPkt.Deserialize(msg.reader);
|
|
|
|
|
|
|
|
|
|
EntityType EntityType = SpawnPkt.EntityType;
|
|
|
|
|
// Check if you are the owner and if they are spawning an NPC
|
|
|
|
|
if (SpawnPkt.OwnerID == Player.ConnectionID && EntityType == EntityType.NPC) {
|
|
|
|
|
// Change it into a PC instead.
|
|
|
|
|
EntityType = EntityType.PC;
|
|
|
|
|
}
|
2017-05-09 16:22:52 +02:00
|
|
|
|
GameObject Obj = Spawner.Spawn(EntityType, SpawnPkt.Position, SpawnPkt.SyncBaseIDList);
|
|
|
|
|
if (SpawnPkt.OwnerID > -1) {
|
|
|
|
|
Players[SpawnPkt.OwnerID].Character = Obj.GetComponent<Character>();
|
|
|
|
|
}
|
2017-05-11 21:00:52 +02:00
|
|
|
|
|
|
|
|
|
if (SpawnPkt.OwnerID == Player.ConnectionID) {
|
|
|
|
|
gameObject.AddComponent<ClientSyncer>();
|
|
|
|
|
}
|
2017-05-09 16:15:21 +02:00
|
|
|
|
break;
|
|
|
|
|
case (PktType.MoveCreature):
|
|
|
|
|
MoveCreaturePkt MoveCreature = new MoveCreaturePkt();
|
|
|
|
|
MoveCreature.Deserialize(msg.reader);
|
|
|
|
|
|
|
|
|
|
SyncBase SyncBase = Spawner.SyncDB.Get(MoveCreature.SyncBaseID);
|
|
|
|
|
if (SyncBase != null || SyncBase is Character ) {
|
|
|
|
|
Character Character = (Character) SyncBase;
|
|
|
|
|
Character.Move(MoveCreature.Direction);
|
|
|
|
|
} else {
|
|
|
|
|
Debug.LogError("SyncBase " + MoveCreature.SyncBaseID + " is not a Creature");
|
|
|
|
|
Term.Println("SyncBase " + MoveCreature.SyncBaseID + " is not a Creature");
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-11 00:52:05 +02:00
|
|
|
|
break;
|
2017-05-11 21:00:52 +02:00
|
|
|
|
case (PktType.Interact):
|
2017-05-11 00:52:05 +02:00
|
|
|
|
InteractionPkt Interaction = new InteractionPkt();
|
|
|
|
|
Interaction.Deserialize(msg.reader);
|
2017-05-11 04:03:07 +02:00
|
|
|
|
if (Interaction.OwnerSyncBaseID == Player.Character.ID) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2017-05-11 00:52:05 +02:00
|
|
|
|
|
2017-05-11 04:03:07 +02:00
|
|
|
|
SyncBase Target = Spawner.SyncDB.Get(Interaction.InteractSyncBaseID);
|
2017-05-11 00:52:05 +02:00
|
|
|
|
if (Target != null && Target is Interactable) {
|
2017-05-12 01:03:27 +02:00
|
|
|
|
((Interactable) Target).Interact(
|
|
|
|
|
Spawner.SyncDB.Get(Interaction.OwnerSyncBaseID),
|
|
|
|
|
Interaction.InteractionType);
|
2017-05-11 00:52:05 +02:00
|
|
|
|
} else {
|
|
|
|
|
Term.Println("Server has sent an erroneus SyncBase ID!");
|
|
|
|
|
}
|
2017-05-09 16:15:21 +02:00
|
|
|
|
break;
|
2017-05-11 21:00:52 +02:00
|
|
|
|
case (PktType.Sync):
|
2017-05-09 16:15:21 +02:00
|
|
|
|
SyncHandler.HandleSyncPkt(msg);
|
|
|
|
|
break;
|
2017-05-11 21:00:52 +02:00
|
|
|
|
case (PktType.StaticObjectIds):
|
2017-05-11 03:01:33 +02:00
|
|
|
|
IntListPkt StaticIds = new IntListPkt();
|
|
|
|
|
StaticIds.Deserialize(msg.reader);
|
2017-05-16 23:41:03 +02:00
|
|
|
|
Spawner.SyncDB.SetStaticObjectsIDs(StaticIds.IntList);
|
2017-05-11 03:01:33 +02:00
|
|
|
|
break;
|
2017-05-11 23:35:53 +02:00
|
|
|
|
case (PktType.Disconnect):
|
|
|
|
|
DisconnectPkt Disconnect = new DisconnectPkt();
|
|
|
|
|
Disconnect.Deserialize(msg.reader);
|
|
|
|
|
|
|
|
|
|
Term.Println(Disconnect.ConnectionID + " disconnected!");
|
|
|
|
|
Spawner.Remove(Players[Disconnect.ConnectionID].Character.gameObject);
|
|
|
|
|
Players.Remove(Disconnect.ConnectionID);
|
|
|
|
|
break;
|
2017-05-14 23:35:44 +02:00
|
|
|
|
case (PktType.InventoryAction):
|
|
|
|
|
InventoryActionPkt InventoryActionPkt = new InventoryActionPkt();
|
|
|
|
|
InventoryActionPkt.Deserialize(msg.reader);
|
|
|
|
|
|
|
|
|
|
SyncBase CurrSyncBase = Spawner.SyncDB.Get(InventoryActionPkt.SyncBaseID);
|
|
|
|
|
Inventory Inventory;
|
|
|
|
|
if (CurrSyncBase is Inventory) {
|
|
|
|
|
Inventory = (Inventory) CurrSyncBase;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-16 23:41:03 +02:00
|
|
|
|
Inventory.ActionHandler.HandleAction(InventoryActionPkt.Action, InventoryActionPkt.IntList);
|
2017-05-16 10:36:31 +02:00
|
|
|
|
|
2017-05-14 23:35:44 +02:00
|
|
|
|
break;
|
2017-05-09 16:15:21 +02:00
|
|
|
|
default:
|
|
|
|
|
Debug.LogError("Received an unknown packet, id: " + msg.msgType);
|
|
|
|
|
Term.Println("Received an unknown packet, id: " + msg.msgType);
|
|
|
|
|
break;
|
2017-05-08 23:06:18 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Built-in handles for some events
|
|
|
|
|
|
|
|
|
|
private void OnConnected(NetworkMessage msg) {
|
|
|
|
|
Debug.Log("Connected!");
|
|
|
|
|
Term.Println("Connected!");
|
|
|
|
|
|
|
|
|
|
Term.AddCommand("send (message)", "Send a message across the vastness of space and time!", (args) => {
|
|
|
|
|
Term.Println("You: " + args[0]);
|
2017-05-09 03:25:04 +02:00
|
|
|
|
NetClient.Send(PktType.TextMessage, new TextMessagePkt("A Client: " + args[0]));
|
2017-05-08 23:06:18 +02:00
|
|
|
|
});
|
2017-05-11 23:35:53 +02:00
|
|
|
|
|
|
|
|
|
Term.AddCommand("disconnect", "Disconnect from the server.", (args) => {
|
|
|
|
|
NetClient.Send(PktType.Disconnect, new DisconnectPkt());
|
|
|
|
|
});
|
2017-05-08 23:06:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnDisconnected(NetworkMessage msg) {
|
|
|
|
|
Debug.Log("Disconnected!");
|
|
|
|
|
Term.Println("Disconnected!");
|
2017-05-11 23:35:53 +02:00
|
|
|
|
|
|
|
|
|
foreach (var Entry in Players) {
|
|
|
|
|
Spawner.Remove(Entry.Value.Character.gameObject);
|
|
|
|
|
}
|
|
|
|
|
Players.Clear();
|
2017-05-08 23:06:18 +02:00
|
|
|
|
Running = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnError(NetworkMessage msg) {
|
|
|
|
|
Debug.LogError("Encountered a network error. Shutting down.");
|
|
|
|
|
Term.Println("Encountered a network error. Shutting down.");
|
|
|
|
|
NetClient.Disconnect();
|
|
|
|
|
Running = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|