/*
* MIT License
*
* Copyright (c) 2017 Michael VanOverbeek and ShiftOS devs
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ShiftOS.Objects;
using NetSockets;
using System.Windows.Forms;
using System.Threading;
using ShiftOS;
using static ShiftOS.Engine.SaveSystem;
using Newtonsoft.Json;
using System.Net.Sockets;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace ShiftOS.Engine
{
///
/// Digital Society connection management class.
///
public static class ServerManager
{
///
/// Print connection diagnostic information.
///
public static void PrintDiagnostics()
{
Console.WriteLine($@"{{CLIENT_DIAGNOSTICS}}
{{GUID}}: {thisGuid}
Ping: {ServerManager.DigitalSocietyPing} ms
{{CLIENT_DATA}}:
{JsonConvert.SerializeObject(client, Formatting.Indented)}");
}
///
/// Gets the unique identifier for this Digital Society connection. This can be used for peer-to-peer communication between two clients.
///
public static Guid thisGuid { get; private set; }
///
/// Gets the underlying NetSockets client for this connection.
///
public static NetObjectClient client { get; private set; }
private static bool UserDisconnect = false;
///
/// Gets or sets the server response time for the last request made by this client.
///
public static long DigitalSocietyPing
{
get;
private set;
}
///
/// Disconnect from the digital society intentionally.
///
public static void Disconnect()
{
UserDisconnect = true;
if (client != null)
{
client.Disconnect();
}
Disconnected?.Invoke();
}
///
/// Occurs when you are disconnected from the Digital Society.
///
public static event EmptyEventHandler Disconnected;
///
/// Occurs when the unique ID for this client is sent by the server.
///
public static event Action GUIDReceived;
private static void delegateToServer(ServerMessage msg)
{
string[] split = msg.GUID.Split('|');
bool finished = false;
foreach (var exec in Directory.GetFiles(Environment.CurrentDirectory))
{
if(exec.ToLower().EndsWith(".exe") || exec.ToLower().EndsWith(".dll"))
{
try
{
var asm = Assembly.LoadFile(exec);
foreach(var type in asm.GetTypes().Where(x => x.GetInterfaces().Contains(typeof(Server))))
{
var attrib = type.GetCustomAttributes().FirstOrDefault(x => x is ServerAttribute) as ServerAttribute;
if(attrib != null)
{
if(split[0] == SaveSystem.CurrentSave.SystemName && split[1] == attrib.Port.ToString())
{
if (Shiftorium.UpgradeAttributesUnlocked(type))
{
type.GetMethods(BindingFlags.Public | BindingFlags.Instance).FirstOrDefault(x => x.Name == "MessageReceived")?.Invoke(Activator.CreateInstance(type), null);
finished = true;
}
}
}
}
}
catch { }
}
}
if (finished == false)
{
Forward(split[2], "Error", $"{split[0]}:{split[1]}: connection refused");
}
}
private static void ServerManager_MessageReceived(ServerMessage msg)
{
switch(msg.Name)
{
case "getguid_fromserver":
if(SaveSystem.CurrentUser.Username == msg.Contents)
{
client.Send(new NetObject("yes_i_am", new ServerMessage
{
Name = "getguid_reply",
GUID = msg.GUID,
Contents = thisGuid.ToString(),
}));
}
break;
case "msgtosys":
try
{
var m = JsonConvert.DeserializeObject(msg.Contents);
if(m.GUID.Split('|')[2] != thisGuid.ToString())
{
delegateToServer(m);
}
}
catch { }
break;
case "getguid_reply":
GUIDReceived?.Invoke(msg.Contents);
break;
}
}
public static void SendMessageToIngameServer(string sysname, int port, string title, string contents)
{
var smsg = new ServerMessage
{
Name = title,
GUID = $"{sysname}|{port}|{thisGuid.ToString()}",
Contents = contents
};
Forward("all", "msgtosys", JsonConvert.SerializeObject(smsg));
}
public static void Detach_ServerManager_MessageReceived()
{
MessageReceived -= new ServerMessageReceived(ServerManager_MessageReceived);
}
///
/// Initiate a new Digital Society connection.
///
/// The IP address or hostname of the target server
/// The target port.
public static void Initiate(string mud_address, int port)
{
client = new NetObjectClient();
client.OnDisconnected += (o, a) =>
{
if (!UserDisconnect)
{
Desktop.PushNotification("digital_society_connection", "Disconnected from Digital Society.", "The ShiftOS kernel has been disconnected from the Digital Society. We are attempting to re-connect you.");
TerminalBackend.PrefixEnabled = true;
ConsoleEx.ForegroundColor = ConsoleColor.Red;
ConsoleEx.Bold = true;
Console.Write($@"Disconnected from MUD: ");
ConsoleEx.Bold = false;
ConsoleEx.Italic = true;
ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("You have been disconnected from the multi-user domain for an unknown reason. Your save data is preserved within the kernel and you will be reconnected shortly.");
TerminalBackend.PrefixEnabled = true;
TerminalBackend.PrintPrompt();
Initiate(mud_address, port);
}
};
client.OnReceived += (o, a) =>
{
if (PingTimer.IsRunning)
{
DigitalSocietyPing = PingTimer.ElapsedMilliseconds;
PingTimer.Reset();
}
var msg = a.Data.Object as ServerMessage;
if (msg.Name == "Welcome")
{
thisGuid = new Guid(msg.Contents);
GUIDReceived?.Invoke(msg.Contents);
TerminalBackend.PrefixEnabled = true;
TerminalBackend.PrintPrompt();
}
else if(msg.Name == "allusers")
{
foreach(var acc in JsonConvert.DeserializeObject(msg.Contents))
{
Console.WriteLine(acc);
}
TerminalBackend.PrintPrompt();
}
else if(msg.Name == "update_your_cp")
{
var args = JsonConvert.DeserializeObject>(msg.Contents);
if(args["username"] as string == SaveSystem.CurrentUser.Username)
{
SaveSystem.CurrentSave.Codepoints += (ulong)args["amount"];
Desktop.InvokeOnWorkerThread(new Action(() =>
{
Infobox.Show($"MUD Control Centre", $"Someone bought an item in your shop, and they have paid {args["amount"]}, and as such, you have been granted these Codepoints.");
}));
SaveSystem.SaveGame();
}
}
else if(msg.Name =="broadcast")
{
Console.WriteLine(msg.Contents);
}
else if(msg.Name == "forward")
{
MessageReceived?.Invoke(JsonConvert.DeserializeObject(msg.Contents));
}
else if (msg.Name == "Error")
{
var ex = JsonConvert.DeserializeObject(msg.Contents);
TerminalBackend.PrefixEnabled = true;
ConsoleEx.ForegroundColor = ConsoleColor.Red;
ConsoleEx.Bold = true;
Console.Write($@"{{MUD_ERROR}}: ");
ConsoleEx.Bold = false;
ConsoleEx.Italic = true;
ConsoleEx.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine(ex.Message);
TerminalBackend.PrefixEnabled = true;
TerminalBackend.PrintPrompt();
}
else
{
MessageReceived?.Invoke(msg);
}
};
try
{
client.Connect(mud_address, port);
}
catch(SocketException ex)
{
System.Diagnostics.Debug.Print(ex.ToString());
Initiate(mud_address, port);
}
}
private static Stopwatch PingTimer = new Stopwatch();
///
/// Send a message to the server.
///
/// The message name
/// The message body
public static void SendMessage(string name, string contents)
{
var sMsg = new ServerMessage
{
Name = name,
Contents = contents,
GUID = thisGuid.ToString(),
};
PingTimer.Start();
client.Send(new NetObject("msg", sMsg));
}
private static bool singleplayer = false;
public static bool IsSingleplayer { get { return singleplayer; } }
///
/// Occurs when the server sends a message to this client.
///
public static event ServerMessageReceived MessageReceived;
///
/// Send a message to another client.
///
/// The target client GUID.
/// The message title
/// The message contents
public static void Forward(string targetGUID, string title, string message)
{
var smsg = new ServerMessage
{
GUID = targetGUID,
Name = title,
Contents = message
};
ServerManager.SendMessage("mud_forward", JsonConvert.SerializeObject(smsg));
}
}
///
/// Delegate for handling server messages
///
/// A server message containing the protocol message name, GUID of the sender, and the contents of the message.
public delegate void ServerMessageReceived(ServerMessage msg);
public class MultiplayerOnlyAttribute : Attribute
{
///
/// Marks this application as a multiplayer-only application.
///
public MultiplayerOnlyAttribute()
{
}
}
}