2017-05-08 23:14:30 +02:00
|
|
|
|
using Cyber.Console;
|
2017-05-09 03:25:04 +02:00
|
|
|
|
using Cyber.Entities;
|
2017-05-10 15:05:02 +02:00
|
|
|
|
using Cyber.Entities.SyncBases;
|
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.Serverside {
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Server-class used to host a server and communicate to clients.
|
|
|
|
|
/// </summary>
|
2017-05-09 03:25:04 +02:00
|
|
|
|
/// \todo Change connection channels to Unreliable to optimize ping.
|
2017-05-09 05:13:30 +02:00
|
|
|
|
/// \todo Remove PC/NPC and make a Human-type entity instead.
|
2017-05-08 23:06:18 +02:00
|
|
|
|
public class Server : MonoBehaviour {
|
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
private Dictionary<int, SConnectedPlayer> Players = new Dictionary<int, SConnectedPlayer>();
|
2017-05-08 23:06:18 +02:00
|
|
|
|
private static Server Singleton;
|
|
|
|
|
|
2017-05-09 03:25:04 +02:00
|
|
|
|
private Spawner Spawner;
|
|
|
|
|
|
2017-05-11 00:52:05 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// The Syncer which syncs. <see cref="Syncer"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Syncer Syncer;
|
|
|
|
|
|
2017-05-11 21:00:52 +02:00
|
|
|
|
private ServerSyncHandler ServerSyncHandler;
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Creates the server-component, and sets the singleton as itself.
|
|
|
|
|
/// </summary>
|
|
|
|
|
public Server() {
|
|
|
|
|
Singleton = this;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Static methods for public usage
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Launches the server if not already launched.
|
|
|
|
|
/// Returns false if the server was already launched, true otherwise.
|
|
|
|
|
///
|
|
|
|
|
/// Generally instead of this you should use <see cref="NetworkEstablisher.StartServer(int)"/>
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="port">Port used to host the server.</param>
|
|
|
|
|
/// <returns>Weather the launch was successful.</returns>
|
|
|
|
|
public static bool Launch(int port) {
|
|
|
|
|
return Singleton.LaunchServer(port);
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 16:15:21 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Sends Message to all clients using specified channel.
|
|
|
|
|
/// <see cref="SendToAll(short, MessageBase)"/> defaults to <see cref="NetworkChannelID.ReliableSequenced"/>.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="msgType">Message type being sent.</param>
|
|
|
|
|
/// <param name="message">Message contents.</param>
|
|
|
|
|
/// <param name="channel">Channel being used.</param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static bool SendToAllByChannel(short msgType, MessageBase message, byte channel) {
|
|
|
|
|
if (NetworkServer.active) {
|
|
|
|
|
NetworkServer.SendByChannelToAll(msgType, message, channel);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to send a message to all clients who are listening.
|
|
|
|
|
/// Returns false if server wasn't active, true otherwise.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="msgType">Type of the message being sent.</param>
|
|
|
|
|
/// <param name="message">The message being sent.</param>
|
|
|
|
|
/// <returns>Weather sending was successful.</returns>
|
|
|
|
|
public static bool SendToAll(short msgType, MessageBase message) {
|
|
|
|
|
if (NetworkServer.active) {
|
|
|
|
|
NetworkServer.SendToAll(msgType, message);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Attempts to send a message to a specific client.
|
|
|
|
|
/// Returns false if server wasn't active, true otherwise.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="clientID">ID of the client which to send this message.</param>
|
|
|
|
|
/// <param name="msgType">Type of message being sent.</param>
|
|
|
|
|
/// <param name="message">The message being sent.</param>
|
|
|
|
|
/// <returns>Weather sending was successful.</returns>
|
|
|
|
|
public static bool Send(int clientID, short msgType, MessageBase message) {
|
|
|
|
|
if (NetworkServer.active) {
|
|
|
|
|
NetworkServer.SendToClient(clientID, msgType, message);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Is the server currently active.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>Weather the server is running or not</returns>
|
|
|
|
|
public static bool IsRunning() {
|
|
|
|
|
return NetworkServer.active;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-11 23:35:53 +02:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Properly shuts the server down.
|
|
|
|
|
/// \todo Server should inform all client that the server was shut down.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <returns>True if the server was running, false if not.</returns>
|
|
|
|
|
public static bool Shutdown() {
|
|
|
|
|
if (NetworkServer.active) {
|
|
|
|
|
foreach (var Entry in Singleton.Players) {
|
|
|
|
|
Singleton.Spawner.Remove(Entry.Value.Character.gameObject);
|
|
|
|
|
}
|
|
|
|
|
Singleton.Players.Clear();
|
|
|
|
|
NetworkServer.Shutdown();
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
private bool LaunchServer(int port) {
|
|
|
|
|
if (NetworkServer.active) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-09 03:25:04 +02:00
|
|
|
|
Spawner = GetComponent<Spawner>();
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
NetworkServer.Listen(port);
|
|
|
|
|
|
2017-05-12 00:24:34 +02:00
|
|
|
|
Spawner.SyncDB.SetStaticObjectsIDs();
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
NetworkServer.RegisterHandler(PktType.TextMessage, HandlePacket);
|
2017-05-09 06:15:34 +02:00
|
|
|
|
NetworkServer.RegisterHandler(PktType.MoveCreature, HandlePacket);
|
2017-05-11 21:00:52 +02:00
|
|
|
|
NetworkServer.RegisterHandler(PktType.Interact, HandlePacket);
|
|
|
|
|
NetworkServer.RegisterHandler(PktType.ClientSync, HandlePacket);
|
2017-05-11 23:35:53 +02:00
|
|
|
|
NetworkServer.RegisterHandler(PktType.Disconnect, HandlePacket);
|
2017-05-08 23:06:18 +02:00
|
|
|
|
|
|
|
|
|
NetworkServer.RegisterHandler(MsgType.Connect, OnConnected);
|
|
|
|
|
NetworkServer.RegisterHandler(MsgType.Disconnect, OnDisconnected);
|
|
|
|
|
NetworkServer.RegisterHandler(MsgType.Error, OnError);
|
|
|
|
|
|
|
|
|
|
Debug.Log("Server started on port " + port);
|
|
|
|
|
Term.Println("Server started on port " + port);
|
|
|
|
|
|
|
|
|
|
Term.AddCommand("send (message)", "Howl at the darkness of space. Does it echo though?", (args) => {
|
|
|
|
|
Term.Println("You: " + args[0]);
|
2017-05-09 03:25:04 +02:00
|
|
|
|
SendToAll(PktType.TextMessage, new TextMessagePkt("Server: " + args[0]));
|
2017-05-08 23:06:18 +02:00
|
|
|
|
});
|
|
|
|
|
|
2017-05-11 23:35:53 +02:00
|
|
|
|
Term.AddCommand("unhost", "Shuts down the server, shut it all down!", (args) => {
|
|
|
|
|
Term.Println("Unhosting the server.");
|
|
|
|
|
Shutdown();
|
|
|
|
|
});
|
|
|
|
|
|
2017-05-11 19:30:15 +02:00
|
|
|
|
Syncer = gameObject.AddComponent<Syncer>();
|
2017-05-09 16:15:21 +02:00
|
|
|
|
|
2017-05-11 21:00:52 +02:00
|
|
|
|
ServerSyncHandler = new ServerSyncHandler(Players);
|
|
|
|
|
|
2017-05-08 23:06:18 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void HandlePacket(NetworkMessage msg) {
|
|
|
|
|
|
|
|
|
|
switch (msg.msgType) {
|
2017-05-11 00:52:05 +02:00
|
|
|
|
case PktType.TextMessage:
|
|
|
|
|
TextMessagePkt TextMsg = new TextMessagePkt();
|
|
|
|
|
TextMsg.Deserialize(msg.reader);
|
|
|
|
|
Term.Println(TextMsg.Message);
|
|
|
|
|
break;
|
|
|
|
|
case PktType.MoveCreature:
|
|
|
|
|
MoveCreaturePkt MoveCreature = new MoveCreaturePkt();
|
|
|
|
|
MoveCreature.Deserialize(msg.reader);
|
|
|
|
|
|
|
|
|
|
// Check if the player is allowed to move this character
|
|
|
|
|
Character Controlled = Players[msg.conn.connectionId].Character.GetComponent<Character>();
|
|
|
|
|
if (Controlled.ID != MoveCreature.SyncBaseID) {
|
2017-05-08 23:06:18 +02:00
|
|
|
|
break;
|
2017-05-11 00:52:05 +02:00
|
|
|
|
}
|
2017-05-09 06:15:34 +02:00
|
|
|
|
|
2017-05-11 00:52:05 +02:00
|
|
|
|
Controlled.Move(MoveCreature.Direction);
|
2017-05-09 06:15:34 +02:00
|
|
|
|
|
2017-05-11 00:52:05 +02:00
|
|
|
|
foreach (var Player in Players) {
|
2017-05-12 00:50:11 +02:00
|
|
|
|
if (Player.Value.Connection.connectionId == msg.conn.connectionId) {
|
2017-05-11 00:52:05 +02:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
MoveCreature.Timestamp = NetworkHelper.GetCurrentSystemTime();
|
2017-05-12 00:50:11 +02:00
|
|
|
|
NetworkServer.SendToClient(Player.Value.Connection.connectionId, PktType.MoveCreature, MoveCreature);
|
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);
|
|
|
|
|
|
|
|
|
|
Character Sender = Players[msg.conn.connectionId].Character;
|
2017-05-11 04:03:07 +02:00
|
|
|
|
SyncBase Target = Spawner.SyncDB.Get(Interaction.InteractSyncBaseID);
|
|
|
|
|
|
|
|
|
|
Interaction.OwnerSyncBaseID = Sender.ID;
|
2017-05-11 00:52:05 +02:00
|
|
|
|
|
|
|
|
|
if (Target != null && Target is Interactable) {
|
|
|
|
|
Interactable Interacted = (Interactable) Target;
|
|
|
|
|
Vector3 Delta = Interacted.gameObject.transform.position - Sender.gameObject.transform.position;
|
|
|
|
|
float ServerInteractionDistance = Sender.InteractionDistance + Sender.MovementSpeed * 0.5f;
|
|
|
|
|
if (Delta.magnitude <= ServerInteractionDistance) {
|
2017-05-11 18:58:33 +02:00
|
|
|
|
Interacted.Interact(Sender);
|
2017-05-11 21:00:52 +02:00
|
|
|
|
NetworkServer.SendToAll(PktType.Interact, Interaction);
|
2017-05-11 00:52:05 +02:00
|
|
|
|
if (Interacted.GetInteractableSyncdata().RequiresSyncing) {
|
|
|
|
|
Syncer.DirtSyncBase(Interacted.ID);
|
2017-05-09 06:15:34 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-05-11 00:52:05 +02:00
|
|
|
|
} else {
|
|
|
|
|
Term.Println("Client has reported an erronous SyncBase ID!");
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-11 21:00:52 +02:00
|
|
|
|
break;
|
|
|
|
|
case PktType.ClientSync:
|
|
|
|
|
ServerSyncHandler.HandleSyncPkt(msg);
|
2017-05-11 00:52:05 +02:00
|
|
|
|
break;
|
2017-05-11 23:35:53 +02:00
|
|
|
|
case PktType.Disconnect:
|
|
|
|
|
msg.conn.Disconnect();
|
|
|
|
|
break;
|
2017-05-11 00:52:05 +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
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Internal built-in event handler
|
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
private void OnConnected(NetworkMessage msg) {
|
|
|
|
|
// Get client's ID
|
2017-05-09 03:25:04 +02:00
|
|
|
|
int Id = msg.conn.connectionId;
|
|
|
|
|
|
|
|
|
|
Term.Println(Id + " connected!");
|
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
// Send all other clients a notification and collect their id's
|
2017-05-09 03:25:04 +02:00
|
|
|
|
int[] IdList = new int[Players.Count];
|
2017-05-09 05:13:30 +02:00
|
|
|
|
int TempCounter = 0;
|
|
|
|
|
foreach (SConnectedPlayer P in Players.Values) {
|
2017-05-12 00:50:11 +02:00
|
|
|
|
IdList[TempCounter++] = P.Connection.connectionId;
|
|
|
|
|
NetworkServer.SendToClient(P.Connection.connectionId, PktType.Identity, new IdentityPkt(Id, false));
|
2017-05-09 03:25:04 +02:00
|
|
|
|
}
|
2017-05-09 05:13:30 +02:00
|
|
|
|
|
|
|
|
|
// Then send the client a list of all other clients
|
2017-05-11 03:01:33 +02:00
|
|
|
|
NetworkServer.SendToClient(Id, PktType.MassIdentity, new IntListPkt(IdList));
|
2017-05-09 03:25:04 +02:00
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
// Add the player to the list
|
2017-05-12 00:50:11 +02:00
|
|
|
|
SConnectedPlayer Player = new SConnectedPlayer(msg.conn);
|
2017-05-09 05:13:30 +02:00
|
|
|
|
Players.Add(Id, Player);
|
2017-05-09 03:25:04 +02:00
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
// Send the previously collected list to the player
|
2017-05-09 03:25:04 +02:00
|
|
|
|
NetworkServer.SendToClient(msg.conn.connectionId,
|
|
|
|
|
PktType.Identity, new IdentityPkt(msg.conn.connectionId, true));
|
2017-05-09 05:13:30 +02:00
|
|
|
|
|
2017-05-11 03:01:33 +02:00
|
|
|
|
// Spawn the player and collect it's IDs
|
2017-05-09 05:13:30 +02:00
|
|
|
|
Vector3 Position = new Vector3(0, 0, 0);
|
|
|
|
|
GameObject Obj = Spawner.Spawn(EntityType.NPC, Position);
|
|
|
|
|
int[] EntityIdList = Spawner.SyncDB.GetEntityIDs(Obj);
|
|
|
|
|
Player.Character = Obj.GetComponent<Character>();
|
|
|
|
|
|
|
|
|
|
NetworkServer.SendToAll(PktType.SpawnEntity, new SpawnEntityPkt(EntityType.NPC, Position, EntityIdList, Id));
|
|
|
|
|
|
2017-05-11 03:01:33 +02:00
|
|
|
|
// Send ID's of every existing static SyncBase object in the world.
|
2017-05-11 21:00:52 +02:00
|
|
|
|
NetworkServer.SendToClient(Id, PktType.StaticObjectIds, new IntListPkt(Spawner.SyncDB.GetStaticSyncBaseIDList()));
|
2017-05-11 03:01:33 +02:00
|
|
|
|
|
2017-05-09 05:13:30 +02:00
|
|
|
|
// Send every entity to the player who just connected.
|
|
|
|
|
foreach (var Entry in Players) {
|
|
|
|
|
if (Entry.Key == Id) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
Character Char = Players[Entry.Key].Character;
|
|
|
|
|
GameObject CurrObj = Char.gameObject;
|
2017-05-09 06:15:34 +02:00
|
|
|
|
int[] CurrEntityIdList = Spawner.SyncDB.GetEntityIDs(CurrObj);
|
2017-05-09 05:13:30 +02:00
|
|
|
|
NetworkServer.SendToClient(Id, PktType.SpawnEntity,
|
|
|
|
|
new SpawnEntityPkt(EntityType.NPC, CurrObj.transform.position, CurrEntityIdList, Entry.Key));
|
|
|
|
|
}
|
2017-05-08 23:06:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnDisconnected(NetworkMessage msg) {
|
2017-05-11 23:35:53 +02:00
|
|
|
|
// Get client's ID
|
|
|
|
|
int Id = msg.conn.connectionId;
|
|
|
|
|
|
|
|
|
|
Term.Println(Id + " disconnected.");
|
|
|
|
|
|
|
|
|
|
Spawner.Remove(Players[Id].Character.gameObject);
|
|
|
|
|
Players.Remove(Id);
|
|
|
|
|
ServerSyncHandler.ClearConnectionFromSyncDict(Id);
|
|
|
|
|
|
|
|
|
|
foreach (var Entry in Players) {
|
|
|
|
|
Send(Entry.Key, PktType.Disconnect, new DisconnectPkt(Id));
|
|
|
|
|
}
|
2017-05-08 23:06:18 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void OnError(NetworkMessage msg) {
|
|
|
|
|
Debug.LogError("Encountered a network error on server");
|
|
|
|
|
Term.Println("Encountered a network error on server");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|