You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

648 lines
23 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ProxyCore.Input;
using ProxyCore.Output;
using ProxyCore.Messages;
using ProxyCore.Scripting;
using System.Net;
using System.IO;
using System.Text.RegularExpressions;
namespace ProxyCore
{
public class World
{
public World()
{
Instance = this;
Log.Write("Loading plugins...");
PluginMgr.LoadAll();
TriggerHandler.RegisterTrigger("core.login", @"^\$gmcp\.char\.vitals\.hp ", _GMCPHP);
InputHandler.RegisterCommand("commands", "", _Commands, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8);
InputHandler.RegisterCommand("lastlines", @"^(\d+)$", _LineInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8);
InputHandler.RegisterCommand("plugins", @"^(\w+)(\s+full)?", _PluginInfo, CMDFlags.None, null, 0, ulong.MaxValue, "core", 6);
InputHandler.RegisterCommand("shutdown", "", _Shutdown, CMDFlags.None, null, 0, ulong.MaxValue, "core", 8);
}
private bool _Shutdown(InputData i)
{
_doShutdown = true;
return true;
}
private bool _Commands(InputData i)
{
SendMessage("@wCommands in @Wcore@w:", i.ClientMask);
int c = WriteCommands(InputHandler.Commands, "core", i.ClientMask);
SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found.");
foreach(KeyValuePair<string, Plugin> p in PluginMgr.Plugins)
{
SendMessage("", i.ClientMask);
SendMessage("@wCommands in @W" + p.Key + "@w:", i.ClientMask);
c = WriteCommands(InputHandler.Commands, p.Key, i.ClientMask);
SendMessage("@C" + c + " @wcommand" + (c == 1 ? "" : "s") + " found.");
}
SendMessage("", i.ClientMask);
SendMessage("@WThese are commands for the proxy. If you want to see MUD commands type command.", i.ClientMask);
return true;
}
private int WriteCommands(SortedDictionary<string, InputEntry> y, string plugin, uint[] clientMask)
{
if(y == null || y.Count == 0)
return 0;
int c = 0;
foreach(KeyValuePair<string, InputEntry> x in y)
{
if(x.Value.Plugin != plugin)
continue;
if((x.Value.Flags & (CMDFlags.Disabled | CMDFlags.Hidden)) != CMDFlags.None)
continue;
string cmd = "";
InputEntry p = x.Value.Parent;
while(p != null)
{
cmd = cmd.Insert(0, p.Command + " ");
p = p.Parent;
}
SendMessage("@Y" + cmd, clientMask);
c++;
c += WriteCommands(x.Value.Subcommands, plugin, clientMask);
}
return c;
}
/// <summary>
/// Game world instance.
/// </summary>
public static World Instance
{
get;
private set;
}
/// <summary>
/// Handle text line as if we received it from Aardwolf.
/// </summary>
/// <param name="Msg"></param>
public void _OnReceived(string Msg)
{
TriggerHandler.HandleText(Msg, this);
}
public void _OnConnected(bool connected)
{
isWorldReady = false;
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
if(connected)
x.Value.OnConnect();
else
x.Value.OnDisconnect();
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Key);
}
#endif
}
}
private bool _GMCPHP(TriggerData t)
{
if(!isWorldReady)
{
isWorldReady = true;
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
x.Value.OnLogin();
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Key);
}
#endif
}
CheckUpdatesNow = MSTime + 5000;
}
return false;
}
private long CheckUpdatesNow = 0;
private bool _LineInfo(InputData i)
{
int j = 10;
if(i.Arguments.Success)
{
if(!int.TryParse(i.Arguments.Groups[1].Value, out j))
j = 10;
}
if(j > 100)
j = 100;
else if(j < 1)
j = 1;
SendMessage("@wDisplaying last @Y" + j + " @wline" + (j != 1 ? "s" : "") + ":", i.ClientMask);
j = lastLine.Count - j;
if(j < 0)
j = 0;
for(; j < lastLine.Count; j++)
SendMessage(lastLine[j].Replace("@", "@@"), i.ClientMask);
if(j == 10)
SendMessage("@wType '@Wlastlines <nr>@w' to see <nr> amount of lines.", i.ClientMask);
return true;
}
internal List<string> lastLine = new List<string>();
private bool _PluginInfo(InputData i)
{
if(!i.Arguments.Success)
{
SendMessage("@wYou have the following plugins installed:", i.ClientMask);
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
SendMessage("@Y" + string.Format("{0,-20}", x.Value.Keyword.Trim()) + " @w- " + x.Value.Name + ", version " + x.Value.Version.ToString(), i.ClientMask);
if(PluginMgr.Plugins.Count == 0)
SendMessage("@RYou have no plugins installed.", i.ClientMask);
else
{
SendMessage("", i.ClientMask);
SendMessage("@C" + PluginMgr.Plugins.Count + " @wplugin" + (PluginMgr.Plugins.Count != 1 ? "s" : "") + " found.");
SendMessage("@wUse '@Wplugin <keyword>@w' for more information about a plugin.", i.ClientMask);
SendMessage("@wUse '@Wplugin <keyword> full@w' for all information about a plugin.", i.ClientMask);
//SendMessage("@wUse '@Wplugin <keyword> write@w' to write all information about plugin to a file.", i.ClientMask);
}
}
else
{
Plugin p = PluginMgr.Plugins.ContainsKey(i.Arguments.Groups[1].Value.ToLower().Trim()) ? PluginMgr.Plugins[i.Arguments.Groups[1].Value.ToLower().Trim()] : null;
if(p == null)
{
SendMessage("@wNo such plugin (@W" + i.Arguments.Groups[1].Value.ToLower().Trim() + "@w).", i.ClientMask);
SendMessage("@wType '@Wplugins@w' for a list of installed plugins.", i.ClientMask);
return true;
}
SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask);
SendMessage("@w|@R" + p.Name.Substring(0, p.Name.Length / 2).PadLeft(35, ' ') +
p.Name.Substring(p.Name.Length / 2).PadRight(35, ' ') + "@w|", i.ClientMask);
SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask);
SendMessage("@w| @WKeyword @w: @g" + string.Format("{0,-55}", p.Keyword) + "@w|", i.ClientMask);
SendMessage("@w| @WAuthor @w: @g" + string.Format("{0,-55}", !string.IsNullOrEmpty(p.Author) ? p.Author : "Unknown") + "@w|", i.ClientMask);
SendMessage("@w| @WVersion @w: @Y" + string.Format("{0,-55}", p.Version) + "@w|", i.ClientMask);
if(!string.IsNullOrEmpty(p.Website))
SendMessage("@w| @WWebsite @w: @Y" + string.Format("{0,-55}", p.Website) + "@w|", i.ClientMask);
SendMessage("@w| @WClass name @w: @g" + string.Format("{0,-55}", p.ClassName) + "@w|", i.ClientMask);
if(!string.IsNullOrEmpty(p.Description))
{
string[] desc = Utility.WrapColored(p.Description, 54, 0);
for(int j = 0; j < desc.Length; j++)
{
SendMessage(
j == 0
? ("@w| @WDescription @w: @C" + string.Format("{0,-55}", desc[j]) + "@w|")
: ("@w| : @C" + string.Format("{0,-55}", desc[j]) + "@w|"), i.ClientMask);
}
}
if(p.RequiredPlayerConfig.Count != 0)
{
for(int j = 0; j < p.RequiredPlayerConfig.Count; j++)
{
SendMessage(
j == 0
? ("@w| @WReq. config @w: " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) +
"@w|")
: ("@w| : " + string.Format("{0,-55}", p.RequiredPlayerConfig[j]) + "@w|"),
i.ClientMask);
}
}
SendMessage("@w+----------------------------------------------------------------------+", i.ClientMask);
if(i.Arguments.Groups[2].Length > 0 || i.Arguments.Groups[3].Length > 0)
{
int j = _PluginInfoWriteCommands(0, InputHandler.Commands, p.Keyword.ToLower().Trim(), i.ClientMask);
int k = 0;
foreach(KeyValuePair<string, TriggerEntry> x in TriggerHandler.TriggersName)
{
if(x.Value.Plugin != p.Keyword.ToLower().Trim())
continue;
if(k == 0)
{
SendMessage(
"@w| @WTriggers @w: \"" +
Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|",
i.ClientMask);
}
else
{
SendMessage(
"@w| : \"" +
Utility.FormatColoredString(x.Value.PatternStr.Replace("@", "@@") + "\"", -54) + "@w|",
i.ClientMask);
}
k++;
}
if(j != 0 || k != 0)
{
SendMessage("@w+----------------------------------------------------------------------+",
i.ClientMask);
}
}
}
return true;
}
private int _PluginInfoWriteCommands(int j, SortedDictionary<string, InputEntry> y, string plugin, uint[] clientMask)
{
foreach(KeyValuePair<string, InputEntry> x in y)
{
if(x.Value.Plugin != plugin)
continue;
string cmd = x.Key;
InputEntry parent = x.Value.Parent;
while(parent != null)
{
cmd = cmd.Insert(0, parent.Command + " ");
parent = parent.Parent;
}
if(j == 0)
SendMessage("@w| @WCommands @w: @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask);
else
SendMessage("@w| : @c" + string.Format("{0,-55}", cmd) + "@w|", clientMask);
j++;
if(x.Value.Subcommands != null)
j = _PluginInfoWriteCommands(j, x.Value.Subcommands, plugin, clientMask);
}
return j;
}
private bool isWorldReady = false;
/// <summary>
/// Handle GMCP data that we received from Aardwolf.
/// </summary>
/// <param name="Data">GMCP data received.</param>
public void _OnReceived(byte[] Data)
{
string Msg = Encoding.Default.GetString(Data);
string Module = Msg.Trim().ToLower();
if(Module.Contains(' '))
Module = Module.Substring(0, Module.IndexOf(' '));
Message m = new Message(true);
m.Clients = null;
m.Flags |= MessageFlags.GMCP;
m.Msg = Module;
m.MsgData = Data;
_SendMessage(m);
TriggerHandler.HandleGMCP(Msg);
}
/// <summary>
/// This is called from proxy when it shuts down. Do NOT call from a plugin.
/// </summary>
public void Shutdown()
{
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
x.Value.Shutdown();
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Key);
}
#endif
}
_doShutdown = true;
}
/// <summary>
/// Enter input as if a client entered it. Meaning we parse it. Consider using the Execute command instead.
/// </summary>
/// <param name="Msg">Input entered.</param>
/// <param name="ClientId">Which client is this from? Enter 0 to set not from a client.</param>
/// <param name="AuthLevel">Authlevel of client who entered command (1...64)</param>
public void _OnSent(string Msg, uint ClientId, int AuthLevel)
{
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
x.Value.OnEnteredCommandBefore(ref Msg, ClientId, AuthLevel);
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Key);
}
#endif
if(Msg == null)
return;
}
InputData i = InputHandler.HandleInput(Msg, Msg, ClientId, AuthLevel, null, this);
if(i != null)
{
#if !DEBUG
try
{
#endif
if(i.Function.Func(i))
return;
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, i.Function.Plugin);
}
#endif
Msg = i.Command;
}
if(Msg != null)
{
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
x.Value.OnEnteredCommandAfter(ref Msg, ClientId, AuthLevel);
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Value.Keyword);
}
#endif
if(Msg == null)
return;
}
_SendMessage(Msg, new uint[] {0}, ClientId != 0);
}
}
internal void _SendMessage(string Msg, uint[] Clients, bool Natural)
{
_SendMessage(Msg, Clients, Natural, MessageFlags.None);
}
internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags)
{
_SendMessage(Msg, Clients, Natural, Flags, ulong.MaxValue);
}
internal void _SendMessage(string Msg, uint[] Clients, bool Natural, MessageFlags Flags, ulong AuthMask)
{
Message m = new Message(Natural);
m.Clients = Clients;
m.Msg = Msg;
m.Flags = Flags;
m.AuthMask = AuthMask;
_SendMessage(m);
}
internal void _SendMessage(Message msg)
{
_MessageData.Add(msg);
}
/// <summary>
/// Send message to specified clients.
/// </summary>
/// <param name="Msg">Message to send.</param>
/// <param name="Clients">Clients to send it to. Enter null to send to all connected clients.
/// Enter 0 as client to send it as a command to Aardwolf (we don't parse it though, if you want
/// parsed input use the Execute command).</param>
public void SendMessage(string Msg, uint[] Clients)
{
_SendMessage(Msg, Clients, false);
}
/// <summary>
/// Send a message to all connected authorized clients.
/// </summary>
/// <param name="Msg">Message to send to all authorized clients.</param>
public void SendMessage(string Msg)
{
SendMessage(Msg, null);
}
/// <summary>
/// Send a message to all connected authorized clients.
/// </summary>
/// <param name="Msg">Message to send to all authorized clients.</param>
/// <param name="AuthMask">Authorization levels required to see this message. This is a mask.</param>
public void SendMessage(string Msg, ulong AuthMask)
{
_SendMessage(Msg, null, false, MessageFlags.None, AuthMask);
}
/// <summary>
/// Execute a command.
/// </summary>
/// <param name="Msg">Command to execute.</param>
/// <param name="allowParse">Allow parsing it for aliases and such, or send it directly to Aardwolf?</param>
public void Execute(string Msg, bool allowParse)
{
Execute(Msg, allowParse, 1);
}
/// <summary>
/// Execute a command.
/// </summary>
/// <param name="Msg">Command to execute.</param>
/// <param name="allowParse">Allow parsing it for aliases and such, or send it directly to Aardwolf?</param>
/// <param name="AuthLevel">Auth level that executes this command. (1...64)</param>
public void Execute(string Msg, bool allowParse, int AuthLevel)
{
if(Msg == null)
return;
if(allowParse)
_OnSent(Msg, uint.MaxValue, Math.Max(1, Math.Min(64, AuthLevel)));
else
_SendMessage(Msg, new uint[] { 0 }, false);
}
/// <summary>
/// Send raw bytes to MUD.
/// </summary>
/// <param name="Data">Bytes to send.</param>
public void Send(byte[] Data)
{
if(Data == null)
return;
Message m = new Message(false);
m.Clients = new uint[] { 0 };
m.MsgData = Data;
_SendMessage(m);
}
/// <summary>
/// Internal command for updating the world. DO NOT CALL FROM A PLUGIN!
/// </summary>
/// <param name="msTime">New mstime.</param>
public bool Update(long msTime)
{
MSTime = msTime;
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
#if !DEBUG
try
{
#endif
x.Value.Update(msTime);
#if !DEBUG
}
catch(Exception e)
{
Log.Crash(e, x.Key);
}
#endif
}
if(CheckUpdatesNow != 0 && msTime > CheckUpdatesNow)
{
CheckUpdatesNow = 0;
CheckUpdates(Config.GetInt32("Updates.Core", 1) != 0, Config.GetInt32("Updates.Plugins", 1) != 0, false);
}
return _doShutdown;
}
private bool _doShutdown = false;
/// <summary>
/// This is messages for networking to handle. Don't touch unless you know what you are doing.
/// </summary>
public readonly List<Message> _MessageData = new List<Message>(256);
internal CoreConfig Config = new CoreConfig();
/// <summary>
/// Version of Proxy.
/// </summary>
public const int Version = 7;
public const string CoreUrl = "www.duckbat.com/plugins/update.core.txt";
public const string CoreUrl2 = "code.google.com/p/proxymud/";
private int GetVersion(string Url)
{
if(!_urlRegex.Match(Url).Success)
Url = "http://" + Url;
WebClient w = new WebClient();
byte[] d = w.DownloadData(Url);
string s = Encoding.Default.GetString(d);
s = s.Replace("\r", "");
s = s.Replace(" ", "");
s = s.Replace("\n", "");
return int.Parse(s);
}
private static Regex _urlRegex = new Regex(@"^\w+://", RegexOptions.Compiled);
/// <summary>
/// Milliseconds since program startup.
/// </summary>
public long MSTime
{
get;
private set;
}
/// <summary>
/// Check for updates and report to all connected users if there are any.
/// </summary>
/// <param name="Core">Check core update.</param>
/// <param name="Plugins">Check plugin updates.</param>
/// <param name="ReportNoUpdates">Should we report if no updates were found?</param>
public void CheckUpdates(bool Core, bool Plugins, bool ReportNoUpdates)
{
#if DEBUG
return;
#endif
bool s = false;
if(Core)
{
try
{
int coreVer = GetVersion(CoreUrl);
if(coreVer > Version)
{
SendMessage("@GUPDATE: @wThere is a newer version of @Wcore @wavailable. (@W" + coreVer + "@w)");
SendMessage(" @wGo to @W" + CoreUrl2 + " @wto see more.");
s = true;
}
}
catch
{
}
}
if(Plugins)
{
foreach(KeyValuePair<string, Plugin> x in PluginMgr.Plugins)
{
if(string.IsNullOrEmpty(x.Value.Website) || string.IsNullOrEmpty(x.Value.UpdateUrl))
continue;
try
{
int ver = GetVersion(x.Value.UpdateUrl);
if(ver > x.Value.Version)
{
if(s)
SendMessage("");
SendMessage("@GUPDATE: @wThere is a newer version of @W" + x.Value.Keyword.ToLower().Trim() + " @wavailable. (@W" + ver + "@w)");
SendMessage(" @wGo to @W" + x.Value.Website + " @wto see more.");
s = true;
}
}
catch
{
continue;
}
}
}
if(ReportNoUpdates && !s)
SendMessage("@GUPDATE: @wNo updates found.");
}
}
}